@fjell/lib-sequelize 4.4.0 → 4.4.2

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.
Files changed (109) hide show
  1. package/dist/cjs/AggregationBuilder.cjs +31 -0
  2. package/dist/cjs/Coordinate.cjs +37 -0
  3. package/dist/cjs/Definition.cjs +46 -0
  4. package/dist/cjs/EventCoordinator.cjs +54 -0
  5. package/dist/cjs/Instance.cjs +40 -0
  6. package/dist/cjs/KeyMaster.cjs +129 -0
  7. package/dist/cjs/Operations.cjs +29 -0
  8. package/dist/cjs/Options.cjs +39 -0
  9. package/dist/cjs/QueryBuilder.cjs +290 -0
  10. package/dist/cjs/ReferenceBuilder.cjs +34 -0
  11. package/dist/cjs/RowProcessor.cjs +41 -0
  12. package/dist/cjs/contained/Instance.cjs +21 -0
  13. package/dist/cjs/contained/index.cjs +10 -0
  14. package/dist/cjs/index.cjs +23 -0
  15. package/dist/cjs/logger.cjs +10 -0
  16. package/dist/cjs/ops/all.cjs +62 -0
  17. package/dist/cjs/ops/create.cjs +154 -0
  18. package/dist/cjs/ops/find.cjs +45 -0
  19. package/dist/cjs/ops/get.cjs +85 -0
  20. package/dist/cjs/ops/one.cjs +26 -0
  21. package/dist/cjs/ops/remove.cjs +111 -0
  22. package/dist/cjs/ops/update.cjs +61 -0
  23. package/dist/cjs/primary/Instance.cjs +31 -0
  24. package/dist/cjs/primary/index.cjs +10 -0
  25. package/dist/cjs/util/general.cjs +52 -0
  26. package/dist/cjs/util/relationshipUtils.cjs +117 -0
  27. package/dist/es/AggregationBuilder.js +27 -0
  28. package/dist/es/EventCoordinator.js +48 -0
  29. package/dist/{Instance.js → es/Instance.js} +6 -5
  30. package/dist/es/KeyMaster.js +124 -0
  31. package/dist/{Operations.js → es/Operations.js} +8 -8
  32. package/dist/{Options.js → es/Options.js} +8 -11
  33. package/dist/{QueryBuilder.js → es/QueryBuilder.js} +123 -21
  34. package/dist/es/ReferenceBuilder.js +30 -0
  35. package/dist/es/RowProcessor.js +37 -0
  36. package/dist/{contained → es/contained}/Instance.js +6 -5
  37. package/dist/{index.js → es/index.js} +1 -1
  38. package/dist/{ops → es/ops}/all.js +7 -4
  39. package/dist/es/ops/create.js +150 -0
  40. package/dist/{ops → es/ops}/find.js +16 -6
  41. package/dist/es/ops/get.js +81 -0
  42. package/dist/{ops → es/ops}/one.js +2 -2
  43. package/dist/{ops → es/ops}/remove.js +51 -11
  44. package/dist/{ops → es/ops}/update.js +15 -15
  45. package/dist/{primary → es/primary}/Instance.js +6 -5
  46. package/dist/es/util/general.js +47 -0
  47. package/dist/es/util/relationshipUtils.js +112 -0
  48. package/dist/index.cjs +1279 -0
  49. package/dist/index.cjs.map +1 -0
  50. package/dist/types/AggregationBuilder.d.ts +4 -0
  51. package/dist/{Definition.d.ts → types/Definition.d.ts} +1 -1
  52. package/dist/{EventCoordinator.d.ts → types/EventCoordinator.d.ts} +1 -0
  53. package/dist/{Instance.d.ts → types/Instance.d.ts} +1 -1
  54. package/dist/types/KeyMaster.d.ts +4 -0
  55. package/dist/{Operations.d.ts → types/Operations.d.ts} +2 -2
  56. package/dist/{Options.d.ts → types/Options.d.ts} +14 -3
  57. package/dist/{QueryBuilder.d.ts → types/QueryBuilder.d.ts} +1 -0
  58. package/dist/types/ReferenceBuilder.d.ts +3 -0
  59. package/dist/{RowProcessor.d.ts → types/RowProcessor.d.ts} +3 -1
  60. package/dist/{contained → types/contained}/Instance.d.ts +2 -1
  61. package/dist/{ops → types/ops}/all.d.ts +2 -1
  62. package/dist/{ops → types/ops}/create.d.ts +3 -2
  63. package/dist/{ops → types/ops}/find.d.ts +2 -1
  64. package/dist/{ops → types/ops}/get.d.ts +2 -1
  65. package/dist/{ops → types/ops}/one.d.ts +2 -1
  66. package/dist/{ops → types/ops}/remove.d.ts +2 -1
  67. package/dist/{ops → types/ops}/update.d.ts +2 -1
  68. package/dist/{primary → types/primary}/Instance.d.ts +2 -1
  69. package/dist/types/util/general.d.ts +4 -0
  70. package/dist/types/util/relationshipUtils.d.ts +21 -0
  71. package/package.json +37 -32
  72. package/dist/EventCoordinator.js +0 -30
  73. package/dist/KeyMaster.d.ts +0 -4
  74. package/dist/KeyMaster.js +0 -84
  75. package/dist/RowProcessor.js +0 -18
  76. package/dist/ops/create.js +0 -18
  77. package/dist/ops/get.js +0 -45
  78. package/src/Coordinate.ts +0 -16
  79. package/src/Definition.ts +0 -49
  80. package/src/EventCoordinator.ts +0 -103
  81. package/src/Instance.ts +0 -44
  82. package/src/KeyMaster.ts +0 -90
  83. package/src/Operations.ts +0 -42
  84. package/src/Options.ts +0 -41
  85. package/src/QueryBuilder.ts +0 -208
  86. package/src/RowProcessor.ts +0 -23
  87. package/src/contained/Instance.ts +0 -44
  88. package/src/contained/index.ts +0 -1
  89. package/src/index.ts +0 -7
  90. package/src/logger.ts +0 -5
  91. package/src/ops/all.ts +0 -76
  92. package/src/ops/create.ts +0 -40
  93. package/src/ops/find.ts +0 -49
  94. package/src/ops/get.ts +0 -67
  95. package/src/ops/one.ts +0 -37
  96. package/src/ops/remove.ts +0 -81
  97. package/src/ops/update.ts +0 -78
  98. package/src/primary/Instance.ts +0 -40
  99. package/src/primary/index.ts +0 -1
  100. /package/dist/{Coordinate.js → es/Coordinate.js} +0 -0
  101. /package/dist/{Definition.js → es/Definition.js} +0 -0
  102. /package/dist/{contained → es/contained}/index.js +0 -0
  103. /package/dist/{logger.js → es/logger.js} +0 -0
  104. /package/dist/{primary → es/primary}/index.js +0 -0
  105. /package/dist/{Coordinate.d.ts → types/Coordinate.d.ts} +0 -0
  106. /package/dist/{contained → types/contained}/index.d.ts +0 -0
  107. /package/dist/{index.d.ts → types/index.d.ts} +0 -0
  108. /package/dist/{logger.d.ts → types/logger.d.ts} +0 -0
  109. /package/dist/{primary → types/primary}/index.d.ts +0 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,1279 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ const Library = require('@fjell/lib');
6
+ const deepmerge = require('deepmerge');
7
+ const Logging = require('@fjell/logging');
8
+ const core = require('@fjell/core');
9
+ const sequelize = require('sequelize');
10
+
11
+ function _interopNamespaceDefault(e) {
12
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
13
+ if (e) {
14
+ for (const k in e) {
15
+ if (k !== 'default') {
16
+ const d = Object.getOwnPropertyDescriptor(e, k);
17
+ Object.defineProperty(n, k, d.get ? d : {
18
+ enumerable: true,
19
+ get: () => e[k]
20
+ });
21
+ }
22
+ }
23
+ }
24
+ n.default = e;
25
+ return Object.freeze(n);
26
+ }
27
+
28
+ const Library__namespace = /*#__PURE__*/_interopNamespaceDefault(Library);
29
+
30
+ const SCOPE_SEQUELIZE = 'sequelize';
31
+ const createCoordinate = (kta, scopes)=>{
32
+ const coordinate = Library__namespace.createCoordinate(kta, [
33
+ SCOPE_SEQUELIZE,
34
+ ...scopes || []
35
+ ]);
36
+ return coordinate;
37
+ };
38
+
39
+ /* eslint-disable @typescript-eslint/no-unused-vars */ /* eslint-disable no-undefined */ const clean = (obj)=>{
40
+ return Object.fromEntries(Object.entries(obj).filter(([_, v])=>v !== undefined));
41
+ };
42
+ //Recursive implementation of jSON.stringify;
43
+ const general.stringifyJSON = function(obj, visited = new Set()) {
44
+ const arrOfKeyVals = [];
45
+ const arrVals = [];
46
+ let objKeys = [];
47
+ /*********CHECK FOR PRIMITIVE TYPES**********/ if (typeof obj === 'number' || typeof obj === 'boolean' || obj === null) return '' + obj;
48
+ else if (typeof obj === 'string') return '"' + obj + '"';
49
+ /*********DETECT CIRCULAR REFERENCES**********/ if (obj instanceof Object && visited.has(obj)) {
50
+ return '"(circular)"';
51
+ } else if (Array.isArray(obj)) {
52
+ //check for empty array
53
+ if (obj[0] === undefined) return '[]';
54
+ else {
55
+ // Add array to visited before processing its elements
56
+ visited.add(obj);
57
+ obj.forEach(function(el) {
58
+ arrVals.push(general.stringifyJSON(el, visited));
59
+ });
60
+ return '[' + arrVals + ']';
61
+ }
62
+ } else if (obj instanceof Object) {
63
+ // Add object to visited before processing its properties
64
+ visited.add(obj);
65
+ //get object keys
66
+ objKeys = Object.keys(obj);
67
+ //set key output;
68
+ objKeys.forEach(function(key) {
69
+ const keyOut = '"' + key + '":';
70
+ const keyValOut = obj[key];
71
+ //skip functions and undefined properties
72
+ if (keyValOut instanceof Function || keyValOut === undefined) return; // Skip this entry entirely instead of pushing an empty string
73
+ else if (typeof keyValOut === 'string') arrOfKeyVals.push(keyOut + '"' + keyValOut + '"');
74
+ else if (typeof keyValOut === 'boolean' || typeof keyValOut === 'number' || keyValOut === null) arrOfKeyVals.push(keyOut + keyValOut);
75
+ else if (keyValOut instanceof Object) {
76
+ arrOfKeyVals.push(keyOut + general.stringifyJSON(keyValOut, visited));
77
+ }
78
+ });
79
+ return '{' + arrOfKeyVals + '}';
80
+ }
81
+ return '';
82
+ };
83
+
84
+ const DEFAULT_OPTIONS = {
85
+ deleteOnRemove: false,
86
+ references: [],
87
+ aggregations: []
88
+ };
89
+ const createOptions = (libOptions)=>{
90
+ const options = Library__namespace.createOptions(libOptions);
91
+ return deepmerge(DEFAULT_OPTIONS, clean(options));
92
+ };
93
+
94
+ const logger$1.default = Logging.getLogger('@fjell/lib-sequelize');
95
+
96
+ const logger$c = logger$1.default.get('lib-sequelize', 'Definition');
97
+ function createDefinition(kta, scopes, libOptions) {
98
+ logger$c.debug('createDefinition', {
99
+ kta,
100
+ scopes,
101
+ libOptions
102
+ });
103
+ const coordinate = createCoordinate(kta, scopes);
104
+ const options = createOptions(libOptions);
105
+ const definition = Library__namespace.createDefinition(coordinate, options);
106
+ return {
107
+ ...definition,
108
+ options
109
+ };
110
+ }
111
+
112
+ const logger$b = logger$1.default.get('sequelize', 'QueryBuilder');
113
+ const addDeleteQuery = (options, model)=>{
114
+ logger$b.default('Adding Delete Query', {
115
+ options
116
+ });
117
+ if (model.getAttributes().deletedAt) {
118
+ options.where['deletedAt'] = {
119
+ [sequelize.Op.eq]: null
120
+ };
121
+ } else if (model.getAttributes().isDeleted) {
122
+ options.where['isDeleted'] = {
123
+ [sequelize.Op.eq]: false
124
+ };
125
+ }
126
+ return options;
127
+ };
128
+ const addEventQueries = (options, events, model)=>{
129
+ logger$b.default('Adding Event Queries', {
130
+ options,
131
+ events
132
+ });
133
+ Object.keys(events).forEach((key)=>{
134
+ if (!model.getAttributes()[`${key}At`]) {
135
+ throw new Error(`Event ${key} is not supported on this model, column ${key}At not found`);
136
+ }
137
+ let whereClauses = {};
138
+ const event = events[key];
139
+ if (event.start) {
140
+ whereClauses = {
141
+ ...whereClauses,
142
+ [sequelize.Op.gte]: new Date(event.start)
143
+ };
144
+ }
145
+ if (event.end) {
146
+ whereClauses = {
147
+ ...whereClauses,
148
+ [sequelize.Op.lt]: new Date(event.end)
149
+ };
150
+ }
151
+ if (event.by) {
152
+ if (!model.getAttributes()[`${key}By`]) {
153
+ throw new Error(`Event ${key} is not supported on this model, column ${key}By not found`);
154
+ }
155
+ whereClauses = {
156
+ ...whereClauses,
157
+ [sequelize.Op.eq]: event.by
158
+ };
159
+ }
160
+ options.where[`${key}At`] = whereClauses;
161
+ });
162
+ return options;
163
+ };
164
+ // Add the references to the query
165
+ const addReferenceQueries = (options, references, model)=>{
166
+ logger$b.default('Adding Reference Queries', {
167
+ options,
168
+ references
169
+ });
170
+ Object.keys(references).forEach((key)=>{
171
+ logger$b.default('Adding Reference Query', {
172
+ key,
173
+ references
174
+ });
175
+ if (!model.getAttributes()[`${key}Id`]) {
176
+ throw new Error(`Reference ${key} is not supported on this model, column ${key}Id not found`);
177
+ }
178
+ if (core.isPriKey(references[key])) {
179
+ const priKey = references[key];
180
+ options.where[`${key}Id`] = {
181
+ [sequelize.Op.eq]: priKey.pk
182
+ };
183
+ } else if (core.isComKey(references[key])) {
184
+ throw new Error('ComKeys are not supported in Sequelize');
185
+ }
186
+ });
187
+ return options;
188
+ };
189
+ const addCompoundCondition = (options, compoundCondition, model)=>{
190
+ // Ensure options.where exists
191
+ options.where = options.where || {};
192
+ let compoundOp;
193
+ const compoundType = compoundCondition.compoundType;
194
+ if (compoundType === "AND") {
195
+ compoundOp = sequelize.Op.and;
196
+ } else {
197
+ compoundOp = sequelize.Op.or;
198
+ }
199
+ let conditions = {};
200
+ compoundCondition.conditions.forEach((condition)=>{
201
+ if (core.isCondition(condition)) {
202
+ conditions = addCondition(conditions, condition, model);
203
+ } else {
204
+ throw new Error('Nest Compound conditions not supported');
205
+ }
206
+ });
207
+ // Merge with existing where conditions instead of replacing
208
+ if (Object.keys(options.where).length > 0) {
209
+ // If there are existing conditions, wrap everything in an AND
210
+ options.where = {
211
+ [sequelize.Op.and]: [
212
+ options.where,
213
+ {
214
+ [compoundOp]: conditions
215
+ }
216
+ ]
217
+ };
218
+ } else {
219
+ // If no existing conditions, just set the compound condition
220
+ options.where[compoundOp] = conditions;
221
+ }
222
+ return options;
223
+ };
224
+ const getSequelizeOperator = (operator)=>{
225
+ if (operator === '==') {
226
+ return sequelize.Op.eq;
227
+ } else if (operator === '<') {
228
+ return sequelize.Op.lt;
229
+ } else if (operator === '>') {
230
+ return sequelize.Op.gt;
231
+ } else if (operator === '<=') {
232
+ return sequelize.Op.lte;
233
+ } else if (operator === '>=') {
234
+ return sequelize.Op.gte;
235
+ } else if (operator === 'in') {
236
+ return sequelize.Op.in;
237
+ } else {
238
+ throw new Error(`Operator ${operator} not supported`);
239
+ }
240
+ };
241
+ const addAssociationCondition = (conditions, condition, model)=>{
242
+ const [associationName, attributeName] = condition.column.split('.', 2);
243
+ // Check if the association exists on the model
244
+ if (!model.associations || !model.associations[associationName]) {
245
+ throw new Error(`Association ${associationName} not found on model ${model.name}`);
246
+ }
247
+ const association = model.associations[associationName];
248
+ const associatedModel = association.target;
249
+ // Check if the attribute exists on the associated model
250
+ if (!associatedModel.getAttributes()[attributeName]) {
251
+ throw new Error(`Attribute ${attributeName} not found on associated model ${associatedModel.name} for association ${associationName}`);
252
+ }
253
+ // Use Sequelize's $association.attribute$ syntax for querying associated models
254
+ const sequelizeAssociationColumn = `$${associationName}.${attributeName}$`;
255
+ const conditionOp = getSequelizeOperator(condition.operator);
256
+ conditions[sequelizeAssociationColumn] = {
257
+ [conditionOp]: condition.value
258
+ };
259
+ return conditions;
260
+ };
261
+ const addAttributeCondition = (conditions, condition, model)=>{
262
+ const conditionColumn = condition.column;
263
+ if (!model.getAttributes()[conditionColumn]) {
264
+ throw new Error(`Condition column ${conditionColumn} not found on model ${model.name}`);
265
+ }
266
+ const conditionOp = getSequelizeOperator(condition.operator);
267
+ conditions[conditionColumn] = {
268
+ [conditionOp]: condition.value
269
+ };
270
+ return conditions;
271
+ };
272
+ const addCondition = (conditions, condition, model)=>{
273
+ const conditionColumn = condition.column;
274
+ // Check if this is an association query (contains a dot)
275
+ if (conditionColumn.includes('.')) {
276
+ return addAssociationCondition(conditions, condition, model);
277
+ }
278
+ // Handle regular column queries
279
+ return addAttributeCondition(conditions, condition, model);
280
+ };
281
+ const collectAssociationsFromConditions = (conditions)=>{
282
+ const associations = new Set();
283
+ const processObject = (obj)=>{
284
+ if (typeof obj === 'object' && obj !== null) {
285
+ // Check string keys
286
+ Object.keys(obj).forEach((key)=>{
287
+ // Check if this is an association reference ($association.attribute$)
288
+ if (typeof key === 'string' && key.startsWith('$') && key.endsWith('$') && key.includes('.')) {
289
+ const associationName = key.substring(1, key.indexOf('.'));
290
+ associations.add(associationName);
291
+ }
292
+ // Recursively process nested objects
293
+ if (typeof obj[key] === 'object') {
294
+ processObject(obj[key]);
295
+ }
296
+ });
297
+ // Also check Symbol keys (for compound conditions like Op.and, Op.or)
298
+ Object.getOwnPropertySymbols(obj).forEach((symbol)=>{
299
+ if (typeof obj[symbol] === 'object') {
300
+ processObject(obj[symbol]);
301
+ }
302
+ });
303
+ }
304
+ // Handle arrays (for compound conditions that might be arrays)
305
+ if (Array.isArray(obj)) {
306
+ obj.forEach((item)=>{
307
+ if (typeof item === 'object') {
308
+ processObject(item);
309
+ }
310
+ });
311
+ }
312
+ };
313
+ processObject(conditions);
314
+ return associations;
315
+ };
316
+ const addAssociationIncludes = (options, model)=>{
317
+ // Collect all association names used in conditions
318
+ const referencedAssociations = collectAssociationsFromConditions(options.where);
319
+ if (referencedAssociations.size > 0) {
320
+ options.include = options.include || [];
321
+ // Add each referenced association to the include array
322
+ referencedAssociations.forEach((associationName)=>{
323
+ // Check if this association is already included
324
+ const alreadyIncluded = options.include.some((inc)=>typeof inc === 'string' && inc === associationName || typeof inc === 'object' && inc.association === associationName);
325
+ if (!alreadyIncluded && model.associations && model.associations[associationName]) {
326
+ options.include.push({
327
+ model: model.associations[associationName].target,
328
+ as: associationName,
329
+ required: false // Use LEFT JOIN so records without associations are still returned
330
+ });
331
+ }
332
+ });
333
+ }
334
+ return options;
335
+ };
336
+ const buildQuery = (itemQuery, model)=>{
337
+ logger$b.default('build', {
338
+ itemQuery
339
+ });
340
+ let options = {
341
+ where: {}
342
+ };
343
+ if (itemQuery.compoundCondition) {
344
+ logger$b.default('Adding Conditions', {
345
+ compoundCondition: itemQuery.compoundCondition
346
+ });
347
+ options = addCompoundCondition(options, itemQuery.compoundCondition, model);
348
+ }
349
+ // If the model has a deletedAt column, we need to add a delete query
350
+ if (model.getAttributes().deletedAt || model.getAttributes().isDeleted) {
351
+ options = addDeleteQuery(options, model);
352
+ }
353
+ if (itemQuery.refs) {
354
+ options = addReferenceQueries(options, itemQuery.refs, model);
355
+ }
356
+ if (itemQuery.events) {
357
+ options = addEventQueries(options, itemQuery.events, model);
358
+ }
359
+ // TODO: Once we start to support Aggs on the server-side, we'll need to parse agg queries
360
+ // Apply a limit to the result set
361
+ if (itemQuery.limit) {
362
+ logger$b.default('Limiting to', {
363
+ limit: itemQuery.limit
364
+ });
365
+ options.limit = itemQuery.limit;
366
+ }
367
+ // Apply an offset to the result set
368
+ if (itemQuery.offset) {
369
+ options.offset = itemQuery.offset;
370
+ }
371
+ // Add orderBy to the query
372
+ if (itemQuery.orderBy) {
373
+ itemQuery.orderBy.forEach((orderBy)=>{
374
+ if (!model.getAttributes()[orderBy.field]) {
375
+ throw new Error(`Order by field ${orderBy.field} not found on model ${model.name}`);
376
+ }
377
+ options.order = [
378
+ [
379
+ orderBy.field,
380
+ orderBy.direction
381
+ ]
382
+ ];
383
+ });
384
+ }
385
+ // Add includes for any associations referenced in conditions
386
+ options = addAssociationIncludes(options, model);
387
+ return options;
388
+ };
389
+
390
+ /* eslint-disable indent */ /**
391
+ * Helper function to build relationship chain includes
392
+ */ const buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex)=>{
393
+ // Build the association path and validate relationships exist
394
+ const associationParts = [];
395
+ const modelChain = [
396
+ targetModel
397
+ ];
398
+ let currentModel = targetModel;
399
+ // Validate that all associations exist and build model chain
400
+ for(let i = currentIndex + 1; i <= targetIndex; i++){
401
+ const intermediateType = kta[i];
402
+ const associationName = intermediateType;
403
+ if (!currentModel.associations || !currentModel.associations[associationName]) {
404
+ return {
405
+ success: false
406
+ };
407
+ }
408
+ associationParts.push(associationName);
409
+ currentModel = currentModel.associations[associationName].target;
410
+ modelChain.push(currentModel);
411
+ }
412
+ // Build the full association path for the target field
413
+ const targetType = kta[targetIndex];
414
+ const associationPath = `$${associationParts.join('.')}.${targetType}Id$`;
415
+ // Build nested includes structure iteratively (clearer than recursion)
416
+ let deepestInclude = null;
417
+ // Build from the deepest level back to the root
418
+ for(let i = targetIndex; i > currentIndex; i--){
419
+ const currentType = kta[i];
420
+ const modelIndex = i - currentIndex;
421
+ const includeObj = {
422
+ model: modelChain[modelIndex],
423
+ as: currentType,
424
+ required: true
425
+ };
426
+ if (deepestInclude) {
427
+ includeObj.include = [
428
+ deepestInclude
429
+ ];
430
+ }
431
+ deepestInclude = includeObj;
432
+ }
433
+ const includes = deepestInclude ? [
434
+ deepestInclude
435
+ ] : [];
436
+ return {
437
+ success: true,
438
+ path: associationPath,
439
+ includes
440
+ };
441
+ };
442
+ /**
443
+ * Helper function to build relationship path for a locator
444
+ * @param includeIsDirect Whether to include the isDirect flag in the result
445
+ */ const relationshipUtils.buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = false)=>{
446
+ // First check if the field exists directly
447
+ const directFieldName = `${locatorType}Id`;
448
+ const attributes = targetModel.getAttributes();
449
+ if (attributes && attributes[directFieldName]) {
450
+ const result = {
451
+ found: true
452
+ };
453
+ if (includeIsDirect) {
454
+ result.isDirect = true;
455
+ }
456
+ return result;
457
+ }
458
+ // If not direct, look for relationship path
459
+ const targetIndex = kta.indexOf(locatorType);
460
+ if (targetIndex === -1) {
461
+ const result = {
462
+ found: false
463
+ };
464
+ if (includeIsDirect) {
465
+ result.isDirect = false;
466
+ }
467
+ return result;
468
+ }
469
+ const currentIndex = 0; // We're always looking from the base model
470
+ if (targetIndex <= currentIndex) {
471
+ const result = {
472
+ found: false
473
+ };
474
+ if (includeIsDirect) {
475
+ result.isDirect = false;
476
+ }
477
+ return result;
478
+ }
479
+ const chainResult = buildRelationshipChain(targetModel, kta, currentIndex, targetIndex);
480
+ if (chainResult.success) {
481
+ const result = {
482
+ found: true,
483
+ path: chainResult.path,
484
+ includes: chainResult.includes
485
+ };
486
+ if (includeIsDirect) {
487
+ result.isDirect = false;
488
+ }
489
+ return result;
490
+ }
491
+ const result = {
492
+ found: false
493
+ };
494
+ if (includeIsDirect) {
495
+ result.isDirect = false;
496
+ }
497
+ return result;
498
+ };
499
+
500
+ const logger$a = logger$1.default.get('sequelize', 'KeyMaster');
501
+ // Helper function to extract location key value from item
502
+ const extractLocationKeyValue = (model, item, locatorType, kta)=>{
503
+ logger$a.default('Extracting location key value', {
504
+ locatorType,
505
+ kta
506
+ });
507
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locatorType, kta, true);
508
+ if (!relationshipInfo.found) {
509
+ throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships.`);
510
+ }
511
+ if (relationshipInfo.isDirect) {
512
+ // Direct foreign key field
513
+ const foreignKeyField = `${locatorType}Id`;
514
+ const value = item[foreignKeyField];
515
+ if (typeof value === 'undefined' || value === null) {
516
+ throw new Error(`Direct foreign key field '${foreignKeyField}' is missing or null in item`);
517
+ }
518
+ return value;
519
+ } else {
520
+ // Need to traverse relationship
521
+ // Try to get the value from the loaded relationship object
522
+ const relationshipObject = item[locatorType];
523
+ if (relationshipObject && typeof relationshipObject.id !== 'undefined') {
524
+ return relationshipObject.id;
525
+ }
526
+ // If the relationship object isn't loaded, we might need to look at the foreign key field
527
+ // This handles cases where we have the foreign key but not the full object
528
+ const foreignKeyField = `${locatorType}Id`;
529
+ if (typeof item[foreignKeyField] !== 'undefined' && item[foreignKeyField] !== null) {
530
+ return item[foreignKeyField];
531
+ }
532
+ throw new Error(`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available.`);
533
+ }
534
+ };
535
+ const removeKey = (item)=>{
536
+ logger$a.default('Removing Key', {
537
+ item
538
+ });
539
+ delete item.key;
540
+ return item;
541
+ };
542
+ // export const populateKey = <
543
+ // S extends string,
544
+ // L1 extends string = never,
545
+ // L2 extends string = never,
546
+ // L3 extends string = never,
547
+ // L4 extends string = never,
548
+ // L5 extends string = never
549
+ // >(
550
+ // item: ItemProperties<S, L1, L2, L3, L4, L5>,
551
+ // keyTypes: AllItemTypeArrays<S, L1, L2, L3, L4, L5>
552
+ // ): ItemProperties<S, L1, L2, L3, L4, L5> => {
553
+ // if (keyTypes.length === 1) {
554
+ // item.key = { kt: keyTypes[0], pk: item.id };
555
+ // delete item.id;
556
+ // } else if (keyTypes.length === 2) {
557
+ // item.key = {
558
+ // kt: keyTypes[0], pk: item.id,
559
+ // // TODO: Shouldn't this be inspecting the model to get the primary key type?
560
+ // loc: [{ kt: keyTypes[1], lk: item[keyTypes[1] + 'Id'] }],
561
+ // };
562
+ // delete item.id;
563
+ // delete item[keyTypes[1] + 'Id'];
564
+ // } else {
565
+ // throw new Error('Not implemented');
566
+ // }
567
+ // return item;
568
+ // }
569
+ const addKey = (model, item, keyTypes)=>{
570
+ logger$a.default('Adding Key', {
571
+ item
572
+ });
573
+ const key = {};
574
+ const modelClass = model.constructor;
575
+ const primaryKeyAttr = modelClass.primaryKeyAttribute;
576
+ if (Array.isArray(keyTypes) && keyTypes.length > 1) {
577
+ const type = [
578
+ ...keyTypes
579
+ ];
580
+ const pkType = type.shift();
581
+ Object.assign(key, {
582
+ kt: pkType,
583
+ pk: item[primaryKeyAttr]
584
+ });
585
+ // Build location keys for composite key
586
+ const locationKeys = [];
587
+ for (const locatorType of type){
588
+ try {
589
+ const lk = extractLocationKeyValue(modelClass, item, locatorType, keyTypes);
590
+ locationKeys.push({
591
+ kt: locatorType,
592
+ lk
593
+ });
594
+ } catch (error) {
595
+ const errorMessage = error instanceof Error ? error.message : String(error);
596
+ logger$a.error(`Failed to extract location key for '${locatorType}'`, {
597
+ error: errorMessage,
598
+ item,
599
+ keyTypes
600
+ });
601
+ throw error;
602
+ }
603
+ }
604
+ Object.assign(key, {
605
+ loc: locationKeys
606
+ });
607
+ } else {
608
+ Object.assign(key, {
609
+ kt: keyTypes[0],
610
+ pk: item[primaryKeyAttr]
611
+ });
612
+ }
613
+ Object.assign(item, {
614
+ key
615
+ });
616
+ return item;
617
+ };
618
+
619
+ const buildReference = async (item, referenceDefinition, registry)=>{
620
+ // Check if there is more than one key type
621
+ if (referenceDefinition.kta.length > 1) {
622
+ throw new Error("The ReferenceBuilder doesn't work with more than one key type yet");
623
+ }
624
+ // Check if dependencies exist
625
+ if (!registry) {
626
+ throw new Error("This model definition has a reference definition, but the registry is not present");
627
+ }
628
+ // Find the Library.Instance for the key type
629
+ const library = registry.get(referenceDefinition.kta);
630
+ if (!library) {
631
+ throw new Error("This model definition has a reference definition, but the dependency is not present");
632
+ }
633
+ // Create a PriKey using the column value from item
634
+ const priKey = {
635
+ kt: referenceDefinition.kta[0],
636
+ pk: item[referenceDefinition.column]
637
+ };
638
+ // Get the referenced item using the Library.Operations get method
639
+ const referencedItem = await library.operations.get(priKey);
640
+ // TODO: In a Fjell-compliant implementation, this value should be stored in the ref property
641
+ // For now, we'll just populate the property directly
642
+ // Store the result in the property on item
643
+ item[referenceDefinition.property] = referencedItem;
644
+ return item;
645
+ };
646
+
647
+ const buildAggregation = async (item, aggregationDefinition, registry)=>{
648
+ const location = core.ikToLKA(item.key);
649
+ // Get the library instance from the registry using the key type array
650
+ const libraryInstance = registry.get(aggregationDefinition.kta);
651
+ if (!libraryInstance) {
652
+ throw new Error(`Library instance not found for key type array: ${aggregationDefinition.kta.join(', ')}`);
653
+ }
654
+ // Based on cardinality, use either one or all operation
655
+ if (aggregationDefinition.cardinality === 'one') {
656
+ // For one-to-one relationship, use the one operation
657
+ return libraryInstance.operations.one({}, location).then((result)=>{
658
+ item[aggregationDefinition.property] = result;
659
+ return item;
660
+ });
661
+ } else {
662
+ // For one-to-many relationship, use the all operation
663
+ return libraryInstance.operations.all({}, location).then((results)=>{
664
+ item[aggregationDefinition.property] = results;
665
+ return item;
666
+ });
667
+ }
668
+ };
669
+
670
+ const logger$9 = logger$1.default.get("sequelize", "EventCoordinator");
671
+ //#endregion
672
+ const populateEvents = (item)=>{
673
+ const events = {
674
+ created: {
675
+ at: item.createdAt || null
676
+ },
677
+ updated: {
678
+ at: item.updatedAt || null
679
+ },
680
+ deleted: {
681
+ at: null
682
+ }
683
+ };
684
+ item.events = events;
685
+ return item;
686
+ };
687
+ const extractEvents = (item)=>{
688
+ logger$9.default('Extracting Events to database fields', {
689
+ item
690
+ });
691
+ if (item.events) {
692
+ var _item_events_created, _item_events_updated, _item_events_deleted;
693
+ if ((_item_events_created = item.events.created) === null || _item_events_created === void 0 ? void 0 : _item_events_created.at) {
694
+ item.createdAt = item.events.created.at;
695
+ }
696
+ if ((_item_events_updated = item.events.updated) === null || _item_events_updated === void 0 ? void 0 : _item_events_updated.at) {
697
+ item.updatedAt = item.events.updated.at;
698
+ }
699
+ if ((_item_events_deleted = item.events.deleted) === null || _item_events_deleted === void 0 ? void 0 : _item_events_deleted.at) {
700
+ item.deletedAt = item.events.deleted.at;
701
+ }
702
+ }
703
+ return item;
704
+ };
705
+ const removeEvents = (item)=>{
706
+ logger$9.default('Removing Events', {
707
+ item
708
+ });
709
+ delete item.events;
710
+ return item;
711
+ };
712
+
713
+ const logger$8 = logger$1.default.get('sequelize', 'RowProcessor');
714
+ const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry)=>{
715
+ logger$8.default('Processing Row', {
716
+ row
717
+ });
718
+ let item = row.get({
719
+ plain: true
720
+ });
721
+ logger$8.default('Adding Key to Item with Key Types: %s', general.stringifyJSON(keyTypes));
722
+ item = addKey(row, item, keyTypes);
723
+ item = populateEvents(item);
724
+ logger$8.default('Key Added to Item: %s', general.stringifyJSON(item.key));
725
+ if (referenceDefinitions && referenceDefinitions.length > 0) {
726
+ for (const referenceDefinition of referenceDefinitions){
727
+ logger$8.default('Processing Reference for %s to %s', item.key.kt, general.stringifyJSON(referenceDefinition.kta));
728
+ item = await buildReference(item, referenceDefinition, registry);
729
+ }
730
+ }
731
+ if (aggregationDefinitions && aggregationDefinitions.length > 0) {
732
+ for (const aggregationDefinition of aggregationDefinitions){
733
+ logger$8.default('Processing Aggregation for %s from %s', item.key.kt, general.stringifyJSON(aggregationDefinition.kta));
734
+ item = await buildAggregation(item, aggregationDefinition, registry);
735
+ }
736
+ }
737
+ logger$8.default('Processed Row: %j', general.stringifyJSON(item));
738
+ return item;
739
+ };
740
+
741
+ const logger$7 = logger$1.default.get('sequelize', 'ops', 'all');
742
+ const all.getAllOperation = (models, definition, registry)=>{
743
+ const { coordinate, options: { references, aggregations } } = definition;
744
+ //#region Query
745
+ const all = async (itemQuery, locations)=>{
746
+ logger$7.default('All', {
747
+ itemQuery,
748
+ locations
749
+ });
750
+ const loc = locations || [];
751
+ // SQ Libs don't support locations
752
+ if (loc.length > 1) {
753
+ throw new Error('Not implemented for more than one location key');
754
+ }
755
+ // We have the model here?
756
+ // @ts-ignore
757
+ const model = models[0];
758
+ // We have the model here?
759
+ const options = buildQuery(itemQuery, model);
760
+ // If this has a location array, we need to add a where clause
761
+ if (loc.length === 1) {
762
+ const locKeyType = loc[0].kt;
763
+ if (model.associations[locKeyType]) {
764
+ const association = model.associations[locKeyType];
765
+ options.where = {
766
+ ...options.where,
767
+ [association.foreignKey]: {
768
+ [sequelize.Op.eq]: loc[0].lk
769
+ }
770
+ };
771
+ } else {
772
+ logger$7.error('Location key type not found in sequelize model association for: %s', locKeyType);
773
+ throw new Error('Location key type not found in model');
774
+ }
775
+ }
776
+ logger$7.default('Configured this Item Query', {
777
+ itemQuery,
778
+ options
779
+ });
780
+ const matchingItems = await model.findAll(options);
781
+ // this.logger.default('Matching Items', { matchingItems });
782
+ // TODO: Move this Up!
783
+ return await Promise.all(matchingItems.map(async (row)=>{
784
+ const processedRow = await processRow(row, coordinate.kta, references, aggregations, registry);
785
+ return core.validateKeys(processedRow, coordinate.kta);
786
+ }));
787
+ };
788
+ return all;
789
+ };
790
+
791
+ const logger$6 = logger$1.default.get('sequelize', 'ops', 'create');
792
+ // Helper function to validate hierarchical chain exists
793
+ async function validateHierarchicalChain(models, locKey, kta) {
794
+ // Find the direct parent model that contains this locator
795
+ const locatorIndex = kta.indexOf(locKey.kt);
796
+ if (locatorIndex === -1) {
797
+ throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
798
+ }
799
+ // Get the model for this locator
800
+ const locatorModel = models[locatorIndex] || models[0]; // Fallback to primary model
801
+ // Build a query to validate the chain exists
802
+ const chainResult = buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
803
+ if (!chainResult.success) {
804
+ // If we can't build a chain, just validate the record exists
805
+ const record = await locatorModel.findByPk(locKey.lk);
806
+ if (!record) {
807
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist`);
808
+ }
809
+ return;
810
+ }
811
+ // Validate that the chain exists
812
+ const queryOptions = {
813
+ where: {
814
+ id: locKey.lk
815
+ }
816
+ };
817
+ if (chainResult.includes && chainResult.includes.length > 0) {
818
+ queryOptions.include = chainResult.includes;
819
+ }
820
+ const record = await locatorModel.findOne(queryOptions);
821
+ if (!record) {
822
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist or chain is invalid`);
823
+ }
824
+ }
825
+ const getCreateOperation = (models, definition, registry)=>{
826
+ const create = async (item, options)=>{
827
+ logger$6.default('Create', {
828
+ item,
829
+ options
830
+ });
831
+ const { coordinate, options: { references, aggregations } } = definition;
832
+ const { kta } = coordinate;
833
+ // Get the primary model (first model in array)
834
+ const model = models[0];
835
+ const modelAttributes = model.getAttributes();
836
+ // Validate that all item attributes exist on the model
837
+ let itemData = {
838
+ ...item
839
+ };
840
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
841
+ itemData = extractEvents(itemData);
842
+ itemData = removeEvents(itemData);
843
+ for (const key of Object.keys(itemData)){
844
+ if (!modelAttributes[key]) {
845
+ throw new Error(`Attribute '${key}' does not exist on model ${model.name}`);
846
+ }
847
+ }
848
+ // Handle key options
849
+ // If a key is supplied, assume its contents are to be assigned to the appropriate ids.
850
+ // For most cases this will be null as key generation is often through autoIncrement.
851
+ // If this is a CItem then the locations will be present.
852
+ if (options === null || options === void 0 ? void 0 : options.key) {
853
+ const key = options.key;
854
+ if (core.isPriKey(key)) {
855
+ // Set the primary key
856
+ itemData.id = key.pk;
857
+ } else if (core.isComKey(key)) {
858
+ // Set primary key
859
+ itemData.id = key.pk;
860
+ // Process location keys - only set direct foreign keys, validate hierarchical chains
861
+ const comKey = key;
862
+ const directLocations = [];
863
+ const hierarchicalLocations = [];
864
+ // Categorize location keys as direct or hierarchical
865
+ for (const locKey of comKey.loc){
866
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locKey.kt, kta, true);
867
+ if (!relationshipInfo.found) {
868
+ const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
869
+ logger$6.error(errorMessage, {
870
+ key: comKey,
871
+ kta
872
+ });
873
+ throw new Error(errorMessage);
874
+ }
875
+ if (relationshipInfo.isDirect) {
876
+ directLocations.push(locKey);
877
+ } else {
878
+ hierarchicalLocations.push(locKey);
879
+ }
880
+ }
881
+ // Set direct foreign keys
882
+ for (const locKey of directLocations){
883
+ const foreignKeyField = locKey.kt + 'Id';
884
+ itemData[foreignKeyField] = locKey.lk;
885
+ }
886
+ // Validate hierarchical chains exist
887
+ for (const locKey of hierarchicalLocations){
888
+ await validateHierarchicalChain(models, locKey, kta);
889
+ }
890
+ }
891
+ }
892
+ // Handle locations options
893
+ // This is the most frequent way relationship ids will be set
894
+ if (options === null || options === void 0 ? void 0 : options.locations) {
895
+ const directLocations = [];
896
+ const hierarchicalLocations = [];
897
+ // Categorize location keys as direct or hierarchical
898
+ for (const locKey of options.locations){
899
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locKey.kt, kta, true);
900
+ if (!relationshipInfo.found) {
901
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
902
+ logger$6.error(errorMessage, {
903
+ locations: options.locations,
904
+ kta
905
+ });
906
+ throw new Error(errorMessage);
907
+ }
908
+ if (relationshipInfo.isDirect) {
909
+ directLocations.push(locKey);
910
+ } else {
911
+ hierarchicalLocations.push(locKey);
912
+ }
913
+ }
914
+ // Set direct foreign keys
915
+ for (const locKey of directLocations){
916
+ const foreignKeyField = locKey.kt + 'Id';
917
+ itemData[foreignKeyField] = locKey.lk;
918
+ }
919
+ // Validate hierarchical chains exist
920
+ for (const locKey of hierarchicalLocations){
921
+ await validateHierarchicalChain(models, locKey, kta);
922
+ }
923
+ }
924
+ // Create the record
925
+ const createdRecord = await model.create(itemData);
926
+ // Add key and events
927
+ const processedRecord = await processRow(createdRecord, kta, references, aggregations, registry);
928
+ return core.validateKeys(processedRecord, kta);
929
+ };
930
+ return create;
931
+ };
932
+
933
+ const logger$5 = logger$1.default.get('sequelize', 'ops', 'find');
934
+ const getFindOperation = (models, definition, registry)=>{
935
+ const { options: { finders, references, aggregations } } = definition;
936
+ const find = async (finder, finderParams, locations)=>{
937
+ logger$5.default('Find', {
938
+ finder,
939
+ finderParams,
940
+ locations
941
+ });
942
+ // Note that we execute the createFinders function here because we want to make sure we're always getting the
943
+ // most up to date methods.
944
+ if (finders && finders[finder]) {
945
+ const finderMethod = finders[finder];
946
+ if (finderMethod) {
947
+ const results = await finderMethod(finderParams, locations);
948
+ if (results && results.length > 0) {
949
+ return await Promise.all(results.map(async (row)=>{
950
+ const processedRow = await processRow(row, definition.coordinate.kta, references, aggregations, registry);
951
+ return core.validateKeys(processedRow, definition.coordinate.kta);
952
+ }));
953
+ } else {
954
+ return [];
955
+ }
956
+ } else {
957
+ logger$5.error(`Finder %s not found`, finder);
958
+ throw new Error(`Finder ${finder} not found`);
959
+ }
960
+ } else {
961
+ logger$5.error(`No finders have been defined for this lib`);
962
+ throw new Error(`No finders found`);
963
+ }
964
+ };
965
+ return find;
966
+ };
967
+
968
+ const logger$4 = logger$1.default.get('sequelize', 'ops', 'get');
969
+ // Helper function to process composite key and build query options
970
+ const processCompositeKey$1 = (comKey, model, kta)=>{
971
+ const where = {
972
+ id: comKey.pk
973
+ };
974
+ const includes = [];
975
+ for (const locator of comKey.loc){
976
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locator.kt, kta);
977
+ if (!relationshipInfo.found) {
978
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
979
+ logger$4.error(errorMessage, {
980
+ key: comKey,
981
+ kta
982
+ });
983
+ throw new Error(errorMessage);
984
+ }
985
+ if (relationshipInfo.path) {
986
+ // This requires a relationship traversal
987
+ where[relationshipInfo.path] = locator.lk;
988
+ if (relationshipInfo.includes) {
989
+ includes.push(...relationshipInfo.includes);
990
+ }
991
+ } else {
992
+ // This is a direct field
993
+ const fieldName = `${locator.kt}Id`;
994
+ where[fieldName] = locator.lk;
995
+ }
996
+ }
997
+ const result = {
998
+ where
999
+ };
1000
+ if (includes.length > 0) {
1001
+ result.include = includes;
1002
+ }
1003
+ return result;
1004
+ };
1005
+ const getGetOperation = (models, definition, registry)=>{
1006
+ const { coordinate, options: { references, aggregations } } = definition;
1007
+ const { kta } = coordinate;
1008
+ const get = async (key)=>{
1009
+ logger$4.default('Get', {
1010
+ key
1011
+ });
1012
+ if (!core.isValidItemKey(key)) {
1013
+ logger$4.error('Key for Get is not a valid ItemKey: %j', key);
1014
+ throw new Error('Key for Get is not a valid ItemKey');
1015
+ }
1016
+ const itemKey = key;
1017
+ // @ts-ignore
1018
+ const model = models[0];
1019
+ let item;
1020
+ if (core.isPriKey(itemKey)) {
1021
+ // This is the easy case because we can just find the item by its primary key
1022
+ item = await model.findByPk(itemKey.pk);
1023
+ } else if (core.isComKey(itemKey)) {
1024
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
1025
+ const comKey = itemKey;
1026
+ const queryOptions = processCompositeKey$1(comKey, model, kta);
1027
+ logger$4.default('Composite key query', {
1028
+ queryOptions
1029
+ });
1030
+ item = await model.findOne(queryOptions);
1031
+ }
1032
+ if (!item) {
1033
+ throw new Library.NotFoundError('get', coordinate, key);
1034
+ } else {
1035
+ return core.validateKeys(await processRow(item, kta, references, aggregations, registry), kta);
1036
+ }
1037
+ };
1038
+ return get;
1039
+ };
1040
+
1041
+ const logger$3 = logger$1.default.get('sequelize', 'ops', 'one');
1042
+ const getOneOperation = (models, definition, registry)=>{
1043
+ const one = async (itemQuery, locations = [])=>{
1044
+ logger$3.default('One', {
1045
+ itemQuery,
1046
+ locations
1047
+ });
1048
+ const items = await all.getAllOperation(models, definition, registry)(itemQuery, locations);
1049
+ if (items.length > 0) {
1050
+ return items[0];
1051
+ } else {
1052
+ return null;
1053
+ }
1054
+ };
1055
+ return one;
1056
+ };
1057
+
1058
+ const logger$2 = logger$1.default.get('sequelize', 'ops', 'remove');
1059
+ // Helper function to process composite key and build query options
1060
+ const processCompositeKey = (comKey, model, kta)=>{
1061
+ const where = {
1062
+ id: comKey.pk
1063
+ };
1064
+ const includes = [];
1065
+ for (const locator of comKey.loc){
1066
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locator.kt, kta);
1067
+ if (!relationshipInfo.found) {
1068
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
1069
+ logger$2.error(errorMessage, {
1070
+ key: comKey,
1071
+ kta
1072
+ });
1073
+ throw new Error(errorMessage);
1074
+ }
1075
+ if (relationshipInfo.path) {
1076
+ // This requires a relationship traversal
1077
+ where[relationshipInfo.path] = locator.lk;
1078
+ if (relationshipInfo.includes) {
1079
+ includes.push(...relationshipInfo.includes);
1080
+ }
1081
+ } else {
1082
+ // This is a direct field
1083
+ const fieldName = `${locator.kt}Id`;
1084
+ where[fieldName] = locator.lk;
1085
+ }
1086
+ }
1087
+ const result = {
1088
+ where
1089
+ };
1090
+ if (includes.length > 0) {
1091
+ result.include = includes;
1092
+ }
1093
+ return result;
1094
+ };
1095
+ const getRemoveOperation = (models, definition, // eslint-disable-next-line @typescript-eslint/no-unused-vars
1096
+ registry)=>{
1097
+ const { coordinate, options } = definition;
1098
+ const { kta } = coordinate;
1099
+ const remove = async (key)=>{
1100
+ logger$2.default('Remove', {
1101
+ key
1102
+ });
1103
+ if (!core.isValidItemKey(key)) {
1104
+ logger$2.error('Key for Remove is not a valid ItemKey: %j', key);
1105
+ throw new Error('Key for Remove is not a valid ItemKey');
1106
+ }
1107
+ // @ts-ignore
1108
+ const model = models[0];
1109
+ let item;
1110
+ let returnItem;
1111
+ logger$2.debug('remove: %s', core.abbrevIK(key));
1112
+ if (core.isPriKey(key)) {
1113
+ item = await model.findByPk(key.pk);
1114
+ } else if (core.isComKey(key)) {
1115
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
1116
+ const comKey = key;
1117
+ const queryOptions = processCompositeKey(comKey, model, kta);
1118
+ logger$2.default('Composite key query', {
1119
+ queryOptions
1120
+ });
1121
+ item = await model.findOne(queryOptions);
1122
+ }
1123
+ if (!item) {
1124
+ throw new Error(`Item not found for removal with key: ${core.abbrevIK(key)}`);
1125
+ }
1126
+ const isDeletedAttribute = model.getAttributes().isDeleted;
1127
+ const deletedAtAttribute = model.getAttributes().deletedAt;
1128
+ if (isDeletedAttribute || deletedAtAttribute) {
1129
+ if (model.getAttributes().isDeleted) {
1130
+ item.isDeleted = true;
1131
+ }
1132
+ if (model.getAttributes().deletedAt) {
1133
+ item.deletedAt = new Date();
1134
+ }
1135
+ // Save the object
1136
+ await (item === null || item === void 0 ? void 0 : item.save());
1137
+ returnItem = item === null || item === void 0 ? void 0 : item.get({
1138
+ plain: true
1139
+ });
1140
+ returnItem = addKey(item, returnItem, kta);
1141
+ returnItem = populateEvents(returnItem);
1142
+ } else if (options.deleteOnRemove) {
1143
+ await (item === null || item === void 0 ? void 0 : item.destroy());
1144
+ returnItem = item === null || item === void 0 ? void 0 : item.get({
1145
+ plain: true
1146
+ });
1147
+ returnItem = addKey(item, returnItem, kta);
1148
+ returnItem = populateEvents(returnItem);
1149
+ } else {
1150
+ throw new Error('No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set');
1151
+ }
1152
+ return returnItem;
1153
+ };
1154
+ return remove;
1155
+ };
1156
+
1157
+ const logger$1 = logger$1.default.get('sequelize', 'ops', 'update');
1158
+ const getUpdateOperation = (models, definition, registry)=>{
1159
+ const { options: { references, aggregations } } = definition;
1160
+ const update = async (key, item)=>{
1161
+ const { coordinate } = definition;
1162
+ const { kta } = coordinate;
1163
+ logger$1.debug('update: %s, %j', core.abbrevIK(key), item);
1164
+ // Find the object we're updating
1165
+ // @ts-ignore
1166
+ const model = models[0];
1167
+ let response;
1168
+ if (core.isPriKey(key)) {
1169
+ // Find the model by using the PK
1170
+ const priKey = key;
1171
+ response = await model.findByPk(priKey.pk);
1172
+ } else if (core.isComKey(key)) {
1173
+ var _comKey_loc_, _comKey_loc_1;
1174
+ const comKey = key;
1175
+ // Find the model by using both of the identifiers.
1176
+ response = await model.findOne({
1177
+ where: {
1178
+ [(comKey === null || comKey === void 0 ? void 0 : (_comKey_loc_ = comKey.loc[0]) === null || _comKey_loc_ === void 0 ? void 0 : _comKey_loc_.kt) + 'Id']: comKey === null || comKey === void 0 ? void 0 : (_comKey_loc_1 = comKey.loc[0]) === null || _comKey_loc_1 === void 0 ? void 0 : _comKey_loc_1.lk,
1179
+ id: comKey === null || comKey === void 0 ? void 0 : comKey.pk
1180
+ }
1181
+ });
1182
+ }
1183
+ if (response) {
1184
+ // Remove the key and events
1185
+ let updateProps = removeKey(item);
1186
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
1187
+ updateProps = extractEvents(updateProps);
1188
+ updateProps = removeEvents(updateProps);
1189
+ logger$1.default('Response: %s', general.stringifyJSON(response));
1190
+ logger$1.default('Update Properties: %s', general.stringifyJSON(updateProps));
1191
+ // Update the object
1192
+ response = await response.update(updateProps);
1193
+ // Populate the key and events
1194
+ const processedItem = await processRow(response, kta, references, aggregations, registry);
1195
+ const returnItem = core.validateKeys(processedItem, kta);
1196
+ return returnItem;
1197
+ } else {
1198
+ throw new Library.NotFoundError('update', coordinate, key);
1199
+ }
1200
+ };
1201
+ return update;
1202
+ };
1203
+
1204
+ const createOperations = (models, definition, registry)=>{
1205
+ const operations = {};
1206
+ operations.all = all.getAllOperation(models, definition, registry);
1207
+ operations.one = getOneOperation(models, definition, registry);
1208
+ operations.create = getCreateOperation(models, definition, registry);
1209
+ operations.update = getUpdateOperation(models, definition, registry);
1210
+ operations.get = getGetOperation(models, definition, registry);
1211
+ operations.remove = getRemoveOperation(models, definition);
1212
+ operations.find = getFindOperation(models, definition, registry);
1213
+ operations.upsert = async ()=>{
1214
+ throw new Error('Not implemented');
1215
+ };
1216
+ return operations;
1217
+ };
1218
+
1219
+ function createInstance$2(keyTypes, models, libOptions = {}, scopes = [], registry) {
1220
+ const definition = createDefinition(keyTypes, scopes, libOptions);
1221
+ const operations = createOperations(models, definition, registry);
1222
+ return {
1223
+ definition,
1224
+ operations: Library__namespace.wrapOperations(operations, definition, registry),
1225
+ models,
1226
+ registry
1227
+ };
1228
+ }
1229
+
1230
+ function createInstance$1(keyTypes, models, libOptions = {}, scopes = [], registry) {
1231
+ const definition = createDefinition(keyTypes, scopes, libOptions);
1232
+ const operations = createOperations(models, definition, registry);
1233
+ return {
1234
+ definition,
1235
+ operations: Library.Contained.wrapOperations(operations, definition, registry),
1236
+ models,
1237
+ registry
1238
+ };
1239
+ }
1240
+
1241
+ const index$1 = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
1242
+ __proto__: null,
1243
+ createInstance: createInstance$1
1244
+ }, Symbol.toStringTag, { value: 'Module' }));
1245
+
1246
+ const logger = logger$1.default.get('lib-sequelize', 'primary', 'instance');
1247
+ function createInstance(keyType, models, libOptions = {}, scopes = [], registry) {
1248
+ logger.debug('createInstance', {
1249
+ keyType,
1250
+ models,
1251
+ libOptions,
1252
+ scopes
1253
+ });
1254
+ const definition = createDefinition([
1255
+ keyType
1256
+ ], scopes, libOptions);
1257
+ const operations = createOperations(models, definition, registry);
1258
+ return {
1259
+ definition,
1260
+ operations: Library.Primary.wrapOperations(operations, definition, registry),
1261
+ models,
1262
+ registry
1263
+ };
1264
+ }
1265
+
1266
+ const index = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({
1267
+ __proto__: null,
1268
+ createInstance
1269
+ }, Symbol.toStringTag, { value: 'Module' }));
1270
+
1271
+ exports.Contained = index$1;
1272
+ exports.Primary = index;
1273
+ exports.SCOPE_SEQUELIZE = SCOPE_SEQUELIZE;
1274
+ exports.createCoordinate = createCoordinate;
1275
+ exports.createDefinition = createDefinition;
1276
+ exports.createInstance = createInstance$2;
1277
+ exports.createOperations = createOperations;
1278
+ exports.createOptions = createOptions;
1279
+ //# sourceMappingURL=index.cjs.map