@fjell/lib-sequelize 4.4.14 → 4.4.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{types/Operations.d.ts → Operations.d.ts} +5 -5
- package/dist/{types/SequelizeLibraryFactory.d.ts → SequelizeLibraryFactory.d.ts} +4 -4
- package/dist/{types/contained → contained}/SequelizeLibrary.d.ts +4 -4
- package/dist/index.js +1488 -0
- package/dist/index.js.map +7 -0
- package/dist/logger.d.ts +2 -0
- package/dist/{types/ops → ops}/all.d.ts +3 -3
- package/dist/{types/ops → ops}/create.d.ts +3 -3
- package/dist/{types/ops → ops}/find.d.ts +3 -3
- package/dist/{types/ops → ops}/get.d.ts +1 -1
- package/dist/{types/ops → ops}/one.d.ts +3 -3
- package/dist/{types/ops → ops}/remove.d.ts +4 -3
- package/dist/{types/ops → ops}/update.d.ts +3 -3
- package/dist/{types/primary → primary}/SequelizeLibrary.d.ts +4 -4
- package/package.json +17 -20
- package/dist/cjs/AggregationBuilder.cjs +0 -65
- package/dist/cjs/Coordinate.cjs +0 -24
- package/dist/cjs/Definition.cjs +0 -25
- package/dist/cjs/EventCoordinator.cjs +0 -54
- package/dist/cjs/KeyMaster.cjs +0 -151
- package/dist/cjs/OperationContext.cjs +0 -161
- package/dist/cjs/Operations.cjs +0 -34
- package/dist/cjs/Options.cjs +0 -46
- package/dist/cjs/QueryBuilder.cjs +0 -296
- package/dist/cjs/ReferenceBuilder.cjs +0 -76
- package/dist/cjs/RowProcessor.cjs +0 -56
- package/dist/cjs/SequelizeLibrary.cjs +0 -56
- package/dist/cjs/SequelizeLibraryFactory.cjs +0 -25
- package/dist/cjs/contained/SequelizeLibrary.cjs +0 -31
- package/dist/cjs/contained/index.cjs +0 -11
- package/dist/cjs/index.cjs +0 -26
- package/dist/cjs/logger.cjs +0 -10
- package/dist/cjs/ops/all.cjs +0 -145
- package/dist/cjs/ops/create.cjs +0 -252
- package/dist/cjs/ops/find.cjs +0 -47
- package/dist/cjs/ops/get.cjs +0 -92
- package/dist/cjs/ops/one.cjs +0 -27
- package/dist/cjs/ops/remove.cjs +0 -114
- package/dist/cjs/ops/update.cjs +0 -120
- package/dist/cjs/primary/SequelizeLibrary.cjs +0 -41
- package/dist/cjs/primary/index.cjs +0 -11
- package/dist/cjs/util/general.cjs +0 -48
- package/dist/cjs/util/relationshipUtils.cjs +0 -117
- package/dist/es/AggregationBuilder.js +0 -61
- package/dist/es/Coordinate.js +0 -19
- package/dist/es/Definition.js +0 -21
- package/dist/es/EventCoordinator.js +0 -48
- package/dist/es/KeyMaster.js +0 -146
- package/dist/es/OperationContext.js +0 -155
- package/dist/es/Operations.js +0 -30
- package/dist/es/Options.js +0 -23
- package/dist/es/QueryBuilder.js +0 -290
- package/dist/es/ReferenceBuilder.js +0 -72
- package/dist/es/RowProcessor.js +0 -52
- package/dist/es/SequelizeLibrary.js +0 -32
- package/dist/es/SequelizeLibraryFactory.js +0 -21
- package/dist/es/contained/SequelizeLibrary.js +0 -26
- package/dist/es/contained/index.js +0 -2
- package/dist/es/index.js +0 -11
- package/dist/es/logger.js +0 -6
- package/dist/es/ops/all.js +0 -141
- package/dist/es/ops/create.js +0 -248
- package/dist/es/ops/find.js +0 -43
- package/dist/es/ops/get.js +0 -88
- package/dist/es/ops/one.js +0 -23
- package/dist/es/ops/remove.js +0 -110
- package/dist/es/ops/update.js +0 -116
- package/dist/es/primary/SequelizeLibrary.js +0 -36
- package/dist/es/primary/index.js +0 -2
- package/dist/es/util/general.js +0 -44
- package/dist/es/util/relationshipUtils.js +0 -112
- package/dist/index.cjs +0 -1853
- package/dist/index.cjs.map +0 -1
- package/dist/types/AggregationBuilder.d.ts +0 -5
- package/dist/types/EventCoordinator.d.ts +0 -6
- package/dist/types/KeyMaster.d.ts +0 -4
- package/dist/types/OperationContext.d.ts +0 -72
- package/dist/types/QueryBuilder.d.ts +0 -12
- package/dist/types/ReferenceBuilder.d.ts +0 -4
- package/dist/types/RowProcessor.d.ts +0 -6
- package/dist/types/logger.d.ts +0 -2
- package/dist/types/util/general.d.ts +0 -4
- package/dist/types/util/relationshipUtils.d.ts +0 -21
- package/dist/{types/Coordinate.d.ts → Coordinate.d.ts} +0 -0
- package/dist/{types/Definition.d.ts → Definition.d.ts} +0 -0
- package/dist/{types/Options.d.ts → Options.d.ts} +1 -1
- package/dist/{types/Registry.d.ts → Registry.d.ts} +0 -0
- package/dist/{types/SequelizeLibrary.d.ts → SequelizeLibrary.d.ts} +1 -1
- /package/dist/{types/contained → contained}/index.d.ts +0 -0
- /package/dist/{types/index.d.ts → index.d.ts} +0 -0
- /package/dist/{types/primary → primary}/index.d.ts +0 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1488 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __export = (target, all) => {
|
|
3
|
+
for (var name in all)
|
|
4
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
// src/Options.ts
|
|
8
|
+
import * as Library from "@fjell/lib";
|
|
9
|
+
var DEFAULT_SEQUELIZE_OPTIONS = {
|
|
10
|
+
deleteOnRemove: false,
|
|
11
|
+
references: [],
|
|
12
|
+
aggregations: []
|
|
13
|
+
};
|
|
14
|
+
var createOptions2 = (sequelizeOptions) => {
|
|
15
|
+
const baseOptions = Library.createOptions(sequelizeOptions);
|
|
16
|
+
const result = {
|
|
17
|
+
...baseOptions,
|
|
18
|
+
deleteOnRemove: sequelizeOptions?.deleteOnRemove ?? DEFAULT_SEQUELIZE_OPTIONS.deleteOnRemove,
|
|
19
|
+
references: sequelizeOptions?.references ?? DEFAULT_SEQUELIZE_OPTIONS.references,
|
|
20
|
+
aggregations: sequelizeOptions?.aggregations ?? DEFAULT_SEQUELIZE_OPTIONS.aggregations
|
|
21
|
+
};
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
// src/logger.ts
|
|
26
|
+
import Logging from "@fjell/logging";
|
|
27
|
+
var LibLogger = Logging.getLogger("@fjell/lib-sequelize");
|
|
28
|
+
var logger_default = LibLogger;
|
|
29
|
+
|
|
30
|
+
// src/Coordinate.ts
|
|
31
|
+
import { createCoordinate as createBaseCoordinate } from "@fjell/registry";
|
|
32
|
+
var logger = logger_default.get("Coordinate");
|
|
33
|
+
var SCOPE_SEQUELIZE = "sequelize";
|
|
34
|
+
var createCoordinate = (kta, scopes) => {
|
|
35
|
+
logger.debug("createCoordinate", { kta, scopes });
|
|
36
|
+
const coordinate = createBaseCoordinate(kta, [SCOPE_SEQUELIZE, ...scopes || []]);
|
|
37
|
+
return coordinate;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// src/Definition.ts
|
|
41
|
+
var logger2 = logger_default.get("lib-sequelize", "Definition");
|
|
42
|
+
var createDefinition = (kta, scopes, libOptions) => {
|
|
43
|
+
logger2.debug("createDefinition", { kta, scopes, libOptions });
|
|
44
|
+
const coordinate = createCoordinate(kta, scopes);
|
|
45
|
+
const options = createOptions2(libOptions);
|
|
46
|
+
return {
|
|
47
|
+
coordinate,
|
|
48
|
+
options
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// src/SequelizeLibrary.ts
|
|
53
|
+
import * as Library2 from "@fjell/lib";
|
|
54
|
+
|
|
55
|
+
// src/ops/all.ts
|
|
56
|
+
import { validateKeys } from "@fjell/core";
|
|
57
|
+
|
|
58
|
+
// src/QueryBuilder.ts
|
|
59
|
+
import {
|
|
60
|
+
isComKey,
|
|
61
|
+
isCondition,
|
|
62
|
+
isPriKey
|
|
63
|
+
} from "@fjell/core";
|
|
64
|
+
import { Op } from "sequelize";
|
|
65
|
+
|
|
66
|
+
// src/util/general.ts
|
|
67
|
+
var stringifyJSON = function(obj, visited = /* @__PURE__ */ new Set()) {
|
|
68
|
+
const arrOfKeyVals = [];
|
|
69
|
+
const arrVals = [];
|
|
70
|
+
let objKeys = [];
|
|
71
|
+
if (typeof obj === "number" || typeof obj === "boolean" || obj === null)
|
|
72
|
+
return "" + obj;
|
|
73
|
+
else if (typeof obj === "string")
|
|
74
|
+
return '"' + obj + '"';
|
|
75
|
+
if (obj instanceof Object && visited.has(obj)) {
|
|
76
|
+
return '"(circular)"';
|
|
77
|
+
} else if (Array.isArray(obj)) {
|
|
78
|
+
if (obj[0] === void 0)
|
|
79
|
+
return "[]";
|
|
80
|
+
else {
|
|
81
|
+
visited.add(obj);
|
|
82
|
+
obj.forEach(function(el) {
|
|
83
|
+
arrVals.push(stringifyJSON(el, visited));
|
|
84
|
+
});
|
|
85
|
+
return "[" + arrVals + "]";
|
|
86
|
+
}
|
|
87
|
+
} else if (obj instanceof Object) {
|
|
88
|
+
visited.add(obj);
|
|
89
|
+
objKeys = Object.keys(obj);
|
|
90
|
+
objKeys.forEach(function(key) {
|
|
91
|
+
const keyOut = '"' + key + '":';
|
|
92
|
+
const keyValOut = obj[key];
|
|
93
|
+
if (keyValOut instanceof Function || keyValOut === void 0)
|
|
94
|
+
return;
|
|
95
|
+
else if (typeof keyValOut === "string")
|
|
96
|
+
arrOfKeyVals.push(keyOut + '"' + keyValOut + '"');
|
|
97
|
+
else if (typeof keyValOut === "boolean" || typeof keyValOut === "number" || keyValOut === null)
|
|
98
|
+
arrOfKeyVals.push(keyOut + keyValOut);
|
|
99
|
+
else if (keyValOut instanceof Object) {
|
|
100
|
+
arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut, visited));
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
return "{" + arrOfKeyVals + "}";
|
|
104
|
+
}
|
|
105
|
+
return "";
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
// src/QueryBuilder.ts
|
|
109
|
+
var logger3 = logger_default.get("sequelize", "QueryBuilder");
|
|
110
|
+
var addDeleteQuery = (options, model) => {
|
|
111
|
+
logger3.default(`QueryBuilder adding delete query with options: ${stringifyJSON(options)}`);
|
|
112
|
+
if (model.getAttributes().deletedAt) {
|
|
113
|
+
options.where["deletedAt"] = {
|
|
114
|
+
[Op.eq]: null
|
|
115
|
+
};
|
|
116
|
+
} else if (model.getAttributes().isDeleted) {
|
|
117
|
+
options.where["isDeleted"] = {
|
|
118
|
+
[Op.eq]: false
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
return options;
|
|
122
|
+
};
|
|
123
|
+
var addEventQueries = (options, events, model) => {
|
|
124
|
+
logger3.default(`QueryBuilder adding event queries with options: ${stringifyJSON(options)}, events: ${stringifyJSON(events)}`);
|
|
125
|
+
Object.keys(events).forEach((key) => {
|
|
126
|
+
if (!model.getAttributes()[`${key}At`]) {
|
|
127
|
+
throw new Error(`Event ${key} is not supported on this model, column ${key}At not found`);
|
|
128
|
+
}
|
|
129
|
+
let whereClauses = {};
|
|
130
|
+
const event = events[key];
|
|
131
|
+
if (event.start) {
|
|
132
|
+
whereClauses = { ...whereClauses, [Op.gte]: new Date(event.start) };
|
|
133
|
+
}
|
|
134
|
+
if (event.end) {
|
|
135
|
+
whereClauses = { ...whereClauses, [Op.lt]: new Date(event.end) };
|
|
136
|
+
}
|
|
137
|
+
if (event.by) {
|
|
138
|
+
if (!model.getAttributes()[`${key}By`]) {
|
|
139
|
+
throw new Error(`Event ${key} is not supported on this model, column ${key}By not found`);
|
|
140
|
+
}
|
|
141
|
+
whereClauses = { ...whereClauses, [Op.eq]: event.by };
|
|
142
|
+
}
|
|
143
|
+
options.where[`${key}At`] = whereClauses;
|
|
144
|
+
});
|
|
145
|
+
return options;
|
|
146
|
+
};
|
|
147
|
+
var addReferenceQueries = (options, references, model) => {
|
|
148
|
+
logger3.default(`QueryBuilder adding reference queries with options: ${stringifyJSON(options)}, references: ${stringifyJSON(references)}`);
|
|
149
|
+
Object.keys(references).forEach((key) => {
|
|
150
|
+
logger3.default(`QueryBuilder adding reference query for key: ${key}, references: ${stringifyJSON(references)}`);
|
|
151
|
+
if (!model.getAttributes()[`${key}Id`]) {
|
|
152
|
+
throw new Error(`Reference ${key} is not supported on this model, column ${key}Id not found`);
|
|
153
|
+
}
|
|
154
|
+
if (isPriKey(references[key])) {
|
|
155
|
+
const priKey = references[key];
|
|
156
|
+
if (priKey.pk == null || priKey.pk === "" || typeof priKey.pk === "object" && Object.keys(priKey.pk).length === 0) {
|
|
157
|
+
logger3.error(`Reference key '${key}' has invalid pk value: ${stringifyJSON(priKey.pk)}`, { priKey, references });
|
|
158
|
+
throw new Error(`Reference key '${key}' has invalid pk value: ${stringifyJSON(priKey.pk)}`);
|
|
159
|
+
}
|
|
160
|
+
logger3.trace(`[QueryBuilder] Setting reference where clause: ${key}Id = ${stringifyJSON(priKey.pk)} (type: ${typeof priKey.pk})`);
|
|
161
|
+
options.where[`${key}Id`] = {
|
|
162
|
+
[Op.eq]: priKey.pk
|
|
163
|
+
};
|
|
164
|
+
} else if (isComKey(references[key])) {
|
|
165
|
+
throw new Error("ComKeys are not supported in Sequelize");
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return options;
|
|
169
|
+
};
|
|
170
|
+
var addCompoundCondition = (options, compoundCondition, model) => {
|
|
171
|
+
options.where = options.where || {};
|
|
172
|
+
let compoundOp;
|
|
173
|
+
const compoundType = compoundCondition.compoundType;
|
|
174
|
+
if (compoundType === "AND") {
|
|
175
|
+
compoundOp = Op.and;
|
|
176
|
+
} else {
|
|
177
|
+
compoundOp = Op.or;
|
|
178
|
+
}
|
|
179
|
+
;
|
|
180
|
+
let conditions = {};
|
|
181
|
+
compoundCondition.conditions.forEach((condition) => {
|
|
182
|
+
if (isCondition(condition)) {
|
|
183
|
+
conditions = addCondition(conditions, condition, model);
|
|
184
|
+
} else {
|
|
185
|
+
throw new Error("Nest Compound conditions not supported");
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
if (Object.keys(options.where).length > 0) {
|
|
189
|
+
options.where = {
|
|
190
|
+
[Op.and]: [
|
|
191
|
+
options.where,
|
|
192
|
+
{ [compoundOp]: conditions }
|
|
193
|
+
]
|
|
194
|
+
};
|
|
195
|
+
} else {
|
|
196
|
+
options.where[compoundOp] = conditions;
|
|
197
|
+
}
|
|
198
|
+
return options;
|
|
199
|
+
};
|
|
200
|
+
var getSequelizeOperator = (operator) => {
|
|
201
|
+
if (operator === "==") {
|
|
202
|
+
return Op.eq;
|
|
203
|
+
} else if (operator === "<") {
|
|
204
|
+
return Op.lt;
|
|
205
|
+
} else if (operator === ">") {
|
|
206
|
+
return Op.gt;
|
|
207
|
+
} else if (operator === "<=") {
|
|
208
|
+
return Op.lte;
|
|
209
|
+
} else if (operator === ">=") {
|
|
210
|
+
return Op.gte;
|
|
211
|
+
} else if (operator === "in") {
|
|
212
|
+
return Op.in;
|
|
213
|
+
} else {
|
|
214
|
+
throw new Error(`Operator ${operator} not supported`);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
var addAssociationCondition = (conditions, condition, model) => {
|
|
218
|
+
const [associationName, attributeName] = condition.column.split(".", 2);
|
|
219
|
+
if (!model.associations || !model.associations[associationName]) {
|
|
220
|
+
throw new Error(`Association ${associationName} not found on model ${model.name}`);
|
|
221
|
+
}
|
|
222
|
+
const association = model.associations[associationName];
|
|
223
|
+
const associatedModel = association.target;
|
|
224
|
+
if (!associatedModel.getAttributes()[attributeName]) {
|
|
225
|
+
throw new Error(`Attribute ${attributeName} not found on associated model ${associatedModel.name} for association ${associationName}`);
|
|
226
|
+
}
|
|
227
|
+
const sequelizeAssociationColumn = `$${associationName}.${attributeName}$`;
|
|
228
|
+
const conditionOp = getSequelizeOperator(condition.operator);
|
|
229
|
+
if (condition.value == null && condition.operator !== "==" && condition.operator !== "in") {
|
|
230
|
+
logger3.error(`Association condition for '${associationName}.${attributeName}' has undefined/null value`, { condition });
|
|
231
|
+
throw new Error(`Association condition for '${associationName}.${attributeName}' has undefined/null value`);
|
|
232
|
+
}
|
|
233
|
+
logger3.trace(`[QueryBuilder] Setting association condition: ${sequelizeAssociationColumn} = ${stringifyJSON(condition.value)} (type: ${typeof condition.value})`);
|
|
234
|
+
conditions[sequelizeAssociationColumn] = {
|
|
235
|
+
[conditionOp]: condition.value
|
|
236
|
+
};
|
|
237
|
+
return conditions;
|
|
238
|
+
};
|
|
239
|
+
var addAttributeCondition = (conditions, condition, model) => {
|
|
240
|
+
const conditionColumn = condition.column;
|
|
241
|
+
if (!model.getAttributes()[conditionColumn]) {
|
|
242
|
+
throw new Error(`Condition column ${conditionColumn} not found on model ${model.name}`);
|
|
243
|
+
}
|
|
244
|
+
const conditionOp = getSequelizeOperator(condition.operator);
|
|
245
|
+
if (condition.value == null && condition.operator !== "==" && condition.operator !== "in") {
|
|
246
|
+
logger3.error(`Attribute condition for '${conditionColumn}' has undefined/null value`, { condition });
|
|
247
|
+
throw new Error(`Attribute condition for '${conditionColumn}' has undefined/null value`);
|
|
248
|
+
}
|
|
249
|
+
logger3.trace(`[QueryBuilder] Setting attribute condition: ${conditionColumn} = ${stringifyJSON(condition.value)} (type: ${typeof condition.value})`);
|
|
250
|
+
conditions[conditionColumn] = {
|
|
251
|
+
[conditionOp]: condition.value
|
|
252
|
+
};
|
|
253
|
+
return conditions;
|
|
254
|
+
};
|
|
255
|
+
var addCondition = (conditions, condition, model) => {
|
|
256
|
+
const conditionColumn = condition.column;
|
|
257
|
+
if (conditionColumn.includes(".")) {
|
|
258
|
+
return addAssociationCondition(conditions, condition, model);
|
|
259
|
+
}
|
|
260
|
+
return addAttributeCondition(conditions, condition, model);
|
|
261
|
+
};
|
|
262
|
+
var collectAssociationsFromConditions = (conditions) => {
|
|
263
|
+
const associations = /* @__PURE__ */ new Set();
|
|
264
|
+
const processObject = (obj) => {
|
|
265
|
+
if (typeof obj === "object" && obj !== null) {
|
|
266
|
+
Object.keys(obj).forEach((key) => {
|
|
267
|
+
if (typeof key === "string" && key.startsWith("$") && key.endsWith("$") && key.includes(".")) {
|
|
268
|
+
const associationName = key.substring(1, key.indexOf("."));
|
|
269
|
+
associations.add(associationName);
|
|
270
|
+
}
|
|
271
|
+
if (typeof obj[key] === "object") {
|
|
272
|
+
processObject(obj[key]);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
Object.getOwnPropertySymbols(obj).forEach((symbol) => {
|
|
276
|
+
if (typeof obj[symbol] === "object") {
|
|
277
|
+
processObject(obj[symbol]);
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
if (Array.isArray(obj)) {
|
|
282
|
+
obj.forEach((item) => {
|
|
283
|
+
if (typeof item === "object") {
|
|
284
|
+
processObject(item);
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
};
|
|
289
|
+
processObject(conditions);
|
|
290
|
+
return associations;
|
|
291
|
+
};
|
|
292
|
+
var addAssociationIncludes = (options, model) => {
|
|
293
|
+
const referencedAssociations = collectAssociationsFromConditions(options.where);
|
|
294
|
+
if (referencedAssociations.size > 0) {
|
|
295
|
+
options.include = options.include || [];
|
|
296
|
+
referencedAssociations.forEach((associationName) => {
|
|
297
|
+
const alreadyIncluded = options.include.some(
|
|
298
|
+
(inc) => typeof inc === "string" && inc === associationName || typeof inc === "object" && inc.association === associationName
|
|
299
|
+
);
|
|
300
|
+
if (!alreadyIncluded && model.associations && model.associations[associationName]) {
|
|
301
|
+
options.include.push({
|
|
302
|
+
model: model.associations[associationName].target,
|
|
303
|
+
as: associationName,
|
|
304
|
+
required: false
|
|
305
|
+
// Use LEFT JOIN so records without associations are still returned
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
return options;
|
|
311
|
+
};
|
|
312
|
+
var buildQuery = (itemQuery, model) => {
|
|
313
|
+
logger3.default(`QueryBuilder build called with itemQuery: ${stringifyJSON(itemQuery)}`);
|
|
314
|
+
let options = {
|
|
315
|
+
where: {}
|
|
316
|
+
};
|
|
317
|
+
if (itemQuery.compoundCondition) {
|
|
318
|
+
logger3.default(`QueryBuilder adding conditions: ${stringifyJSON(itemQuery.compoundCondition)}`);
|
|
319
|
+
options = addCompoundCondition(options, itemQuery.compoundCondition, model);
|
|
320
|
+
}
|
|
321
|
+
if (model.getAttributes().deletedAt || model.getAttributes().isDeleted) {
|
|
322
|
+
options = addDeleteQuery(options, model);
|
|
323
|
+
}
|
|
324
|
+
if (itemQuery.refs) {
|
|
325
|
+
options = addReferenceQueries(options, itemQuery.refs, model);
|
|
326
|
+
}
|
|
327
|
+
if (itemQuery.events) {
|
|
328
|
+
options = addEventQueries(options, itemQuery.events, model);
|
|
329
|
+
}
|
|
330
|
+
if (itemQuery.limit) {
|
|
331
|
+
logger3.default(`QueryBuilder applying limit: ${itemQuery.limit}`);
|
|
332
|
+
options.limit = itemQuery.limit;
|
|
333
|
+
}
|
|
334
|
+
if (itemQuery.offset) {
|
|
335
|
+
options.offset = itemQuery.offset;
|
|
336
|
+
}
|
|
337
|
+
if (itemQuery.orderBy) {
|
|
338
|
+
itemQuery.orderBy.forEach((orderBy) => {
|
|
339
|
+
if (!model.getAttributes()[orderBy.field]) {
|
|
340
|
+
throw new Error(`Order by field ${orderBy.field} not found on model ${model.name}`);
|
|
341
|
+
}
|
|
342
|
+
options.order = [
|
|
343
|
+
[orderBy.field, orderBy.direction]
|
|
344
|
+
];
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
options = addAssociationIncludes(options, model);
|
|
348
|
+
return options;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// src/util/relationshipUtils.ts
|
|
352
|
+
var buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex) => {
|
|
353
|
+
const associationParts = [];
|
|
354
|
+
const modelChain = [targetModel];
|
|
355
|
+
let currentModel = targetModel;
|
|
356
|
+
for (let i = currentIndex + 1; i <= targetIndex; i++) {
|
|
357
|
+
const intermediateType = kta[i];
|
|
358
|
+
const associationName = intermediateType;
|
|
359
|
+
if (!currentModel.associations || !currentModel.associations[associationName]) {
|
|
360
|
+
return { success: false };
|
|
361
|
+
}
|
|
362
|
+
associationParts.push(associationName);
|
|
363
|
+
currentModel = currentModel.associations[associationName].target;
|
|
364
|
+
modelChain.push(currentModel);
|
|
365
|
+
}
|
|
366
|
+
const targetPrimaryKey = currentModel.primaryKeyAttribute || "id";
|
|
367
|
+
const associationPath = `$${associationParts.join(".")}.${targetPrimaryKey}$`;
|
|
368
|
+
let deepestInclude = null;
|
|
369
|
+
for (let i = targetIndex; i > currentIndex; i--) {
|
|
370
|
+
const currentType = kta[i];
|
|
371
|
+
const modelIndex = i - currentIndex;
|
|
372
|
+
const includeObj = {
|
|
373
|
+
model: modelChain[modelIndex],
|
|
374
|
+
as: currentType,
|
|
375
|
+
required: true
|
|
376
|
+
};
|
|
377
|
+
if (deepestInclude) {
|
|
378
|
+
includeObj.include = [deepestInclude];
|
|
379
|
+
}
|
|
380
|
+
deepestInclude = includeObj;
|
|
381
|
+
}
|
|
382
|
+
const includes = deepestInclude ? [deepestInclude] : [];
|
|
383
|
+
return { success: true, path: associationPath, includes };
|
|
384
|
+
};
|
|
385
|
+
var buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = false) => {
|
|
386
|
+
const directFieldName = `${locatorType}Id`;
|
|
387
|
+
const attributes = targetModel.getAttributes();
|
|
388
|
+
if (attributes && attributes[directFieldName]) {
|
|
389
|
+
const result2 = { found: true };
|
|
390
|
+
if (includeIsDirect) {
|
|
391
|
+
result2.isDirect = true;
|
|
392
|
+
}
|
|
393
|
+
return result2;
|
|
394
|
+
}
|
|
395
|
+
const targetIndex = kta.indexOf(locatorType);
|
|
396
|
+
if (targetIndex === -1) {
|
|
397
|
+
const result2 = { found: false };
|
|
398
|
+
if (includeIsDirect) {
|
|
399
|
+
result2.isDirect = false;
|
|
400
|
+
}
|
|
401
|
+
return result2;
|
|
402
|
+
}
|
|
403
|
+
const currentIndex = 0;
|
|
404
|
+
if (targetIndex <= currentIndex) {
|
|
405
|
+
const result2 = { found: false };
|
|
406
|
+
if (includeIsDirect) {
|
|
407
|
+
result2.isDirect = false;
|
|
408
|
+
}
|
|
409
|
+
return result2;
|
|
410
|
+
}
|
|
411
|
+
const chainResult = buildRelationshipChain(targetModel, kta, currentIndex, targetIndex);
|
|
412
|
+
if (chainResult.success) {
|
|
413
|
+
const result2 = {
|
|
414
|
+
found: true,
|
|
415
|
+
path: chainResult.path,
|
|
416
|
+
includes: chainResult.includes
|
|
417
|
+
};
|
|
418
|
+
if (includeIsDirect) {
|
|
419
|
+
result2.isDirect = false;
|
|
420
|
+
}
|
|
421
|
+
return result2;
|
|
422
|
+
}
|
|
423
|
+
const result = { found: false };
|
|
424
|
+
if (includeIsDirect) {
|
|
425
|
+
result.isDirect = false;
|
|
426
|
+
}
|
|
427
|
+
return result;
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
// src/KeyMaster.ts
|
|
431
|
+
var logger4 = logger_default.get("sequelize", "KeyMaster");
|
|
432
|
+
var extractLocationKeyValue = (model, item, locatorType, kta) => {
|
|
433
|
+
logger4.default("Extracting location key value", { locatorType, kta });
|
|
434
|
+
const relationshipInfo = buildRelationshipPath(model, locatorType, kta, true);
|
|
435
|
+
if (!relationshipInfo.found) {
|
|
436
|
+
throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships.`);
|
|
437
|
+
}
|
|
438
|
+
if (relationshipInfo.isDirect) {
|
|
439
|
+
const foreignKeyField = `${locatorType}Id`;
|
|
440
|
+
const value = item[foreignKeyField];
|
|
441
|
+
if (typeof value === "undefined" || value === null) {
|
|
442
|
+
throw new Error(`Direct foreign key field '${foreignKeyField}' is missing or null in item`);
|
|
443
|
+
}
|
|
444
|
+
return value;
|
|
445
|
+
} else {
|
|
446
|
+
const locatorIndex = kta.indexOf(locatorType);
|
|
447
|
+
if (locatorIndex === -1) {
|
|
448
|
+
throw new Error(`Locator type '${locatorType}' not found in key type array`);
|
|
449
|
+
}
|
|
450
|
+
let currentObject = item;
|
|
451
|
+
for (let i = 1; i < locatorIndex; i++) {
|
|
452
|
+
const intermediateType = kta[i];
|
|
453
|
+
if (currentObject[intermediateType] && typeof currentObject[intermediateType] === "object") {
|
|
454
|
+
currentObject = currentObject[intermediateType];
|
|
455
|
+
} else {
|
|
456
|
+
const foreignKeyField2 = `${intermediateType}Id`;
|
|
457
|
+
if (typeof currentObject[foreignKeyField2] !== "undefined" && currentObject[foreignKeyField2] !== null) {
|
|
458
|
+
throw new Error(`Intermediate relationship '${intermediateType}' is not loaded. Cannot traverse to '${locatorType}'. Either include the relationship in your query or ensure it's loaded.`);
|
|
459
|
+
}
|
|
460
|
+
throw new Error(`Intermediate relationship '${intermediateType}' is missing in the relationship chain. Expected path: ${kta.slice(0, locatorIndex + 1).join(" \u2192 ")}`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
if (currentObject[locatorType] && typeof currentObject[locatorType] === "object" && typeof currentObject[locatorType].id !== "undefined") {
|
|
464
|
+
return currentObject[locatorType].id;
|
|
465
|
+
}
|
|
466
|
+
const foreignKeyField = `${locatorType}Id`;
|
|
467
|
+
if (typeof currentObject[foreignKeyField] !== "undefined" && currentObject[foreignKeyField] !== null) {
|
|
468
|
+
return currentObject[foreignKeyField];
|
|
469
|
+
}
|
|
470
|
+
const traversalPath = kta.slice(0, locatorIndex + 1).join(" \u2192 ");
|
|
471
|
+
throw new Error(
|
|
472
|
+
`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available. Traversal path: ${traversalPath}`
|
|
473
|
+
);
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
var removeKey = (item) => {
|
|
477
|
+
logger4.default("Removing Key", { item });
|
|
478
|
+
delete item.key;
|
|
479
|
+
return item;
|
|
480
|
+
};
|
|
481
|
+
var addKey = (model, item, keyTypes) => {
|
|
482
|
+
logger4.default("Adding Key", { item });
|
|
483
|
+
const key = {};
|
|
484
|
+
const modelClass = model.constructor;
|
|
485
|
+
const primaryKeyAttr = modelClass.primaryKeyAttribute;
|
|
486
|
+
if (Array.isArray(keyTypes) && keyTypes.length > 1) {
|
|
487
|
+
const type = [...keyTypes];
|
|
488
|
+
const pkType = type.shift();
|
|
489
|
+
Object.assign(key, { kt: pkType, pk: item[primaryKeyAttr] });
|
|
490
|
+
const locationKeys = [];
|
|
491
|
+
for (const locatorType of type) {
|
|
492
|
+
try {
|
|
493
|
+
const lk = extractLocationKeyValue(modelClass, item, locatorType, keyTypes);
|
|
494
|
+
locationKeys.push({ kt: locatorType, lk });
|
|
495
|
+
} catch (error) {
|
|
496
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
497
|
+
logger4.error(`Failed to extract location key for '${locatorType}'`, { error: errorMessage, item, keyTypes });
|
|
498
|
+
throw error;
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
Object.assign(key, { loc: locationKeys });
|
|
502
|
+
} else {
|
|
503
|
+
Object.assign(key, { kt: keyTypes[0], pk: item[primaryKeyAttr] });
|
|
504
|
+
}
|
|
505
|
+
Object.assign(item, { key });
|
|
506
|
+
return item;
|
|
507
|
+
};
|
|
508
|
+
|
|
509
|
+
// src/ReferenceBuilder.ts
|
|
510
|
+
var logger5 = logger_default.get("sequelize", "ReferenceBuilder");
|
|
511
|
+
var buildReference = async (item, referenceDefinition, registry, context) => {
|
|
512
|
+
if (referenceDefinition.kta.length > 1) {
|
|
513
|
+
throw new Error("The ReferenceBuilder doesn't work with more than one key type yet");
|
|
514
|
+
}
|
|
515
|
+
if (!registry) {
|
|
516
|
+
throw new Error("This model definition has a reference definition, but the registry is not present");
|
|
517
|
+
}
|
|
518
|
+
const library = registry.get(referenceDefinition.kta);
|
|
519
|
+
if (!library) {
|
|
520
|
+
throw new Error("This model definition has a reference definition, but the dependency is not present");
|
|
521
|
+
}
|
|
522
|
+
const columnValue = item[referenceDefinition.column];
|
|
523
|
+
if (columnValue == null) {
|
|
524
|
+
item[referenceDefinition.property] = null;
|
|
525
|
+
return item;
|
|
526
|
+
}
|
|
527
|
+
const priKey = {
|
|
528
|
+
kt: referenceDefinition.kta[0],
|
|
529
|
+
pk: columnValue
|
|
530
|
+
};
|
|
531
|
+
let referencedItem;
|
|
532
|
+
if (context) {
|
|
533
|
+
if (context.isCached(priKey)) {
|
|
534
|
+
logger5.default("Using cached reference", { priKey, property: referenceDefinition.property });
|
|
535
|
+
referencedItem = context.getCached(priKey);
|
|
536
|
+
} else if (context.isInProgress(priKey)) {
|
|
537
|
+
logger5.default("Circular dependency detected, creating reference placeholder", {
|
|
538
|
+
priKey,
|
|
539
|
+
property: referenceDefinition.property
|
|
540
|
+
});
|
|
541
|
+
referencedItem = {
|
|
542
|
+
key: priKey
|
|
543
|
+
// Add any other minimal properties that might be needed
|
|
544
|
+
// This prevents infinite loops while still providing the key for identification
|
|
545
|
+
};
|
|
546
|
+
} else {
|
|
547
|
+
context.markInProgress(priKey);
|
|
548
|
+
try {
|
|
549
|
+
referencedItem = await library.operations.get(priKey);
|
|
550
|
+
context.setCached(priKey, referencedItem);
|
|
551
|
+
} finally {
|
|
552
|
+
context.markComplete(priKey);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
} else {
|
|
556
|
+
referencedItem = await library.operations.get(priKey);
|
|
557
|
+
}
|
|
558
|
+
item[referenceDefinition.property] = referencedItem;
|
|
559
|
+
return item;
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// src/AggregationBuilder.ts
|
|
563
|
+
import { ikToLKA } from "@fjell/core";
|
|
564
|
+
|
|
565
|
+
// src/OperationContext.ts
|
|
566
|
+
var logger6 = logger_default.get("sequelize", "OperationContext");
|
|
567
|
+
var serializeKey = (key) => {
|
|
568
|
+
if ("pk" in key && "kt" in key && !("loc" in key)) {
|
|
569
|
+
return `${key.kt}:${key.pk}`;
|
|
570
|
+
} else if ("pk" in key && "kt" in key && "loc" in key) {
|
|
571
|
+
const locStr = key.loc.map((l) => `${l.kt}:${l.lk}`).join(",");
|
|
572
|
+
return `${key.kt}:${key.pk}|${locStr}`;
|
|
573
|
+
}
|
|
574
|
+
throw new Error(`Unsupported key type: ${JSON.stringify(key)}`);
|
|
575
|
+
};
|
|
576
|
+
var createOperationContext = () => {
|
|
577
|
+
const inProgress = /* @__PURE__ */ new Set();
|
|
578
|
+
const cache = /* @__PURE__ */ new Map();
|
|
579
|
+
return {
|
|
580
|
+
inProgress,
|
|
581
|
+
cache,
|
|
582
|
+
markInProgress(key) {
|
|
583
|
+
const serialized = serializeKey(key);
|
|
584
|
+
logger6.default("Marking key as in progress", { key, serialized });
|
|
585
|
+
inProgress.add(serialized);
|
|
586
|
+
},
|
|
587
|
+
markComplete(key) {
|
|
588
|
+
const serialized = serializeKey(key);
|
|
589
|
+
logger6.default("Marking key as complete", { key, serialized });
|
|
590
|
+
inProgress.delete(serialized);
|
|
591
|
+
},
|
|
592
|
+
isInProgress(key) {
|
|
593
|
+
const serialized = serializeKey(key);
|
|
594
|
+
const result = inProgress.has(serialized);
|
|
595
|
+
logger6.default("Checking if key is in progress", { key, serialized, result });
|
|
596
|
+
return result;
|
|
597
|
+
},
|
|
598
|
+
getCached(key) {
|
|
599
|
+
const serialized = serializeKey(key);
|
|
600
|
+
const result = cache.get(serialized);
|
|
601
|
+
logger6.default("Getting cached item", { key, serialized, found: !!result });
|
|
602
|
+
return result;
|
|
603
|
+
},
|
|
604
|
+
setCached(key, item) {
|
|
605
|
+
const serialized = serializeKey(key);
|
|
606
|
+
logger6.default("Caching item", { key, serialized });
|
|
607
|
+
cache.set(serialized, item);
|
|
608
|
+
},
|
|
609
|
+
isCached(key) {
|
|
610
|
+
const serialized = serializeKey(key);
|
|
611
|
+
const result = cache.has(serialized);
|
|
612
|
+
logger6.default("Checking if key is cached", { key, serialized, result });
|
|
613
|
+
return result;
|
|
614
|
+
}
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
var ContextManager = class {
|
|
618
|
+
contexts = /* @__PURE__ */ new Map();
|
|
619
|
+
currentContextId = null;
|
|
620
|
+
/**
|
|
621
|
+
* Set the current context for the current operation chain
|
|
622
|
+
*/
|
|
623
|
+
setCurrentContext(context) {
|
|
624
|
+
const contextId = Math.random().toString(36).substring(7);
|
|
625
|
+
this.contexts.set(contextId, context);
|
|
626
|
+
this.currentContextId = contextId;
|
|
627
|
+
logger6.default("Set current context", { contextId });
|
|
628
|
+
return contextId;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Get the current context if one is set
|
|
632
|
+
*/
|
|
633
|
+
getCurrentContext() {
|
|
634
|
+
if (this.currentContextId) {
|
|
635
|
+
const context = this.contexts.get(this.currentContextId);
|
|
636
|
+
logger6.default("Got current context", { contextId: this.currentContextId, found: !!context });
|
|
637
|
+
return context;
|
|
638
|
+
}
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
/**
|
|
642
|
+
* Clear the current context
|
|
643
|
+
*/
|
|
644
|
+
clearCurrentContext() {
|
|
645
|
+
if (this.currentContextId) {
|
|
646
|
+
logger6.default("Clearing current context", { contextId: this.currentContextId });
|
|
647
|
+
this.contexts.delete(this.currentContextId);
|
|
648
|
+
this.currentContextId = null;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Execute a function with a specific context set as current
|
|
653
|
+
*/
|
|
654
|
+
async withContext(context, fn) {
|
|
655
|
+
const previousContextId = this.currentContextId;
|
|
656
|
+
this.setCurrentContext(context);
|
|
657
|
+
try {
|
|
658
|
+
return await fn();
|
|
659
|
+
} finally {
|
|
660
|
+
this.clearCurrentContext();
|
|
661
|
+
if (previousContextId) {
|
|
662
|
+
this.currentContextId = previousContextId;
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
var contextManager = new ContextManager();
|
|
668
|
+
|
|
669
|
+
// src/AggregationBuilder.ts
|
|
670
|
+
var logger7 = logger_default.get("sequelize", "AggregationBuilder");
|
|
671
|
+
var buildAggregation = async (item, aggregationDefinition, registry, context) => {
|
|
672
|
+
const location = ikToLKA(item.key);
|
|
673
|
+
const libraryInstance = registry.get(aggregationDefinition.kta);
|
|
674
|
+
if (!libraryInstance) {
|
|
675
|
+
throw new Error(`Library instance not found for key type array: ${aggregationDefinition.kta.join(", ")}`);
|
|
676
|
+
}
|
|
677
|
+
const aggregationCacheKey = `${aggregationDefinition.kta.join(".")}_${aggregationDefinition.cardinality}_${serializeKey(item.key)}`;
|
|
678
|
+
if (context) {
|
|
679
|
+
if (context.cache.has(aggregationCacheKey)) {
|
|
680
|
+
const cachedResult = context.cache.get(aggregationCacheKey);
|
|
681
|
+
logger7.default("Using cached aggregation result", {
|
|
682
|
+
aggregationCacheKey,
|
|
683
|
+
property: aggregationDefinition.property
|
|
684
|
+
});
|
|
685
|
+
item[aggregationDefinition.property] = cachedResult;
|
|
686
|
+
return item;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return contextManager.withContext(context || contextManager.getCurrentContext() || { inProgress: /* @__PURE__ */ new Set(), cache: /* @__PURE__ */ new Map() }, async () => {
|
|
690
|
+
if (aggregationDefinition.cardinality === "one") {
|
|
691
|
+
return libraryInstance.operations.one({}, location).then((result) => {
|
|
692
|
+
if (context) {
|
|
693
|
+
context.cache.set(aggregationCacheKey, result);
|
|
694
|
+
}
|
|
695
|
+
item[aggregationDefinition.property] = result;
|
|
696
|
+
return item;
|
|
697
|
+
});
|
|
698
|
+
} else {
|
|
699
|
+
return libraryInstance.operations.all({}, location).then((results) => {
|
|
700
|
+
if (context) {
|
|
701
|
+
context.cache.set(aggregationCacheKey, results);
|
|
702
|
+
}
|
|
703
|
+
item[aggregationDefinition.property] = results;
|
|
704
|
+
return item;
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
};
|
|
709
|
+
|
|
710
|
+
// src/EventCoordinator.ts
|
|
711
|
+
import deepmerge from "deepmerge";
|
|
712
|
+
var logger8 = logger_default.get("sequelize", "EventCoordinator");
|
|
713
|
+
var populateEvents = (item) => {
|
|
714
|
+
const events = {
|
|
715
|
+
created: { at: item.createdAt || null },
|
|
716
|
+
updated: { at: item.updatedAt || null },
|
|
717
|
+
deleted: { at: null }
|
|
718
|
+
};
|
|
719
|
+
item.events = events;
|
|
720
|
+
return item;
|
|
721
|
+
};
|
|
722
|
+
var extractEvents = (item) => {
|
|
723
|
+
logger8.default("Extracting Events to database fields", { item });
|
|
724
|
+
if (item.events) {
|
|
725
|
+
if (item.events.created?.at) {
|
|
726
|
+
item.createdAt = item.events.created.at;
|
|
727
|
+
}
|
|
728
|
+
if (item.events.updated?.at) {
|
|
729
|
+
item.updatedAt = item.events.updated.at;
|
|
730
|
+
}
|
|
731
|
+
if (item.events.deleted?.at) {
|
|
732
|
+
item.deletedAt = item.events.deleted.at;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return item;
|
|
736
|
+
};
|
|
737
|
+
var removeEvents = (item) => {
|
|
738
|
+
logger8.default("Removing Events", { item });
|
|
739
|
+
delete item.events;
|
|
740
|
+
return item;
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
// src/RowProcessor.ts
|
|
744
|
+
var logger9 = logger_default.get("sequelize", "RowProcessor");
|
|
745
|
+
var processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry, context) => {
|
|
746
|
+
logger9.default("Processing Row", { row });
|
|
747
|
+
const operationContext = context || createOperationContext();
|
|
748
|
+
return contextManager.withContext(operationContext, async () => {
|
|
749
|
+
let item = row.get({ plain: true });
|
|
750
|
+
logger9.default("Adding Key to Item with Key Types: %s", stringifyJSON(keyTypes));
|
|
751
|
+
item = addKey(row, item, keyTypes);
|
|
752
|
+
item = populateEvents(item);
|
|
753
|
+
logger9.default("Key Added to Item: %s", stringifyJSON(item.key));
|
|
754
|
+
operationContext.markInProgress(item.key);
|
|
755
|
+
try {
|
|
756
|
+
if (referenceDefinitions && referenceDefinitions.length > 0) {
|
|
757
|
+
for (const referenceDefinition of referenceDefinitions) {
|
|
758
|
+
logger9.default("Processing Reference for %s to %s", item.key.kt, stringifyJSON(referenceDefinition.kta));
|
|
759
|
+
item = await buildReference(item, referenceDefinition, registry, operationContext);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
if (aggregationDefinitions && aggregationDefinitions.length > 0) {
|
|
763
|
+
for (const aggregationDefinition of aggregationDefinitions) {
|
|
764
|
+
logger9.default("Processing Aggregation for %s from %s", item.key.kt, stringifyJSON(aggregationDefinition.kta));
|
|
765
|
+
item = await buildAggregation(item, aggregationDefinition, registry, operationContext);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
operationContext.setCached(item.key, item);
|
|
769
|
+
} finally {
|
|
770
|
+
operationContext.markComplete(item.key);
|
|
771
|
+
}
|
|
772
|
+
logger9.default("Processed Row: %j", stringifyJSON(item));
|
|
773
|
+
return item;
|
|
774
|
+
});
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
// src/ops/all.ts
|
|
778
|
+
import { Op as Op2 } from "sequelize";
|
|
779
|
+
var logger10 = logger_default.get("sequelize", "ops", "all");
|
|
780
|
+
var mergeIncludes = (existingIncludes, newIncludes) => {
|
|
781
|
+
const mergedIncludes = [...existingIncludes];
|
|
782
|
+
for (const newInclude of newIncludes) {
|
|
783
|
+
const existingIndex = mergedIncludes.findIndex(
|
|
784
|
+
(existing) => existing.as === newInclude.as && existing.model === newInclude.model
|
|
785
|
+
);
|
|
786
|
+
if (existingIndex === -1) {
|
|
787
|
+
mergedIncludes.push(newInclude);
|
|
788
|
+
} else if (newInclude.include && mergedIncludes[existingIndex].include) {
|
|
789
|
+
mergedIncludes[existingIndex].include = [
|
|
790
|
+
...mergedIncludes[existingIndex].include,
|
|
791
|
+
...newInclude.include
|
|
792
|
+
];
|
|
793
|
+
} else if (newInclude.include) {
|
|
794
|
+
mergedIncludes[existingIndex].include = newInclude.include;
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return mergedIncludes;
|
|
798
|
+
};
|
|
799
|
+
var getAllOperation = (models, definition, registry) => {
|
|
800
|
+
const { coordinate, options: { references, aggregations } } = definition;
|
|
801
|
+
const all = async (itemQuery, locations) => {
|
|
802
|
+
logger10.debug(`ALL operation called on ${models[0].name} with ${locations?.length || 0} location filters: ${locations?.map((loc2) => `${loc2.kt}=${loc2.lk}`).join(", ") || "none"}`);
|
|
803
|
+
const loc = locations || [];
|
|
804
|
+
const model = models[0];
|
|
805
|
+
const options = buildQuery(itemQuery, model);
|
|
806
|
+
if (loc.length > 0) {
|
|
807
|
+
const { kta } = coordinate;
|
|
808
|
+
const directLocations = [];
|
|
809
|
+
const hierarchicalLocations = [];
|
|
810
|
+
const additionalIncludes = [];
|
|
811
|
+
for (const locKey of loc) {
|
|
812
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
813
|
+
if (!relationshipInfo.found) {
|
|
814
|
+
const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
815
|
+
logger10.error(errorMessage, { locations: loc, kta });
|
|
816
|
+
throw new Error(errorMessage);
|
|
817
|
+
}
|
|
818
|
+
if (relationshipInfo.isDirect) {
|
|
819
|
+
directLocations.push(locKey);
|
|
820
|
+
} else {
|
|
821
|
+
hierarchicalLocations.push(locKey);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
for (const locKey of directLocations) {
|
|
825
|
+
if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
|
|
826
|
+
logger10.error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
827
|
+
throw new Error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
828
|
+
}
|
|
829
|
+
const foreignKeyField = locKey.kt + "Id";
|
|
830
|
+
if (options.where[foreignKeyField]) {
|
|
831
|
+
logger10.debug(`[ALL] Field ${foreignKeyField} already constrained by itemQuery, skipping location constraint to avoid conflicts`);
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
logger10.trace(`[ALL] Setting direct location where clause: ${foreignKeyField} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
|
|
835
|
+
options.where[foreignKeyField] = {
|
|
836
|
+
[Op2.eq]: locKey.lk
|
|
837
|
+
};
|
|
838
|
+
}
|
|
839
|
+
for (const locKey of hierarchicalLocations) {
|
|
840
|
+
if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
|
|
841
|
+
logger10.error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
|
|
842
|
+
throw new Error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
|
|
843
|
+
}
|
|
844
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
|
|
845
|
+
if (relationshipInfo.found && relationshipInfo.path) {
|
|
846
|
+
if (options.where[relationshipInfo.path]) {
|
|
847
|
+
logger10.debug(`[ALL] Field ${relationshipInfo.path} already constrained by itemQuery, skipping hierarchical location constraint to avoid conflicts`);
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
logger10.trace(`[ALL] Setting hierarchical location where clause: ${relationshipInfo.path} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
|
|
851
|
+
options.where[relationshipInfo.path] = {
|
|
852
|
+
[Op2.eq]: locKey.lk
|
|
853
|
+
};
|
|
854
|
+
if (relationshipInfo.includes) {
|
|
855
|
+
additionalIncludes.push(...relationshipInfo.includes);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
if (additionalIncludes.length > 0) {
|
|
860
|
+
const existingIncludes = options.include || [];
|
|
861
|
+
options.include = mergeIncludes(existingIncludes, additionalIncludes);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
logger10.default(`All query configured for ${model.name} with where fields: ${options.where ? Object.keys(options.where).join(", ") : "none"}, includes: ${options.include?.length || 0}`);
|
|
865
|
+
try {
|
|
866
|
+
logger10.trace(`[ALL] Executing ${model.name}.findAll() with options: ${JSON.stringify(options, null, 2)}`);
|
|
867
|
+
} catch {
|
|
868
|
+
logger10.trace(`[ALL] Executing ${model.name}.findAll() with options containing non-serializable operators (${Object.keys(options.where || {}).length} where conditions)`);
|
|
869
|
+
}
|
|
870
|
+
const matchingItems = await model.findAll(options);
|
|
871
|
+
const context = contextManager.getCurrentContext();
|
|
872
|
+
const results = await Promise.all(matchingItems.map(async (row) => {
|
|
873
|
+
const processedRow = await processRow(row, coordinate.kta, references, aggregations, registry, context);
|
|
874
|
+
return validateKeys(processedRow, coordinate.kta);
|
|
875
|
+
}));
|
|
876
|
+
logger10.debug(`[ALL] Returning ${results.length} ${model.name} records`);
|
|
877
|
+
return results;
|
|
878
|
+
};
|
|
879
|
+
return all;
|
|
880
|
+
};
|
|
881
|
+
|
|
882
|
+
// src/ops/create.ts
|
|
883
|
+
import { isComKey as isComKey2, isPriKey as isPriKey2, validateKeys as validateKeys2 } from "@fjell/core";
|
|
884
|
+
var logger11 = logger_default.get("sequelize", "ops", "create");
|
|
885
|
+
function translateDatabaseError(error, itemData, modelName) {
|
|
886
|
+
const originalMessage = error.message || "";
|
|
887
|
+
const errorCode = error.original?.code;
|
|
888
|
+
const constraint = error.original?.constraint;
|
|
889
|
+
const detail = error.original?.detail;
|
|
890
|
+
logger11.error("Database error during create operation", {
|
|
891
|
+
errorCode,
|
|
892
|
+
constraint,
|
|
893
|
+
detail,
|
|
894
|
+
originalMessage,
|
|
895
|
+
modelName,
|
|
896
|
+
itemData: JSON.stringify(itemData, null, 2)
|
|
897
|
+
});
|
|
898
|
+
switch (errorCode) {
|
|
899
|
+
case "23505":
|
|
900
|
+
if (constraint) {
|
|
901
|
+
return new Error(`Duplicate value violates unique constraint '${constraint}'. ${detail || ""}`);
|
|
902
|
+
}
|
|
903
|
+
return new Error(`Duplicate value detected. This record already exists. ${detail || ""}`);
|
|
904
|
+
case "23503":
|
|
905
|
+
if (constraint) {
|
|
906
|
+
return new Error(`Foreign key constraint '${constraint}' violated. Referenced record does not exist. ${detail || ""}`);
|
|
907
|
+
}
|
|
908
|
+
return new Error(`Referenced record does not exist. Check that all related records are valid. ${detail || ""}`);
|
|
909
|
+
case "23502":
|
|
910
|
+
const column = error.original?.column;
|
|
911
|
+
if (column) {
|
|
912
|
+
return new Error(`Required field '${column}' cannot be null`);
|
|
913
|
+
}
|
|
914
|
+
return new Error(`Required field is missing or null`);
|
|
915
|
+
case "23514":
|
|
916
|
+
if (constraint) {
|
|
917
|
+
return new Error(`Check constraint '${constraint}' violated. ${detail || ""}`);
|
|
918
|
+
}
|
|
919
|
+
return new Error(`Data validation failed. Check constraint violated. ${detail || ""}`);
|
|
920
|
+
case "22001":
|
|
921
|
+
return new Error(`Data too long for field. Check string lengths. ${detail || ""}`);
|
|
922
|
+
case "22003":
|
|
923
|
+
return new Error(`Numeric value out of range. Check number values. ${detail || ""}`);
|
|
924
|
+
case "42703":
|
|
925
|
+
const undefinedColumn = error.original?.column;
|
|
926
|
+
if (undefinedColumn) {
|
|
927
|
+
return new Error(`Column '${undefinedColumn}' does not exist in table '${modelName}'`);
|
|
928
|
+
}
|
|
929
|
+
return new Error(`Referenced column does not exist`);
|
|
930
|
+
case "42P01":
|
|
931
|
+
return new Error(`Table '${modelName}' does not exist`);
|
|
932
|
+
default:
|
|
933
|
+
return new Error(`Database error in ${modelName}.create(): ${originalMessage}. Item data: ${JSON.stringify(itemData, null, 2)}`);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
async function validateHierarchicalChain(models, locKey, kta) {
|
|
937
|
+
try {
|
|
938
|
+
const locatorIndex = kta.indexOf(locKey.kt);
|
|
939
|
+
if (locatorIndex === -1) {
|
|
940
|
+
throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
|
|
941
|
+
}
|
|
942
|
+
const locatorModel = models[locatorIndex] || models[0];
|
|
943
|
+
const chainResult = buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
|
|
944
|
+
if (!chainResult.success) {
|
|
945
|
+
const record2 = await locatorModel.findByPk(locKey.lk);
|
|
946
|
+
if (!record2) {
|
|
947
|
+
throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist`);
|
|
948
|
+
}
|
|
949
|
+
return;
|
|
950
|
+
}
|
|
951
|
+
const queryOptions = {
|
|
952
|
+
where: { id: locKey.lk }
|
|
953
|
+
};
|
|
954
|
+
if (chainResult.includes && chainResult.includes.length > 0) {
|
|
955
|
+
queryOptions.include = chainResult.includes;
|
|
956
|
+
}
|
|
957
|
+
const record = await locatorModel.findOne(queryOptions);
|
|
958
|
+
if (!record) {
|
|
959
|
+
throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist or chain is invalid`);
|
|
960
|
+
}
|
|
961
|
+
} catch (error) {
|
|
962
|
+
if (error.original) {
|
|
963
|
+
throw translateDatabaseError(error, { locKey, kta }, locKey.kt);
|
|
964
|
+
}
|
|
965
|
+
throw error;
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
var getCreateOperation = (models, definition, registry) => {
|
|
969
|
+
const create = async (item, options) => {
|
|
970
|
+
const constraints = options?.key ? `key: pk=${options.key.pk}, loc=[${isComKey2(options.key) ? options.key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ") : ""}]` : options?.locations ? `locations: ${options.locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ")}` : "no constraints";
|
|
971
|
+
logger11.debug(`CREATE operation called on ${models[0].name} with ${constraints}`);
|
|
972
|
+
logger11.default(`Create configured for ${models[0].name} with ${Object.keys(item).length} item fields`);
|
|
973
|
+
const { coordinate, options: { references, aggregations } } = definition;
|
|
974
|
+
const { kta } = coordinate;
|
|
975
|
+
const model = models[0];
|
|
976
|
+
const modelAttributes = model.getAttributes();
|
|
977
|
+
let itemData = { ...item };
|
|
978
|
+
itemData = extractEvents(itemData);
|
|
979
|
+
itemData = removeEvents(itemData);
|
|
980
|
+
const invalidAttributes = [];
|
|
981
|
+
for (const key of Object.keys(itemData)) {
|
|
982
|
+
if (!modelAttributes[key]) {
|
|
983
|
+
invalidAttributes.push(key);
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
if (invalidAttributes.length > 0) {
|
|
987
|
+
const availableAttributes = Object.keys(modelAttributes).join(", ");
|
|
988
|
+
throw new Error(
|
|
989
|
+
`Invalid attributes for model '${model.name}': [${invalidAttributes.join(", ")}]. Available attributes: [${availableAttributes}]. Item data: ${JSON.stringify(itemData, null, 2)}`
|
|
990
|
+
);
|
|
991
|
+
}
|
|
992
|
+
if (options?.key) {
|
|
993
|
+
const key = options.key;
|
|
994
|
+
if (isPriKey2(key)) {
|
|
995
|
+
itemData.id = key.pk;
|
|
996
|
+
} else if (isComKey2(key)) {
|
|
997
|
+
itemData.id = key.pk;
|
|
998
|
+
const comKey = key;
|
|
999
|
+
const directLocations = [];
|
|
1000
|
+
const hierarchicalLocations = [];
|
|
1001
|
+
for (const locKey of comKey.loc) {
|
|
1002
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
1003
|
+
if (!relationshipInfo.found) {
|
|
1004
|
+
const associations = model.associations ? Object.keys(model.associations) : [];
|
|
1005
|
+
const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Composite key: ${JSON.stringify(comKey, null, 2)}`;
|
|
1006
|
+
logger11.error(errorMessage, { key: comKey, kta, associations });
|
|
1007
|
+
throw new Error(errorMessage);
|
|
1008
|
+
}
|
|
1009
|
+
if (relationshipInfo.isDirect) {
|
|
1010
|
+
directLocations.push(locKey);
|
|
1011
|
+
} else {
|
|
1012
|
+
hierarchicalLocations.push(locKey);
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
for (const locKey of directLocations) {
|
|
1016
|
+
if (locKey.lk == null || locKey.lk === "") {
|
|
1017
|
+
logger11.error(`Composite key location '${locKey.kt}' has undefined/null lk value`, { locKey, key: comKey });
|
|
1018
|
+
throw new Error(`Composite key location '${locKey.kt}' has undefined/null lk value`);
|
|
1019
|
+
}
|
|
1020
|
+
const foreignKeyField = locKey.kt + "Id";
|
|
1021
|
+
itemData[foreignKeyField] = locKey.lk;
|
|
1022
|
+
}
|
|
1023
|
+
for (const locKey of hierarchicalLocations) {
|
|
1024
|
+
await validateHierarchicalChain(models, locKey, kta);
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
if (options?.locations) {
|
|
1029
|
+
const directLocations = [];
|
|
1030
|
+
const hierarchicalLocations = [];
|
|
1031
|
+
for (const locKey of options.locations) {
|
|
1032
|
+
const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
|
|
1033
|
+
if (!relationshipInfo.found) {
|
|
1034
|
+
const associations = model.associations ? Object.keys(model.associations) : [];
|
|
1035
|
+
const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Locations: ${JSON.stringify(options.locations, null, 2)}`;
|
|
1036
|
+
logger11.error(errorMessage, { locations: options.locations, kta, associations });
|
|
1037
|
+
throw new Error(errorMessage);
|
|
1038
|
+
}
|
|
1039
|
+
if (relationshipInfo.isDirect) {
|
|
1040
|
+
directLocations.push(locKey);
|
|
1041
|
+
} else {
|
|
1042
|
+
hierarchicalLocations.push(locKey);
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
for (const locKey of directLocations) {
|
|
1046
|
+
if (locKey.lk == null || locKey.lk === "") {
|
|
1047
|
+
logger11.error(`Location option '${locKey.kt}' has undefined/null lk value`, { locKey, locations: options.locations });
|
|
1048
|
+
throw new Error(`Location option '${locKey.kt}' has undefined/null lk value`);
|
|
1049
|
+
}
|
|
1050
|
+
const foreignKeyField = locKey.kt + "Id";
|
|
1051
|
+
itemData[foreignKeyField] = locKey.lk;
|
|
1052
|
+
}
|
|
1053
|
+
for (const locKey of hierarchicalLocations) {
|
|
1054
|
+
await validateHierarchicalChain(models, locKey, kta);
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
try {
|
|
1058
|
+
logger11.trace(`[CREATE] Executing ${model.name}.create() with data: ${stringifyJSON(itemData)}`);
|
|
1059
|
+
const createdRecord = await model.create(itemData);
|
|
1060
|
+
const processedRecord = await processRow(createdRecord, kta, references, aggregations, registry);
|
|
1061
|
+
const result = validateKeys2(processedRecord, kta);
|
|
1062
|
+
logger11.debug(`[CREATE] Created ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${createdRecord.id}`}`);
|
|
1063
|
+
return result;
|
|
1064
|
+
} catch (error) {
|
|
1065
|
+
throw translateDatabaseError(error, itemData, model.name);
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
return create;
|
|
1069
|
+
};
|
|
1070
|
+
|
|
1071
|
+
// src/ops/find.ts
|
|
1072
|
+
import { validateKeys as validateKeys3 } from "@fjell/core";
|
|
1073
|
+
var logger12 = logger_default.get("sequelize", "ops", "find");
|
|
1074
|
+
var getFindOperation = (models, definition, registry) => {
|
|
1075
|
+
const { options: { finders, references, aggregations } } = definition;
|
|
1076
|
+
const find = async (finder, finderParams, locations) => {
|
|
1077
|
+
const locationFilters = locations?.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none";
|
|
1078
|
+
logger12.debug(
|
|
1079
|
+
`FIND operation called on ${models[0].name} with finder '${finder}' and ${locations?.length || 0} location filters: ${locationFilters}`
|
|
1080
|
+
);
|
|
1081
|
+
logger12.default(`Find configured for ${models[0].name} using finder '${finder}' with ${Object.keys(finderParams).length} params`);
|
|
1082
|
+
if (finders && finders[finder]) {
|
|
1083
|
+
const finderMethod = finders[finder];
|
|
1084
|
+
if (finderMethod) {
|
|
1085
|
+
logger12.trace(`[FIND] Executing finder '${finder}' on ${models[0].name} with params: ${stringifyJSON(finderParams)}, locations: ${stringifyJSON(locations)}`);
|
|
1086
|
+
const results = await finderMethod(finderParams, locations);
|
|
1087
|
+
if (results && results.length > 0) {
|
|
1088
|
+
const processedResults = await Promise.all(results.map(async (row) => {
|
|
1089
|
+
const processedRow = await processRow(row, definition.coordinate.kta, references, aggregations, registry);
|
|
1090
|
+
return validateKeys3(processedRow, definition.coordinate.kta);
|
|
1091
|
+
}));
|
|
1092
|
+
logger12.debug(`[FIND] Found ${processedResults.length} ${models[0].name} records using finder '${finder}'`);
|
|
1093
|
+
return processedResults;
|
|
1094
|
+
} else {
|
|
1095
|
+
logger12.debug(`[FIND] Found 0 ${models[0].name} records using finder '${finder}'`);
|
|
1096
|
+
return [];
|
|
1097
|
+
}
|
|
1098
|
+
} else {
|
|
1099
|
+
logger12.error(`Finder %s not found`, finder);
|
|
1100
|
+
throw new Error(`Finder ${finder} not found`);
|
|
1101
|
+
}
|
|
1102
|
+
} else {
|
|
1103
|
+
logger12.error(`No finders have been defined for this lib`);
|
|
1104
|
+
throw new Error(`No finders found`);
|
|
1105
|
+
}
|
|
1106
|
+
};
|
|
1107
|
+
return find;
|
|
1108
|
+
};
|
|
1109
|
+
|
|
1110
|
+
// src/ops/get.ts
|
|
1111
|
+
import {
|
|
1112
|
+
isComKey as isComKey3,
|
|
1113
|
+
isPriKey as isPriKey3,
|
|
1114
|
+
isValidItemKey,
|
|
1115
|
+
validateKeys as validateKeys4
|
|
1116
|
+
} from "@fjell/core";
|
|
1117
|
+
import { NotFoundError } from "@fjell/lib";
|
|
1118
|
+
var logger13 = logger_default.get("sequelize", "ops", "get");
|
|
1119
|
+
var processCompositeKey = (comKey, model, kta) => {
|
|
1120
|
+
const where = { id: comKey.pk };
|
|
1121
|
+
const includes = [];
|
|
1122
|
+
for (const locator of comKey.loc) {
|
|
1123
|
+
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
|
|
1124
|
+
if (!relationshipInfo.found) {
|
|
1125
|
+
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
1126
|
+
logger13.error(errorMessage, { key: comKey, kta });
|
|
1127
|
+
throw new Error(errorMessage);
|
|
1128
|
+
}
|
|
1129
|
+
if (relationshipInfo.path) {
|
|
1130
|
+
where[relationshipInfo.path] = locator.lk;
|
|
1131
|
+
if (relationshipInfo.includes) {
|
|
1132
|
+
includes.push(...relationshipInfo.includes);
|
|
1133
|
+
}
|
|
1134
|
+
} else {
|
|
1135
|
+
const fieldName = `${locator.kt}Id`;
|
|
1136
|
+
where[fieldName] = locator.lk;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
const result = { where };
|
|
1140
|
+
if (includes.length > 0) {
|
|
1141
|
+
result.include = includes;
|
|
1142
|
+
}
|
|
1143
|
+
return result;
|
|
1144
|
+
};
|
|
1145
|
+
var getGetOperation = (models, definition, registry) => {
|
|
1146
|
+
const { coordinate, options: { references, aggregations } } = definition;
|
|
1147
|
+
const { kta } = coordinate;
|
|
1148
|
+
const get = async (key) => {
|
|
1149
|
+
if (!isValidItemKey(key)) {
|
|
1150
|
+
logger13.error("Key for Get is not a valid ItemKey: %j", key);
|
|
1151
|
+
throw new Error("Key for Get is not a valid ItemKey");
|
|
1152
|
+
}
|
|
1153
|
+
const keyDescription = isPriKey3(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1154
|
+
logger13.debug(`GET operation called on ${models[0].name} with ${keyDescription}`);
|
|
1155
|
+
logger13.default(`Get configured for ${models[0].name} with ${isPriKey3(key) ? "primary" : "composite"} key`);
|
|
1156
|
+
const itemKey = key;
|
|
1157
|
+
const model = models[0];
|
|
1158
|
+
let item;
|
|
1159
|
+
if (isPriKey3(itemKey)) {
|
|
1160
|
+
logger13.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${itemKey.pk}`);
|
|
1161
|
+
item = await model.findByPk(itemKey.pk);
|
|
1162
|
+
} else if (isComKey3(itemKey)) {
|
|
1163
|
+
const comKey = itemKey;
|
|
1164
|
+
const queryOptions = processCompositeKey(comKey, model, kta);
|
|
1165
|
+
logger13.default("Composite key query", { queryOptions });
|
|
1166
|
+
logger13.trace(`[GET] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1167
|
+
item = await model.findOne(queryOptions);
|
|
1168
|
+
}
|
|
1169
|
+
if (!item) {
|
|
1170
|
+
throw new NotFoundError("get", coordinate, key);
|
|
1171
|
+
} else {
|
|
1172
|
+
const context = contextManager.getCurrentContext();
|
|
1173
|
+
const result = validateKeys4(await processRow(item, kta, references, aggregations, registry, context), kta);
|
|
1174
|
+
logger13.debug(`[GET] Retrieved ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${item.id}`}`);
|
|
1175
|
+
return result;
|
|
1176
|
+
}
|
|
1177
|
+
};
|
|
1178
|
+
return get;
|
|
1179
|
+
};
|
|
1180
|
+
|
|
1181
|
+
// src/ops/one.ts
|
|
1182
|
+
var logger14 = logger_default.get("sequelize", "ops", "one");
|
|
1183
|
+
var getOneOperation = (models, definition, registry) => {
|
|
1184
|
+
const one = async (itemQuery, locations = []) => {
|
|
1185
|
+
logger14.debug(`ONE operation called on ${models[0].name} with ${locations.length} location filters: ${locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none"}`);
|
|
1186
|
+
logger14.default(`One configured for ${models[0].name} delegating to all operation`);
|
|
1187
|
+
const items = await getAllOperation(models, definition, registry)(itemQuery, locations);
|
|
1188
|
+
if (items.length > 0) {
|
|
1189
|
+
const result = items[0];
|
|
1190
|
+
logger14.debug(`[ONE] Found ${models[0].name} record with key: ${result.key ? JSON.stringify(result.key) : "unknown"}`);
|
|
1191
|
+
return result;
|
|
1192
|
+
} else {
|
|
1193
|
+
logger14.debug(`[ONE] No ${models[0].name} record found`);
|
|
1194
|
+
return null;
|
|
1195
|
+
}
|
|
1196
|
+
};
|
|
1197
|
+
return one;
|
|
1198
|
+
};
|
|
1199
|
+
|
|
1200
|
+
// src/ops/remove.ts
|
|
1201
|
+
import { isValidItemKey as isValidItemKey2 } from "@fjell/core";
|
|
1202
|
+
import { abbrevIK, isComKey as isComKey4, isPriKey as isPriKey4 } from "@fjell/core";
|
|
1203
|
+
var logger15 = logger_default.get("sequelize", "ops", "remove");
|
|
1204
|
+
var processCompositeKey2 = (comKey, model, kta) => {
|
|
1205
|
+
const where = { id: comKey.pk };
|
|
1206
|
+
const includes = [];
|
|
1207
|
+
for (const locator of comKey.loc) {
|
|
1208
|
+
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
|
|
1209
|
+
if (!relationshipInfo.found) {
|
|
1210
|
+
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
1211
|
+
logger15.error(errorMessage, { key: comKey, kta });
|
|
1212
|
+
throw new Error(errorMessage);
|
|
1213
|
+
}
|
|
1214
|
+
if (relationshipInfo.path) {
|
|
1215
|
+
where[relationshipInfo.path] = locator.lk;
|
|
1216
|
+
if (relationshipInfo.includes) {
|
|
1217
|
+
includes.push(...relationshipInfo.includes);
|
|
1218
|
+
}
|
|
1219
|
+
} else {
|
|
1220
|
+
const fieldName = `${locator.kt}Id`;
|
|
1221
|
+
where[fieldName] = locator.lk;
|
|
1222
|
+
}
|
|
1223
|
+
}
|
|
1224
|
+
const result = { where };
|
|
1225
|
+
if (includes.length > 0) {
|
|
1226
|
+
result.include = includes;
|
|
1227
|
+
}
|
|
1228
|
+
return result;
|
|
1229
|
+
};
|
|
1230
|
+
var getRemoveOperation = (models, definition, registry) => {
|
|
1231
|
+
const { coordinate, options } = definition;
|
|
1232
|
+
const { kta } = coordinate;
|
|
1233
|
+
const remove = async (key) => {
|
|
1234
|
+
if (!isValidItemKey2(key)) {
|
|
1235
|
+
logger15.error("Key for Remove is not a valid ItemKey: %j", key);
|
|
1236
|
+
throw new Error("Key for Remove is not a valid ItemKey");
|
|
1237
|
+
}
|
|
1238
|
+
const keyDescription = isPriKey4(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1239
|
+
logger15.debug(`REMOVE operation called on ${models[0].name} with ${keyDescription}`);
|
|
1240
|
+
logger15.default(`Remove configured for ${models[0].name} with ${isPriKey4(key) ? "primary" : "composite"} key`);
|
|
1241
|
+
const model = models[0];
|
|
1242
|
+
let item;
|
|
1243
|
+
let returnItem;
|
|
1244
|
+
logger15.debug("remove: %s", abbrevIK(key));
|
|
1245
|
+
if (isPriKey4(key)) {
|
|
1246
|
+
logger15.debug(`[REMOVE] Executing ${model.name}.findByPk() with pk: ${key.pk}`);
|
|
1247
|
+
item = await model.findByPk(key.pk);
|
|
1248
|
+
} else if (isComKey4(key)) {
|
|
1249
|
+
const comKey = key;
|
|
1250
|
+
const queryOptions = processCompositeKey2(comKey, model, kta);
|
|
1251
|
+
logger15.default(`Remove composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
|
|
1252
|
+
logger15.debug(`[REMOVE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1253
|
+
item = await model.findOne(queryOptions);
|
|
1254
|
+
}
|
|
1255
|
+
if (!item) {
|
|
1256
|
+
throw new Error(`Item not found for removal with key: ${abbrevIK(key)}`);
|
|
1257
|
+
}
|
|
1258
|
+
const isDeletedAttribute = model.getAttributes().isDeleted;
|
|
1259
|
+
const deletedAtAttribute = model.getAttributes().deletedAt;
|
|
1260
|
+
if (isDeletedAttribute || deletedAtAttribute) {
|
|
1261
|
+
if (model.getAttributes().isDeleted) {
|
|
1262
|
+
item.isDeleted = true;
|
|
1263
|
+
}
|
|
1264
|
+
if (model.getAttributes().deletedAt) {
|
|
1265
|
+
item.deletedAt = /* @__PURE__ */ new Date();
|
|
1266
|
+
}
|
|
1267
|
+
logger15.debug(`[REMOVE] Executing ${model.name}.save() for soft delete`);
|
|
1268
|
+
await item?.save();
|
|
1269
|
+
returnItem = item?.get({ plain: true });
|
|
1270
|
+
returnItem = addKey(item, returnItem, kta);
|
|
1271
|
+
returnItem = populateEvents(returnItem);
|
|
1272
|
+
} else if (options.deleteOnRemove) {
|
|
1273
|
+
logger15.debug(`[REMOVE] Executing ${model.name}.destroy() for hard delete`);
|
|
1274
|
+
await item?.destroy();
|
|
1275
|
+
returnItem = item?.get({ plain: true });
|
|
1276
|
+
returnItem = addKey(item, returnItem, kta);
|
|
1277
|
+
returnItem = populateEvents(returnItem);
|
|
1278
|
+
} else {
|
|
1279
|
+
throw new Error("No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set");
|
|
1280
|
+
}
|
|
1281
|
+
logger15.debug(`[REMOVE] Removed ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${item.id}`}`);
|
|
1282
|
+
return returnItem;
|
|
1283
|
+
};
|
|
1284
|
+
return remove;
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
// src/ops/update.ts
|
|
1288
|
+
import { abbrevIK as abbrevIK2, isComKey as isComKey5, validateKeys as validateKeys5 } from "@fjell/core";
|
|
1289
|
+
import { isPriKey as isPriKey5 } from "@fjell/core";
|
|
1290
|
+
import { NotFoundError as NotFoundError2 } from "@fjell/lib";
|
|
1291
|
+
import { Op as Op3 } from "sequelize";
|
|
1292
|
+
var logger16 = logger_default.get("sequelize", "ops", "update");
|
|
1293
|
+
var mergeIncludes2 = (existingIncludes, newIncludes) => {
|
|
1294
|
+
const mergedIncludes = [...existingIncludes];
|
|
1295
|
+
for (const newInclude of newIncludes) {
|
|
1296
|
+
const existingIndex = mergedIncludes.findIndex(
|
|
1297
|
+
(existing) => existing.as === newInclude.as && existing.model === newInclude.model
|
|
1298
|
+
);
|
|
1299
|
+
if (existingIndex === -1) {
|
|
1300
|
+
mergedIncludes.push(newInclude);
|
|
1301
|
+
} else if (newInclude.include && mergedIncludes[existingIndex].include) {
|
|
1302
|
+
mergedIncludes[existingIndex].include = [
|
|
1303
|
+
...mergedIncludes[existingIndex].include,
|
|
1304
|
+
...newInclude.include
|
|
1305
|
+
];
|
|
1306
|
+
} else if (newInclude.include) {
|
|
1307
|
+
mergedIncludes[existingIndex].include = newInclude.include;
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return mergedIncludes;
|
|
1311
|
+
};
|
|
1312
|
+
var getUpdateOperation = (models, definition, registry) => {
|
|
1313
|
+
const { options: { references, aggregations } } = definition;
|
|
1314
|
+
const update = async (key, item) => {
|
|
1315
|
+
const keyDescription = isPriKey5(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
|
|
1316
|
+
logger16.debug(`UPDATE operation called on ${models[0].name} with ${keyDescription}`);
|
|
1317
|
+
const { coordinate } = definition;
|
|
1318
|
+
const { kta } = coordinate;
|
|
1319
|
+
logger16.debug("update: %s, %j", abbrevIK2(key), item);
|
|
1320
|
+
const model = models[0];
|
|
1321
|
+
let response;
|
|
1322
|
+
if (isPriKey5(key)) {
|
|
1323
|
+
const priKey = key;
|
|
1324
|
+
logger16.trace(`[UPDATE] Executing ${model.name}.findByPk() with pk: ${priKey.pk}`);
|
|
1325
|
+
response = await model.findByPk(priKey.pk);
|
|
1326
|
+
} else if (isComKey5(key)) {
|
|
1327
|
+
const comKey = key;
|
|
1328
|
+
const where = { id: comKey.pk };
|
|
1329
|
+
const additionalIncludes = [];
|
|
1330
|
+
for (const locator of comKey.loc) {
|
|
1331
|
+
const relationshipInfo = buildRelationshipPath(model, locator.kt, kta, true);
|
|
1332
|
+
if (!relationshipInfo.found) {
|
|
1333
|
+
const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
|
|
1334
|
+
logger16.error(errorMessage, { key: comKey, kta });
|
|
1335
|
+
throw new Error(errorMessage);
|
|
1336
|
+
}
|
|
1337
|
+
if (relationshipInfo.isDirect) {
|
|
1338
|
+
const fieldName = `${locator.kt}Id`;
|
|
1339
|
+
where[fieldName] = locator.lk;
|
|
1340
|
+
} else if (relationshipInfo.path) {
|
|
1341
|
+
where[relationshipInfo.path] = {
|
|
1342
|
+
[Op3.eq]: locator.lk
|
|
1343
|
+
};
|
|
1344
|
+
if (relationshipInfo.includes) {
|
|
1345
|
+
additionalIncludes.push(...relationshipInfo.includes);
|
|
1346
|
+
}
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
const queryOptions = { where };
|
|
1350
|
+
if (additionalIncludes.length > 0) {
|
|
1351
|
+
queryOptions.include = mergeIncludes2([], additionalIncludes);
|
|
1352
|
+
}
|
|
1353
|
+
logger16.default(`Update composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
|
|
1354
|
+
logger16.trace(`[UPDATE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
|
|
1355
|
+
response = await model.findOne(queryOptions);
|
|
1356
|
+
}
|
|
1357
|
+
if (response) {
|
|
1358
|
+
let updateProps = removeKey(item);
|
|
1359
|
+
updateProps = extractEvents(updateProps);
|
|
1360
|
+
updateProps = removeEvents(updateProps);
|
|
1361
|
+
logger16.default(`Update found ${model.name} record to modify`);
|
|
1362
|
+
logger16.default(`Update properties configured: ${Object.keys(updateProps).join(", ")}`);
|
|
1363
|
+
logger16.trace(`[UPDATE] Executing ${model.name}.update() with properties: ${stringifyJSON(updateProps)}`);
|
|
1364
|
+
response = await response.update(updateProps);
|
|
1365
|
+
const processedItem = await processRow(response, kta, references, aggregations, registry);
|
|
1366
|
+
const returnItem = validateKeys5(processedItem, kta);
|
|
1367
|
+
logger16.debug(`[UPDATE] Updated ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${response.id}`}`);
|
|
1368
|
+
return returnItem;
|
|
1369
|
+
} else {
|
|
1370
|
+
throw new NotFoundError2("update", coordinate, key);
|
|
1371
|
+
}
|
|
1372
|
+
};
|
|
1373
|
+
return update;
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/Operations.ts
|
|
1377
|
+
var createOperations = (models, coordinate, registry, options) => {
|
|
1378
|
+
const operations = {};
|
|
1379
|
+
const definition = { coordinate, options };
|
|
1380
|
+
operations.all = getAllOperation(models, definition, registry);
|
|
1381
|
+
operations.one = getOneOperation(models, definition, registry);
|
|
1382
|
+
operations.create = getCreateOperation(models, definition, registry);
|
|
1383
|
+
operations.update = getUpdateOperation(models, definition, registry);
|
|
1384
|
+
operations.get = getGetOperation(models, definition, registry);
|
|
1385
|
+
operations.remove = getRemoveOperation(models, definition, registry);
|
|
1386
|
+
operations.find = getFindOperation(models, definition, registry);
|
|
1387
|
+
operations.upsert = async () => {
|
|
1388
|
+
throw new Error("Not implemented");
|
|
1389
|
+
};
|
|
1390
|
+
return operations;
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
// src/SequelizeLibrary.ts
|
|
1394
|
+
var logger17 = logger_default.get("SequelizeLibrary");
|
|
1395
|
+
var createSequelizeLibrary = (registry, coordinate, models, options) => {
|
|
1396
|
+
logger17.debug("createSequelizeLibrary", { coordinate, models, registry, options });
|
|
1397
|
+
const operations = createOperations(models, coordinate, registry, options);
|
|
1398
|
+
const libLibrary = Library2.createLibrary(registry, coordinate, operations, options);
|
|
1399
|
+
return {
|
|
1400
|
+
...libLibrary,
|
|
1401
|
+
models
|
|
1402
|
+
};
|
|
1403
|
+
};
|
|
1404
|
+
var isSequelizeLibrary = (library) => {
|
|
1405
|
+
return library != null && library.coordinate != null && library.operations != null && library.options != null && library.registry != null && library.models != null && Array.isArray(library.models);
|
|
1406
|
+
};
|
|
1407
|
+
|
|
1408
|
+
// src/SequelizeLibraryFactory.ts
|
|
1409
|
+
var logger18 = logger_default.get("InstanceFactory");
|
|
1410
|
+
var createSequelizeLibraryFactory = (models, options) => {
|
|
1411
|
+
return (coordinate, context) => {
|
|
1412
|
+
logger18.debug("Creating Sequelize instance", {
|
|
1413
|
+
coordinate,
|
|
1414
|
+
registry: context.registry,
|
|
1415
|
+
models: models.map((m) => m.name),
|
|
1416
|
+
options
|
|
1417
|
+
});
|
|
1418
|
+
return createSequelizeLibrary(
|
|
1419
|
+
context.registry,
|
|
1420
|
+
coordinate,
|
|
1421
|
+
models,
|
|
1422
|
+
options
|
|
1423
|
+
);
|
|
1424
|
+
};
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// src/contained/index.ts
|
|
1428
|
+
var contained_exports = {};
|
|
1429
|
+
__export(contained_exports, {
|
|
1430
|
+
createInstance: () => createInstance,
|
|
1431
|
+
createSequelizeLibrary: () => createSequelizeLibrary2
|
|
1432
|
+
});
|
|
1433
|
+
|
|
1434
|
+
// src/contained/SequelizeLibrary.ts
|
|
1435
|
+
import { Contained } from "@fjell/lib";
|
|
1436
|
+
function createSequelizeLibrary2(keyTypes, models, libOptions = {}, scopes = [], registry) {
|
|
1437
|
+
const coordinate = createCoordinate(keyTypes, scopes);
|
|
1438
|
+
const options = createOptions2(libOptions);
|
|
1439
|
+
const operations = createOperations(models, coordinate, registry, options);
|
|
1440
|
+
const wrappedOperations = Contained.wrapOperations(operations, options, coordinate, registry);
|
|
1441
|
+
return {
|
|
1442
|
+
coordinate,
|
|
1443
|
+
registry,
|
|
1444
|
+
operations: wrappedOperations,
|
|
1445
|
+
options,
|
|
1446
|
+
models
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
var createInstance = createSequelizeLibrary2;
|
|
1450
|
+
|
|
1451
|
+
// src/primary/index.ts
|
|
1452
|
+
var primary_exports = {};
|
|
1453
|
+
__export(primary_exports, {
|
|
1454
|
+
createInstance: () => createInstance2,
|
|
1455
|
+
createSequelizeLibrary: () => createSequelizeLibrary3
|
|
1456
|
+
});
|
|
1457
|
+
|
|
1458
|
+
// src/primary/SequelizeLibrary.ts
|
|
1459
|
+
import { Primary } from "@fjell/lib";
|
|
1460
|
+
var logger19 = logger_default.get("lib-sequelize", "primary", "instance");
|
|
1461
|
+
function createSequelizeLibrary3(keyType, models, libOptions = {}, scopes = [], registry) {
|
|
1462
|
+
logger19.debug("createSequelizeLibrary", { keyType, models, libOptions, scopes });
|
|
1463
|
+
const coordinate = createCoordinate([keyType], scopes);
|
|
1464
|
+
const options = createOptions2(libOptions);
|
|
1465
|
+
const operations = createOperations(models, coordinate, registry, options);
|
|
1466
|
+
const wrappedOperations = Primary.wrapOperations(operations, options, coordinate, registry);
|
|
1467
|
+
return {
|
|
1468
|
+
coordinate,
|
|
1469
|
+
registry,
|
|
1470
|
+
operations: wrappedOperations,
|
|
1471
|
+
options,
|
|
1472
|
+
models
|
|
1473
|
+
};
|
|
1474
|
+
}
|
|
1475
|
+
var createInstance2 = createSequelizeLibrary3;
|
|
1476
|
+
export {
|
|
1477
|
+
contained_exports as Contained,
|
|
1478
|
+
primary_exports as Primary,
|
|
1479
|
+
SCOPE_SEQUELIZE,
|
|
1480
|
+
createCoordinate,
|
|
1481
|
+
createDefinition,
|
|
1482
|
+
createOperations,
|
|
1483
|
+
createOptions2 as createOptions,
|
|
1484
|
+
createSequelizeLibrary,
|
|
1485
|
+
createSequelizeLibraryFactory,
|
|
1486
|
+
isSequelizeLibrary
|
|
1487
|
+
};
|
|
1488
|
+
//# sourceMappingURL=index.js.map
|