@strapi/database
Advanced tools
Comparing version 4.0.0-next.2 to 4.0.0-next.3
'use strict'; | ||
const util = require('util'); | ||
const _ = require('lodash'); | ||
@@ -74,381 +73,2 @@ const { Database } = require('../lib/index'); | ||
// (async function() { | ||
// for (const key in connections) { | ||
// await main(connections[key]); | ||
// } | ||
// })().catch(err => { | ||
// console.error(err); | ||
// process.exit(); | ||
// }); | ||
main(connections.sqlite); | ||
// const entityService = uid => { | ||
// // knows more about abstraction then the query layer | ||
// // will be moved in the core services not the db | ||
// // D&P should wrap some d&p logic | ||
// // i18N should wrapp some i18n logic etc etc | ||
// const repo = orm.query(uid); | ||
// return { | ||
// findById(id) { | ||
// return repo.findOne({ where: { id } }); | ||
// }, | ||
// async update(id, data) { | ||
// await repo.update({ where: { id }, data }); | ||
// return repo.findOne({ where: { id } }); | ||
// }, | ||
// }; | ||
// }; | ||
/* | ||
db.migration.create(); | ||
db.migration.up(); | ||
db.migration.down(); | ||
db.seed.run(); | ||
db.seed.create(); | ||
db.exporter.dump() | ||
db.importer.restore() | ||
db.schema.addField | ||
db.schema.removeField | ||
db.schema.addCollection | ||
db.schema.removeCollection | ||
*/ | ||
// const r = await orm.entityManager | ||
// .createQueryBuilder('article') | ||
// .select('*') | ||
// .join({ | ||
// rootColumn: 'id', | ||
// alias: 'ac', | ||
// referencedTable: 'categories_articles_articles_links', | ||
// referencedColumn: 'article_id', | ||
// }) | ||
// .join({ | ||
// rootTable: 'ac', | ||
// rootColumn: 'category_id', | ||
// alias: 'c', | ||
// referencedTable: 'categories', | ||
// referencedColumn: 'id', | ||
// }) | ||
// .where({ | ||
// $and: [], | ||
// }) | ||
// .populate({ | ||
// category: true, | ||
// }) | ||
// .execute(); | ||
// console.log(r); | ||
// const catA = await entityService('category').findById(348); | ||
// console.log(catA); | ||
/* | ||
orm.contentType('category').loadRelation() | ||
=> query('category').load('xxx') | ||
*/ | ||
// await orm.query('category').delete(); | ||
// const article = await orm.query('article').create({ | ||
// select: ['id', 'title'], | ||
// populate: { | ||
// category: { | ||
// select: ['price'], | ||
// where: { | ||
// price: { | ||
// $gte: 12, | ||
// }, | ||
// }, | ||
// }, | ||
// }, | ||
// data: { | ||
// title: 'my category', | ||
// category_id: 1, // TODO: handle category: 1 too | ||
// }, | ||
// }); | ||
// console.log(JSON.stringify(article, null, 4)); | ||
const tests = async orm => { | ||
// await orm.query('category').createMany({ | ||
// // select: {}, | ||
// // populate: {}, | ||
// data: Array(5) | ||
// .fill({}) | ||
// .map((v, idx) => ({ | ||
// title: `Category ${_.padStart(idx, 3, '0')}`, | ||
// articles: [idx + 1, idx + 2], | ||
// })), | ||
// }); | ||
await orm.query('article').createMany({ | ||
// select: {}, | ||
// populate: {}, | ||
data: Array(5) | ||
.fill({}) | ||
.map((v, idx) => ({ | ||
title: `Article ${_.padStart(idx, 3, '0')}`, | ||
// category_id: idx + 1, | ||
category: idx + 1, | ||
})), | ||
}); | ||
const cat = await orm.query('category').create({ | ||
data: { | ||
articles: [1, 2, 3, 4, 5], | ||
}, | ||
populate: ['articles'], | ||
}); | ||
console.log(cat); | ||
const tag = await orm.query('tag').create({ | ||
data: { | ||
articles: [1, 2, 3, 4, 5], | ||
}, | ||
populate: ['articles'], | ||
}); | ||
console.log(tag); | ||
const someArticles = await orm.query('article').findMany({ | ||
where: { | ||
id: [1, 2, 3, 4, 5], | ||
}, | ||
populate: ['tags', 'category'], | ||
}); | ||
console.log(someArticles); | ||
await orm.query('category').updateMany({ | ||
where: { | ||
// articles: { | ||
// title: { | ||
// $contains: 'Category', | ||
// }, | ||
// }, | ||
}, | ||
data: { | ||
title: 'Category 007', | ||
}, | ||
}); | ||
let r = await orm.query('category').findMany({ | ||
where: { | ||
$and: [ | ||
{ | ||
title: { | ||
$ne: 'salut', | ||
}, | ||
}, | ||
], | ||
title: 'test', | ||
price: { | ||
$gt: 12, | ||
}, | ||
articles: { | ||
title: { | ||
$startsWith: 'Test', | ||
// $mode: 'insensitive', | ||
}, | ||
}, | ||
compo: { | ||
key: 'x', | ||
}, | ||
}, | ||
}); | ||
// escape stuff | ||
// const raw = (strings, ) | ||
const params = { | ||
select: ['id', 'title'], | ||
where: { | ||
$not: { | ||
$or: [ | ||
{ | ||
articles: { | ||
category: { | ||
title: 'Category 003', | ||
}, | ||
}, | ||
}, | ||
{ | ||
title: { | ||
$in: ['Category 001', 'Category 002'], | ||
}, | ||
}, | ||
{ | ||
title: { | ||
$not: 'Category 001', | ||
}, | ||
}, | ||
], | ||
}, | ||
}, | ||
orderBy: [{ articles: { title: 'asc' } }], | ||
limit: 10, | ||
offset: 0, | ||
}; | ||
r = await orm.query('category').findMany(params); | ||
console.log(r); | ||
r = await orm.query('category').findMany({ | ||
where: { | ||
$or: [ | ||
{ | ||
compo: { | ||
value: { | ||
$gte: 3, | ||
}, | ||
}, | ||
}, | ||
{ | ||
articles: { | ||
title: { | ||
$contains: 'test', | ||
}, | ||
}, | ||
}, | ||
], | ||
}, | ||
}); | ||
console.log(r); | ||
r = await orm.query('user').findMany({ | ||
where: { | ||
address: { | ||
name: 'A', | ||
}, | ||
}, | ||
}); | ||
console.log(r); | ||
const [results, count] = await orm.query('category').findWithCount(params); | ||
console.log({ results, count }); | ||
await orm.query('category', { | ||
populate: ['articles'], | ||
}); | ||
const address = await orm.query('address').findMany({ | ||
populate: { | ||
user: { | ||
// select: ['id', 'address_id'], | ||
}, | ||
}, | ||
}); | ||
console.log(address); | ||
const user = await orm.query('user').findMany({ | ||
populate: { | ||
address: { | ||
// select: ['id', 'name'], | ||
}, | ||
}, | ||
}); | ||
console.log(user); | ||
const article = await orm.query('article').findMany({ | ||
populate: { | ||
category: true, | ||
}, | ||
}); | ||
console.log(article); | ||
await orm.query('category').findMany({ | ||
populate: { | ||
compo: true, | ||
articles: { | ||
select: ['title'], | ||
populate: { | ||
category: { | ||
select: ['title'], | ||
}, | ||
}, | ||
}, | ||
}, | ||
limit: 5, | ||
}); | ||
await orm.query('article').findMany({ | ||
populate: { | ||
tags: true, | ||
}, | ||
limit: 5, | ||
}); | ||
await orm.query('tag').findMany({ | ||
populate: { | ||
articles: true, | ||
}, | ||
limit: 5, | ||
}); | ||
// const articleCategory = orm.query('article').load(article, 'category', { | ||
// select: ['id', 'title'], | ||
// limit: 5, | ||
// offset: 2, | ||
// orderBy: 'title', | ||
// where: {}, | ||
// }); | ||
// const article = await orm.query('article').populate(article, { | ||
// category: true, | ||
// tags: true, | ||
// }); | ||
await orm.query('article').findMany({ | ||
where: { | ||
$not: { | ||
$or: [ | ||
{ | ||
category: { | ||
title: 'Article 003', | ||
}, | ||
}, | ||
{ | ||
title: { | ||
$in: ['Article 001', 'Article 002'], | ||
}, | ||
}, | ||
], | ||
title: { | ||
$not: 'Article 007', | ||
}, | ||
}, | ||
}, | ||
orderBy: [{ category: { name: 'asc' } }], | ||
offset: 0, | ||
limit: 10, | ||
populate: { | ||
category: { | ||
orderBy: 'title', | ||
populate: { | ||
categories: { | ||
populate: { | ||
tags: true, | ||
}, | ||
}, | ||
}, | ||
}, | ||
seo: true, | ||
}, | ||
}); | ||
}; |
@@ -14,2 +14,3 @@ 'use strict'; | ||
// TODO: move to query layer | ||
// TODO: handle programmatic defaults | ||
const toRow = (metadata, data = {}) => { | ||
@@ -90,21 +91,33 @@ const { attributes } = metadata; | ||
return { | ||
findOne(uid, params) { | ||
const qb = this.createQueryBuilder(uid) | ||
async findOne(uid, params) { | ||
await db.lifecycles.run('beforeFindOne', uid, { params }); | ||
const result = await this.createQueryBuilder(uid) | ||
.init(params) | ||
.first(); | ||
.first() | ||
.execute(); | ||
return qb.execute(); | ||
await db.lifecycles.run('afterFindOne', uid, { params, result }); | ||
return result; | ||
}, | ||
// should we name it findOne because people are used to it ? | ||
findMany(uid, params) { | ||
const qb = this.createQueryBuilder(uid).init(params); | ||
async findMany(uid, params) { | ||
await db.lifecycles.run('beforeFindMany', uid, { params }); | ||
return qb.execute(); | ||
const result = await this.createQueryBuilder(uid) | ||
.init(params) | ||
.execute(); | ||
await db.lifecycles.run('afterFindMany', uid, { params, result }); | ||
return result; | ||
}, | ||
async count(uid, params = {}) { | ||
const qb = this.createQueryBuilder(uid).where(params.where); | ||
await db.lifecycles.run('beforeCount', uid, { params }); | ||
const res = await qb | ||
const res = await this.createQueryBuilder(uid) | ||
.where(params.where) | ||
.count() | ||
@@ -114,9 +127,13 @@ .first() | ||
return Number(res.count); | ||
const result = Number(res.count); | ||
await db.lifecycles.run('afterCount', uid, { params, result }); | ||
return result; | ||
}, | ||
async create(uid, params = {}) { | ||
// create entry in DB | ||
await db.lifecycles.run('beforeCreate', uid, { params }); | ||
const metadata = db.metadata.get(uid); | ||
const { data } = params; | ||
@@ -128,4 +145,2 @@ | ||
// transform value to storage value | ||
// apply programatic defaults if any -> I think this should be handled outside of this layer as we might have some applicative rules in the entity service | ||
const dataToInsert = toRow(metadata, data); | ||
@@ -137,20 +152,27 @@ | ||
// create relation associations or move this to the entity service & call attach on the repo instead | ||
await this.attachRelations(uid, id, data); | ||
// TODO: in case there is not select or populate specified return the inserted data ? | ||
// TODO: do not trigger the findOne lifecycles ? | ||
const result = await this.findOne(uid, { | ||
where: { id }, | ||
select: params.select, | ||
populate: params.populate, | ||
}); | ||
return this.findOne(uid, { where: { id }, select: params.select, populate: params.populate }); | ||
await db.lifecycles.run('afterCreate', uid, { params, result }); | ||
return result; | ||
}, | ||
// TODO: where do we handle relation processing for many queries ? | ||
async createMany(uid, params = {}) { | ||
await db.lifecycles.run('beforeCreateMany', uid, { params }); | ||
const metadata = db.metadata.get(uid); | ||
const { data } = params; | ||
if (!_.isArray(data)) { | ||
throw new Error('CreateMany expecets data to be an array'); | ||
throw new Error('CreateMany expects data to be an array'); | ||
} | ||
const metadata = db.metadata.get(uid); | ||
const dataToInsert = data.map(datum => toRow(metadata, datum)); | ||
@@ -166,8 +188,14 @@ | ||
return { count: data.length }; | ||
const result = { count: data.length }; | ||
await db.lifecycles.run('afterCreateMany', uid, { params, result }); | ||
return result; | ||
}, | ||
async update(uid, params = {}) { | ||
await db.lifecycles.run('beforeUpdate', uid, { params }); | ||
const metadata = db.metadata.get(uid); | ||
const { where, data } = params; | ||
const metadata = db.metadata.get(uid); | ||
@@ -189,3 +217,2 @@ if (!_.isPlainObject(data)) { | ||
if (!entity) { | ||
// TODO: or throw ? | ||
return null; | ||
@@ -207,10 +234,20 @@ } | ||
return this.findOne(uid, { where: { id }, select: params.select, populate: params.populate }); | ||
// TODO: do not trigger the findOne lifecycles ? | ||
const result = await this.findOne(uid, { | ||
where: { id }, | ||
select: params.select, | ||
populate: params.populate, | ||
}); | ||
await db.lifecycles.run('afterUpdate', uid, { params, result }); | ||
return result; | ||
}, | ||
// TODO: where do we handle relation processing for many queries ? | ||
async updateMany(uid, params = {}) { | ||
await db.lifecycles.run('beforeUpdateMany', uid, { params }); | ||
const metadata = db.metadata.get(uid); | ||
const { where, data } = params; | ||
const metadata = db.metadata.get(uid); | ||
const dataToUpdate = toRow(metadata, data); | ||
@@ -227,6 +264,12 @@ | ||
return { count: updatedRows }; | ||
const result = { count: updatedRows }; | ||
await db.lifecycles.run('afterUpdateMany', uid, { params, result }); | ||
return result; | ||
}, | ||
async delete(uid, params = {}) { | ||
await db.lifecycles.run('beforeDelete', uid, { params }); | ||
const { where, select, populate } = params; | ||
@@ -238,2 +281,3 @@ | ||
// TODO: avoid trigger the findOne lifecycles in the case ? | ||
const entity = await this.findOne(uid, { | ||
@@ -258,2 +302,4 @@ select: select && ['id'].concat(select), | ||
await db.lifecycles.run('afterDelete', uid, { params, result: entity }); | ||
return entity; | ||
@@ -264,2 +310,4 @@ }, | ||
async deleteMany(uid, params = {}) { | ||
await db.lifecycles.run('beforeDeleteMany', uid, { params }); | ||
const { where } = params; | ||
@@ -272,3 +320,7 @@ | ||
return { count: deletedRows }; | ||
const result = { count: deletedRows }; | ||
await db.lifecycles.run('afterDelete', uid, { params, result }); | ||
return result; | ||
}, | ||
@@ -736,2 +788,3 @@ | ||
// TODO: support multiple relations at once with the populate syntax | ||
// TODO: add lifecycle events | ||
async populate(uid, entity, populate) { | ||
@@ -748,2 +801,3 @@ const entry = await this.findOne(uid, { | ||
// TODO: support multiple relations at once with the populate syntax | ||
// TODO: add lifecycle events | ||
async load(uid, entity, field, params) { | ||
@@ -750,0 +804,0 @@ const { attributes } = db.metadata.get(uid); |
@@ -9,2 +9,3 @@ 'use strict'; | ||
const { createEntityManager } = require('./entity-manager'); | ||
const { createLifecyclesManager } = require('./lifecycles'); | ||
@@ -24,5 +25,7 @@ // TODO: move back into strapi | ||
// TODO: migrations -> allow running them through cli before startup | ||
this.schema = createSchemaProvider(this); | ||
// TODO: migrations -> allow running them through cli before startup | ||
this.lifecycles = createLifecyclesManager(this); | ||
this.entityManager = createEntityManager(this); | ||
@@ -33,3 +36,12 @@ } | ||
await this.dialect.initialize(); | ||
this.connection = knex(this.config.connection); | ||
// register module lifeycles subscriber | ||
this.lifecycles.subscribe(async event => { | ||
const { model } = event; | ||
if (event.action in model.lifecycles) { | ||
await model.lifecycles[event.action](event); | ||
} | ||
}); | ||
} | ||
@@ -46,2 +58,3 @@ | ||
async destroy() { | ||
await this.lifecycles.clear(); | ||
await this.connection.destroy(); | ||
@@ -48,0 +61,0 @@ } |
@@ -71,2 +71,3 @@ /** | ||
}, | ||
lifecycles: model.lifecycles || {}, | ||
}); | ||
@@ -73,0 +74,0 @@ } |
@@ -519,2 +519,6 @@ 'use strict'; | ||
if (_.isEmpty(results)) { | ||
return results; | ||
} | ||
for (const key in populate) { | ||
@@ -583,30 +587,12 @@ // NOTE: Omit limit & offset to avoid needing a query per result to avoid making too many queries | ||
const alias = qb.getAlias(); | ||
const joinColAlias = `${alias}.${joinColumnName}`; | ||
if (isCount) { | ||
const rows = await qb | ||
.init(populateValue) | ||
.join({ | ||
alias: alias, | ||
referencedTable: joinTable.name, | ||
referencedColumn: joinTable.inverseJoinColumn.name, | ||
rootColumn: joinTable.inverseJoinColumn.referencedColumn, | ||
rootTable: qb.alias, | ||
on: joinTable.on, | ||
}) | ||
.select([`${alias}.${joinColumnName}`, qb.raw('count(*) AS count')]) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.groupBy(`${alias}.${joinColumnName}`) | ||
.execute({ mapResults: false }); | ||
const referencedValues = _.uniq( | ||
results.map(r => r[referencedColumnName]).filter(value => !_.isNil(value)) | ||
); | ||
const map = rows.reduce((map, row) => { | ||
map[row[joinColumnName]] = { count: row.count }; | ||
return map; | ||
}, {}); | ||
if (_.isEmpty(referencedValues)) { | ||
results.forEach(result => { | ||
result[key] = map[result[referencedColumnName]] || { count: 0 }; | ||
result[key] = null; | ||
}); | ||
continue; | ||
@@ -625,6 +611,4 @@ } | ||
}) | ||
.addSelect(`${alias}.${joinColumnName}`) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.addSelect(joinColAlias) | ||
.where({ [joinColAlias]: referencedValues }) | ||
.execute({ mapResults: false }); | ||
@@ -687,4 +671,16 @@ | ||
const alias = qb.getAlias(); | ||
const joinColAlias = `${alias}.${joinColumnName}`; | ||
const referencedValues = _.uniq( | ||
results.map(r => r[referencedColumnName]).filter(value => !_.isNil(value)) | ||
); | ||
if (isCount) { | ||
if (_.isEmpty(referencedValues)) { | ||
results.forEach(result => { | ||
result[key] = { count: 0 }; | ||
}); | ||
continue; | ||
} | ||
const rows = await qb | ||
@@ -700,7 +696,5 @@ .init(populateValue) | ||
}) | ||
.select([`${alias}.${joinColumnName}`, qb.raw('count(*) AS count')]) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.groupBy(`${alias}.${joinColumnName}`) | ||
.select([joinColAlias, qb.raw('count(*) AS count')]) | ||
.where({ [joinColAlias]: referencedValues }) | ||
.groupBy(joinColAlias) | ||
.execute({ mapResults: false }); | ||
@@ -720,2 +714,9 @@ | ||
if (_.isEmpty(referencedValues)) { | ||
results.forEach(result => { | ||
result[key] = []; | ||
}); | ||
continue; | ||
} | ||
const rows = await qb | ||
@@ -731,6 +732,4 @@ .init(populateValue) | ||
}) | ||
.addSelect(`${alias}.${joinColumnName}`) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.addSelect(joinColAlias) | ||
.where({ [joinColAlias]: referencedValues }) | ||
.execute({ mapResults: false }); | ||
@@ -755,4 +754,15 @@ | ||
const alias = qb.getAlias(); | ||
const joinColAlias = `${alias}.${joinColumnName}`; | ||
const referencedValues = _.uniq( | ||
results.map(r => r[referencedColumnName]).filter(value => !_.isNil(value)) | ||
); | ||
if (isCount) { | ||
if (_.isEmpty(referencedValues)) { | ||
results.forEach(result => { | ||
result[key] = { count: 0 }; | ||
}); | ||
continue; | ||
} | ||
const rows = await qb | ||
@@ -768,7 +778,5 @@ .init(populateValue) | ||
}) | ||
.select([`${alias}.${joinColumnName}`, qb.raw('count(*) AS count')]) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.groupBy(`${alias}.${joinColumnName}`) | ||
.select([joinColAlias, qb.raw('count(*) AS count')]) | ||
.where({ [joinColAlias]: referencedValues }) | ||
.groupBy(joinColAlias) | ||
.execute({ mapResults: false }); | ||
@@ -788,2 +796,9 @@ | ||
if (_.isEmpty(referencedValues)) { | ||
results.forEach(result => { | ||
result[key] = []; | ||
}); | ||
continue; | ||
} | ||
const rows = await qb | ||
@@ -799,6 +814,4 @@ .init(populateValue) | ||
}) | ||
.addSelect(`${alias}.${joinColumnName}`) | ||
.where({ | ||
[`${alias}.${joinColumnName}`]: results.map(r => r[referencedColumnName]), | ||
}) | ||
.addSelect(joinColAlias) | ||
.where({ [joinColAlias]: referencedValues }) | ||
.execute({ mapResults: false }); | ||
@@ -805,0 +818,0 @@ |
@@ -33,2 +33,9 @@ 'use strict'; | ||
select(args) { | ||
state.type = 'select'; | ||
state.select = _.castArray(args).map(col => this.aliasColumn(col)); | ||
return this; | ||
}, | ||
insert(data) { | ||
@@ -69,9 +76,2 @@ state.type = 'insert'; | ||
select(args) { | ||
state.type = 'select'; | ||
state.select = _.castArray(args).map(col => this.aliasColumn(col)); | ||
return this; | ||
}, | ||
addSelect(args) { | ||
@@ -78,0 +78,0 @@ state.select.push(..._.castArray(args).map(col => this.aliasColumn(col))); |
@@ -75,4 +75,10 @@ 'use strict'; | ||
// TODO: drop foreign keys targeting delete tables | ||
// drop all delete table foreign keys then delete the tables | ||
for (const table of schemaDiff.tables.removed) { | ||
debug(`Removing table foreign keys: ${table.name}`); | ||
const schemaBuilder = this.getSchemaBuilder(table, trx); | ||
await dropTableForeignKeys(schemaBuilder, table); | ||
} | ||
for (const table of schemaDiff.tables.removed) { | ||
@@ -82,3 +88,2 @@ debug(`Removing table: ${table.name}`); | ||
const schemaBuilder = this.getSchemaBuilder(table, trx); | ||
// TODO: add cascading if possible | ||
await dropTable(schemaBuilder, table); | ||
@@ -293,2 +298,4 @@ } | ||
throw new Error(`Table already exists ${table.name}`); | ||
// TODO: alter the table instead | ||
} | ||
@@ -306,2 +313,9 @@ | ||
/** | ||
* Drops a table from a database | ||
* @param {Knex.SchemaBuilder} schemaBuilder | ||
* @param {Table} table | ||
*/ | ||
const dropTable = (schemaBuilder, table) => schemaBuilder.dropTableIfExists(table.name); | ||
/** | ||
* Creates a table foreign keys constraints | ||
@@ -319,6 +333,11 @@ * @param {SchemaBuilder} schemaBuilder | ||
/** | ||
* Drops a table from a database | ||
* @param {Knex.SchemaBuilder} schemaBuilder | ||
* Drops a table foreign keys constraints | ||
* @param {SchemaBuilder} schemaBuilder | ||
* @param {Table} table | ||
*/ | ||
const dropTable = (schemaBuilder, table) => schemaBuilder.dropTableIfExists(table.name); | ||
const dropTableForeignKeys = async (schemaBuilder, table) => { | ||
// foreign keys | ||
await schemaBuilder.table(table.name, tableBuilder => { | ||
(table.foreignKeys || []).forEach(foreignKey => dropForeignKey(tableBuilder, foreignKey)); | ||
}); | ||
}; |
{ | ||
"name": "@strapi/database", | ||
"version": "4.0.0-next.2", | ||
"version": "4.0.0-next.3", | ||
"description": "Strapi's database layer", | ||
@@ -40,3 +40,3 @@ "homepage": "https://strapi.io", | ||
}, | ||
"gitHead": "50849e68c7d3632eb5899c6346e33d71a211a3bd" | ||
"gitHead": "22eb6f24d86dae4bd6dff8293916ec40e2f44d33" | ||
} |
32
252387
4548