@zenstackhq/runtime
Advanced tools
Comparing version
@@ -288,2 +288,3 @@ /** | ||
private visitSubPayload; | ||
private enumerateReverse; | ||
} | ||
@@ -290,0 +291,0 @@ |
@@ -478,3 +478,3 @@ "use strict"; | ||
case "create": | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, {}); | ||
@@ -505,3 +505,3 @@ let callbackResult; | ||
case "connectOrCreate": | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item.where); | ||
@@ -520,3 +520,3 @@ let callbackResult; | ||
if (this.callback.connect) { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item, true); | ||
@@ -529,3 +529,3 @@ yield this.callback.connect(model, item, newContext); | ||
if (this.callback.disconnect) { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item, typeof item === "object"); | ||
@@ -538,3 +538,3 @@ yield this.callback.disconnect(model, item, newContext); | ||
if (this.callback.set) { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item, true); | ||
@@ -546,3 +546,3 @@ yield this.callback.set(model, item, newContext); | ||
case "update": | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item.where); | ||
@@ -560,3 +560,3 @@ let callbackResult; | ||
case "updateMany": | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item.where); | ||
@@ -574,3 +574,3 @@ let callbackResult; | ||
case "upsert": { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, item.where); | ||
@@ -594,3 +594,3 @@ let callbackResult; | ||
if (this.callback.delete) { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, toplevel ? item.where : item); | ||
@@ -604,3 +604,3 @@ yield this.callback.delete(model, item, newContext); | ||
if (this.callback.deleteMany) { | ||
for (const item of enumerate(data)) { | ||
for (const item of this.enumerateReverse(data)) { | ||
const newContext = pushNewContext(field, model, toplevel ? item.where : item); | ||
@@ -646,2 +646,13 @@ yield this.callback.deleteMany(model, item, newContext); | ||
} | ||
// enumerate a (possible) array in reverse order, so that the enumeration | ||
// callback can safely delete the current item | ||
*enumerateReverse(data) { | ||
if (Array.isArray(data)) { | ||
for (let i = data.length - 1; i >= 0; i--) { | ||
yield data[i]; | ||
} | ||
} else { | ||
yield data; | ||
} | ||
} | ||
}; | ||
@@ -648,0 +659,0 @@ |
@@ -21,2 +21,3 @@ import type { DbClientContract } from '../types'; | ||
private injectBaseIncludeRecursively; | ||
private injectConcreteIncludeRecursively; | ||
create(args: any): Promise<any>; | ||
@@ -63,4 +64,4 @@ createMany(args: { | ||
private assembleHierarchy; | ||
private transformWhereHierarchy; | ||
private transformFieldHierarchy; | ||
private assembleUp; | ||
private assembleDown; | ||
} |
@@ -178,3 +178,6 @@ "use strict"; | ||
if (!args.select) { | ||
// include base models upwards | ||
this.injectBaseIncludeRecursively(model, args); | ||
// include sub models downwards | ||
this.injectConcreteIncludeRecursively(model, args); | ||
} | ||
@@ -205,2 +208,3 @@ } | ||
this.injectBaseIncludeRecursively(model, selectInclude); | ||
this.injectConcreteIncludeRecursively(model, selectInclude); | ||
} | ||
@@ -265,2 +269,22 @@ return selectInclude; | ||
} | ||
injectConcreteIncludeRecursively(model, selectInclude) { | ||
const modelInfo = (0, cross_1.getModelInfo)(this.options.modelMeta, model); | ||
if (!modelInfo) { | ||
return; | ||
} | ||
// get sub models of this model | ||
const subModels = Object.values(this.options.modelMeta.models).filter((m) => { var _a; return (_a = m.baseTypes) === null || _a === void 0 ? void 0 : _a.includes(modelInfo.name); }); | ||
for (const subModel of subModels) { | ||
// include sub model relation field | ||
const subRelationName = this.makeAuxRelationName(subModel); | ||
if (selectInclude.select) { | ||
selectInclude.include = Object.assign({ [subRelationName]: {} }, selectInclude.select); | ||
delete selectInclude.select; | ||
} | ||
else { | ||
selectInclude.include = Object.assign({ [subRelationName]: {} }, selectInclude.include); | ||
} | ||
this.injectConcreteIncludeRecursively(subModel.name, selectInclude.include[subRelationName]); | ||
} | ||
} | ||
// #endregion | ||
@@ -830,2 +854,26 @@ // #region create | ||
} | ||
const upMerged = this.assembleUp(model, entity); | ||
const downMerged = this.assembleDown(model, entity); | ||
// https://www.npmjs.com/package/deepmerge#arraymerge-example-combine-arrays | ||
const combineMerge = (target, source, options) => { | ||
const destination = target.slice(); | ||
source.forEach((item, index) => { | ||
if (typeof destination[index] === 'undefined') { | ||
destination[index] = options.cloneUnlessOtherwiseSpecified(item, options); | ||
} | ||
else if (options.isMergeableObject(item)) { | ||
destination[index] = (0, deepmerge_1.default)(target[index], item, options); | ||
} | ||
else if (target.indexOf(item) === -1) { | ||
destination.push(item); | ||
} | ||
}); | ||
return destination; | ||
}; | ||
const result = (0, deepmerge_1.default)(upMerged, downMerged, { | ||
arrayMerge: combineMerge, | ||
}); | ||
return result; | ||
} | ||
assembleUp(model, entity) { | ||
const result = {}; | ||
@@ -838,3 +886,3 @@ const base = this.getBaseModel(model); | ||
if (baseData && typeof baseData === 'object') { | ||
const baseAssembled = this.assembleHierarchy(base.name, baseData); | ||
const baseAssembled = this.assembleUp(base.name, baseData); | ||
Object.assign(result, baseAssembled); | ||
@@ -853,6 +901,6 @@ } | ||
if (Array.isArray(fieldValue)) { | ||
result[field.name] = fieldValue.map((item) => this.assembleHierarchy(field.type, item)); | ||
result[field.name] = fieldValue.map((item) => this.assembleUp(field.type, item)); | ||
} | ||
else { | ||
result[field.name] = this.assembleHierarchy(field.type, fieldValue); | ||
result[field.name] = this.assembleUp(field.type, fieldValue); | ||
} | ||
@@ -867,48 +915,34 @@ } | ||
} | ||
// #endregion | ||
// #region backup | ||
transformWhereHierarchy(where, contextModel, forModel) { | ||
if (!where || typeof where !== 'object') { | ||
return where; | ||
} | ||
let curr = contextModel; | ||
const inheritStack = []; | ||
while (curr) { | ||
inheritStack.unshift(curr); | ||
curr = this.getBaseModel(curr.name); | ||
} | ||
let result = {}; | ||
for (const [key, value] of Object.entries(where)) { | ||
const fieldInfo = (0, cross_1.requireField)(this.options.modelMeta, contextModel.name, key); | ||
const fieldHierarchy = this.transformFieldHierarchy(fieldInfo, value, contextModel, forModel, inheritStack); | ||
result = (0, deepmerge_1.default)(result, fieldHierarchy); | ||
} | ||
return result; | ||
} | ||
transformFieldHierarchy(fieldInfo, value, contextModel, forModel, inheritStack) { | ||
const fieldModel = fieldInfo.inheritedFrom ? this.getModelInfo(fieldInfo.inheritedFrom) : contextModel; | ||
if (fieldModel === forModel) { | ||
return { [fieldInfo.name]: value }; | ||
} | ||
const fieldModelPos = inheritStack.findIndex((m) => m === fieldModel); | ||
const forModelPos = inheritStack.findIndex((m) => m === forModel); | ||
assembleDown(model, entity) { | ||
const result = {}; | ||
let curr = result; | ||
if (fieldModelPos > forModelPos) { | ||
// walk down hierarchy | ||
for (let i = forModelPos + 1; i <= fieldModelPos; i++) { | ||
const rel = this.makeAuxRelationName(inheritStack[i]); | ||
curr[rel] = {}; | ||
curr = curr[rel]; | ||
const modelInfo = (0, cross_1.getModelInfo)(this.options.modelMeta, model, true); | ||
if (modelInfo.discriminator) { | ||
// model is a delegate, merge sub model fields | ||
const subModelName = entity[modelInfo.discriminator]; | ||
if (subModelName) { | ||
const subModel = (0, cross_1.getModelInfo)(this.options.modelMeta, subModelName, true); | ||
const subRelationName = this.makeAuxRelationName(subModel); | ||
const subData = entity[subRelationName]; | ||
if (subData && typeof subData === 'object') { | ||
const subAssembled = this.assembleDown(subModel.name, subData); | ||
Object.assign(result, subAssembled); | ||
} | ||
} | ||
} | ||
else { | ||
// walk up hierarchy | ||
for (let i = forModelPos - 1; i >= fieldModelPos; i--) { | ||
const rel = this.makeAuxRelationName(inheritStack[i]); | ||
curr[rel] = {}; | ||
curr = curr[rel]; | ||
for (const field of Object.values(modelInfo.fields)) { | ||
if (field.name in entity) { | ||
const fieldValue = entity[field.name]; | ||
if (field.isDataModel) { | ||
if (Array.isArray(fieldValue)) { | ||
result[field.name] = fieldValue.map((item) => this.assembleDown(field.type, item)); | ||
} | ||
else { | ||
result[field.name] = this.assembleDown(field.type, fieldValue); | ||
} | ||
} | ||
else { | ||
result[field.name] = fieldValue; | ||
} | ||
} | ||
} | ||
curr[fieldInfo.name] = value; | ||
return result; | ||
@@ -915,0 +949,0 @@ } |
@@ -59,2 +59,4 @@ import { type DbClientContract } from '../../types'; | ||
private requireBackLink; | ||
private mergeToParent; | ||
private removeFromParent; | ||
} |
@@ -278,15 +278,3 @@ "use strict"; | ||
} | ||
if (context.parent.connect) { | ||
// if the payload parent already has a "connect" clause, merge it | ||
if (Array.isArray(context.parent.connect)) { | ||
context.parent.connect.push(args.where); | ||
} | ||
else { | ||
context.parent.connect = [context.parent.connect, args.where]; | ||
} | ||
} | ||
else { | ||
// otherwise, create a new "connect" clause | ||
context.parent.connect = args.where; | ||
} | ||
this.mergeToParent(context.parent, 'connect', args.where); | ||
// record the key of connected entities so we can avoid validating them later | ||
@@ -299,6 +287,6 @@ connectedEntities.add(getEntityKey(model, existing)); | ||
// create a new "create" clause at the parent level | ||
context.parent.create = args.create; | ||
this.mergeToParent(context.parent, 'create', args.create); | ||
} | ||
// remove the connectOrCreate clause | ||
delete context.parent['connectOrCreate']; | ||
this.removeFromParent(context.parent, 'connectOrCreate', args); | ||
// return false to prevent visiting the nested payload | ||
@@ -761,3 +749,3 @@ return false; | ||
// remove it from the update payload | ||
delete context.parent.create; | ||
this.removeFromParent(context.parent, 'create', args); | ||
// don't visit payload | ||
@@ -786,9 +774,10 @@ return false; | ||
// convert upsert to update | ||
context.parent.update = { | ||
const convertedUpdate = { | ||
where: args.where, | ||
data: this.validateUpdateInputSchema(model, args.update), | ||
}; | ||
delete context.parent.upsert; | ||
this.mergeToParent(context.parent, 'update', convertedUpdate); | ||
this.removeFromParent(context.parent, 'upsert', args); | ||
// continue visiting the new payload | ||
return context.parent.update; | ||
return convertedUpdate; | ||
} | ||
@@ -800,3 +789,3 @@ else { | ||
// remove it from the update payload | ||
delete context.parent.upsert; | ||
this.removeFromParent(context.parent, 'upsert', args); | ||
// don't visit payload | ||
@@ -1141,4 +1130,31 @@ return false; | ||
} | ||
mergeToParent(parent, key, value) { | ||
if (parent[key]) { | ||
if (Array.isArray(parent[key])) { | ||
parent[key].push(value); | ||
} | ||
else { | ||
parent[key] = [parent[key], value]; | ||
} | ||
} | ||
else { | ||
parent[key] = value; | ||
} | ||
} | ||
removeFromParent(parent, key, data) { | ||
if (parent[key] === data) { | ||
delete parent[key]; | ||
} | ||
else if (Array.isArray(parent[key])) { | ||
const idx = parent[key].indexOf(data); | ||
if (idx >= 0) { | ||
parent[key].splice(idx, 1); | ||
if (parent[key].length === 0) { | ||
delete parent[key]; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
exports.PolicyProxyHandler = PolicyProxyHandler; | ||
//# sourceMappingURL=handler.js.map |
{ | ||
"name": "@zenstackhq/runtime", | ||
"displayName": "ZenStack Runtime Library", | ||
"version": "2.0.0-alpha.5", | ||
"version": "2.0.0-alpha.6", | ||
"description": "Runtime of ZenStack for both client-side and server-side environments.", | ||
@@ -6,0 +6,0 @@ "repository": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
569951
1.2%7120
1.08%