@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
@@ -0,0 +1,124 @@
1
+ import LibLogger from './logger.js';
2
+ import { buildRelationshipPath } from './util/relationshipUtils.js';
3
+
4
+ const logger = LibLogger.get('sequelize', 'KeyMaster');
5
+ // Helper function to extract location key value from item
6
+ const extractLocationKeyValue = (model, item, locatorType, kta)=>{
7
+ logger.default('Extracting location key value', {
8
+ locatorType,
9
+ kta
10
+ });
11
+ const relationshipInfo = buildRelationshipPath(model, locatorType, kta, true);
12
+ if (!relationshipInfo.found) {
13
+ throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships.`);
14
+ }
15
+ if (relationshipInfo.isDirect) {
16
+ // Direct foreign key field
17
+ const foreignKeyField = `${locatorType}Id`;
18
+ const value = item[foreignKeyField];
19
+ if (typeof value === 'undefined' || value === null) {
20
+ throw new Error(`Direct foreign key field '${foreignKeyField}' is missing or null in item`);
21
+ }
22
+ return value;
23
+ } else {
24
+ // Need to traverse relationship
25
+ // Try to get the value from the loaded relationship object
26
+ const relationshipObject = item[locatorType];
27
+ if (relationshipObject && typeof relationshipObject.id !== 'undefined') {
28
+ return relationshipObject.id;
29
+ }
30
+ // If the relationship object isn't loaded, we might need to look at the foreign key field
31
+ // This handles cases where we have the foreign key but not the full object
32
+ const foreignKeyField = `${locatorType}Id`;
33
+ if (typeof item[foreignKeyField] !== 'undefined' && item[foreignKeyField] !== null) {
34
+ return item[foreignKeyField];
35
+ }
36
+ throw new Error(`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available.`);
37
+ }
38
+ };
39
+ const removeKey = (item)=>{
40
+ logger.default('Removing Key', {
41
+ item
42
+ });
43
+ delete item.key;
44
+ return item;
45
+ };
46
+ // export const populateKey = <
47
+ // S extends string,
48
+ // L1 extends string = never,
49
+ // L2 extends string = never,
50
+ // L3 extends string = never,
51
+ // L4 extends string = never,
52
+ // L5 extends string = never
53
+ // >(
54
+ // item: ItemProperties<S, L1, L2, L3, L4, L5>,
55
+ // keyTypes: AllItemTypeArrays<S, L1, L2, L3, L4, L5>
56
+ // ): ItemProperties<S, L1, L2, L3, L4, L5> => {
57
+ // if (keyTypes.length === 1) {
58
+ // item.key = { kt: keyTypes[0], pk: item.id };
59
+ // delete item.id;
60
+ // } else if (keyTypes.length === 2) {
61
+ // item.key = {
62
+ // kt: keyTypes[0], pk: item.id,
63
+ // // TODO: Shouldn't this be inspecting the model to get the primary key type?
64
+ // loc: [{ kt: keyTypes[1], lk: item[keyTypes[1] + 'Id'] }],
65
+ // };
66
+ // delete item.id;
67
+ // delete item[keyTypes[1] + 'Id'];
68
+ // } else {
69
+ // throw new Error('Not implemented');
70
+ // }
71
+ // return item;
72
+ // }
73
+ const addKey = (model, item, keyTypes)=>{
74
+ logger.default('Adding Key', {
75
+ item
76
+ });
77
+ const key = {};
78
+ const modelClass = model.constructor;
79
+ const primaryKeyAttr = modelClass.primaryKeyAttribute;
80
+ if (Array.isArray(keyTypes) && keyTypes.length > 1) {
81
+ const type = [
82
+ ...keyTypes
83
+ ];
84
+ const pkType = type.shift();
85
+ Object.assign(key, {
86
+ kt: pkType,
87
+ pk: item[primaryKeyAttr]
88
+ });
89
+ // Build location keys for composite key
90
+ const locationKeys = [];
91
+ for (const locatorType of type){
92
+ try {
93
+ const lk = extractLocationKeyValue(modelClass, item, locatorType, keyTypes);
94
+ locationKeys.push({
95
+ kt: locatorType,
96
+ lk
97
+ });
98
+ } catch (error) {
99
+ const errorMessage = error instanceof Error ? error.message : String(error);
100
+ logger.error(`Failed to extract location key for '${locatorType}'`, {
101
+ error: errorMessage,
102
+ item,
103
+ keyTypes
104
+ });
105
+ throw error;
106
+ }
107
+ }
108
+ Object.assign(key, {
109
+ loc: locationKeys
110
+ });
111
+ } else {
112
+ Object.assign(key, {
113
+ kt: keyTypes[0],
114
+ pk: item[primaryKeyAttr]
115
+ });
116
+ }
117
+ Object.assign(item, {
118
+ key
119
+ });
120
+ return item;
121
+ };
122
+
123
+ export { addKey, removeKey };
124
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -6,16 +6,16 @@ import { getOneOperation } from './ops/one.js';
6
6
  import { getRemoveOperation } from './ops/remove.js';
7
7
  import { getUpdateOperation } from './ops/update.js';
8
8
 
9
- const createOperations = (models, definition)=>{
9
+ const createOperations = (models, definition, registry)=>{
10
10
  const operations = {};
11
- operations.all = getAllOperation(models, definition);
12
- operations.one = getOneOperation(models, definition);
13
- operations.create = getCreateOperation();
14
- operations.update = getUpdateOperation(models, definition);
15
- operations.get = getGetOperation(models, definition);
11
+ operations.all = getAllOperation(models, definition, registry);
12
+ operations.one = getOneOperation(models, definition, registry);
13
+ operations.create = getCreateOperation(models, definition, registry);
14
+ operations.update = getUpdateOperation(models, definition, registry);
15
+ operations.get = getGetOperation(models, definition, registry);
16
16
  operations.remove = getRemoveOperation(models, definition);
17
- operations.find = getFindOperation(models, definition);
18
- operations.upsert = ()=>{
17
+ operations.find = getFindOperation(models, definition, registry);
18
+ operations.upsert = async ()=>{
19
19
  throw new Error('Not implemented');
20
20
  };
21
21
  return operations;
@@ -1,19 +1,16 @@
1
1
  import * as Library from '@fjell/lib';
2
2
  import deepmerge from 'deepmerge';
3
+ import { clean } from './util/general.js';
3
4
 
4
- const defaultOptions = {
5
- deleteOnRemove: false
5
+ const DEFAULT_OPTIONS = {
6
+ deleteOnRemove: false,
7
+ references: [],
8
+ aggregations: []
6
9
  };
7
10
  const createOptions = (libOptions)=>{
8
- const defaultOptions = {
9
- deleteOnRemove: false
10
- };
11
11
  const options = Library.createOptions(libOptions);
12
- const mergedOptions = deepmerge(defaultOptions, options);
13
- return {
14
- ...mergedOptions
15
- };
12
+ return deepmerge(DEFAULT_OPTIONS, clean(options));
16
13
  };
17
14
 
18
- export { createOptions, defaultOptions };
19
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3B0aW9ucy5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
15
+ export { createOptions };
16
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3B0aW9ucy5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -80,7 +80,8 @@ const addReferenceQueries = (options, references, model)=>{
80
80
  return options;
81
81
  };
82
82
  const addCompoundCondition = (options, compoundCondition, model)=>{
83
- const where = {};
83
+ // Ensure options.where exists
84
+ options.where = options.where || {};
84
85
  let compoundOp;
85
86
  const compoundType = compoundCondition.compoundType;
86
87
  if (compoundType === "AND") {
@@ -96,36 +97,135 @@ const addCompoundCondition = (options, compoundCondition, model)=>{
96
97
  throw new Error('Nest Compound conditions not supported');
97
98
  }
98
99
  });
99
- where[compoundOp] = conditions;
100
- options.where = where;
100
+ // Merge with existing where conditions instead of replacing
101
+ if (Object.keys(options.where).length > 0) {
102
+ // If there are existing conditions, wrap everything in an AND
103
+ options.where = {
104
+ [Op.and]: [
105
+ options.where,
106
+ {
107
+ [compoundOp]: conditions
108
+ }
109
+ ]
110
+ };
111
+ } else {
112
+ // If no existing conditions, just set the compound condition
113
+ options.where[compoundOp] = conditions;
114
+ }
101
115
  return options;
102
116
  };
103
- const addCondition = (conditions, condition, model)=>{
104
- let conditionOp;
117
+ const getSequelizeOperator = (operator)=>{
118
+ if (operator === '==') {
119
+ return Op.eq;
120
+ } else if (operator === '<') {
121
+ return Op.lt;
122
+ } else if (operator === '>') {
123
+ return Op.gt;
124
+ } else if (operator === '<=') {
125
+ return Op.lte;
126
+ } else if (operator === '>=') {
127
+ return Op.gte;
128
+ } else if (operator === 'in') {
129
+ return Op.in;
130
+ } else {
131
+ throw new Error(`Operator ${operator} not supported`);
132
+ }
133
+ };
134
+ const addAssociationCondition = (conditions, condition, model)=>{
135
+ const [associationName, attributeName] = condition.column.split('.', 2);
136
+ // Check if the association exists on the model
137
+ if (!model.associations || !model.associations[associationName]) {
138
+ throw new Error(`Association ${associationName} not found on model ${model.name}`);
139
+ }
140
+ const association = model.associations[associationName];
141
+ const associatedModel = association.target;
142
+ // Check if the attribute exists on the associated model
143
+ if (!associatedModel.getAttributes()[attributeName]) {
144
+ throw new Error(`Attribute ${attributeName} not found on associated model ${associatedModel.name} for association ${associationName}`);
145
+ }
146
+ // Use Sequelize's $association.attribute$ syntax for querying associated models
147
+ const sequelizeAssociationColumn = `$${associationName}.${attributeName}$`;
148
+ const conditionOp = getSequelizeOperator(condition.operator);
149
+ conditions[sequelizeAssociationColumn] = {
150
+ [conditionOp]: condition.value
151
+ };
152
+ return conditions;
153
+ };
154
+ const addAttributeCondition = (conditions, condition, model)=>{
105
155
  const conditionColumn = condition.column;
106
156
  if (!model.getAttributes()[conditionColumn]) {
107
157
  throw new Error(`Condition column ${conditionColumn} not found on model ${model.name}`);
108
158
  }
109
- if (condition.operator === '==') {
110
- conditionOp = Op.eq;
111
- } else if (condition.operator === '<') {
112
- conditionOp = Op.lt;
113
- } else if (condition.operator === '>') {
114
- conditionOp = Op.gt;
115
- } else if (condition.operator === '<=') {
116
- conditionOp = Op.lte;
117
- } else if (condition.operator === '>=') {
118
- conditionOp = Op.gte;
119
- } else if (condition.operator === 'in') {
120
- conditionOp = Op.in;
121
- } else {
122
- throw new Error(`Operator ${condition.operator} not supported`);
123
- }
159
+ const conditionOp = getSequelizeOperator(condition.operator);
124
160
  conditions[conditionColumn] = {
125
161
  [conditionOp]: condition.value
126
162
  };
127
163
  return conditions;
128
164
  };
165
+ const addCondition = (conditions, condition, model)=>{
166
+ const conditionColumn = condition.column;
167
+ // Check if this is an association query (contains a dot)
168
+ if (conditionColumn.includes('.')) {
169
+ return addAssociationCondition(conditions, condition, model);
170
+ }
171
+ // Handle regular column queries
172
+ return addAttributeCondition(conditions, condition, model);
173
+ };
174
+ const collectAssociationsFromConditions = (conditions)=>{
175
+ const associations = new Set();
176
+ const processObject = (obj)=>{
177
+ if (typeof obj === 'object' && obj !== null) {
178
+ // Check string keys
179
+ Object.keys(obj).forEach((key)=>{
180
+ // Check if this is an association reference ($association.attribute$)
181
+ if (typeof key === 'string' && key.startsWith('$') && key.endsWith('$') && key.includes('.')) {
182
+ const associationName = key.substring(1, key.indexOf('.'));
183
+ associations.add(associationName);
184
+ }
185
+ // Recursively process nested objects
186
+ if (typeof obj[key] === 'object') {
187
+ processObject(obj[key]);
188
+ }
189
+ });
190
+ // Also check Symbol keys (for compound conditions like Op.and, Op.or)
191
+ Object.getOwnPropertySymbols(obj).forEach((symbol)=>{
192
+ if (typeof obj[symbol] === 'object') {
193
+ processObject(obj[symbol]);
194
+ }
195
+ });
196
+ }
197
+ // Handle arrays (for compound conditions that might be arrays)
198
+ if (Array.isArray(obj)) {
199
+ obj.forEach((item)=>{
200
+ if (typeof item === 'object') {
201
+ processObject(item);
202
+ }
203
+ });
204
+ }
205
+ };
206
+ processObject(conditions);
207
+ return associations;
208
+ };
209
+ const addAssociationIncludes = (options, model)=>{
210
+ // Collect all association names used in conditions
211
+ const referencedAssociations = collectAssociationsFromConditions(options.where);
212
+ if (referencedAssociations.size > 0) {
213
+ options.include = options.include || [];
214
+ // Add each referenced association to the include array
215
+ referencedAssociations.forEach((associationName)=>{
216
+ // Check if this association is already included
217
+ const alreadyIncluded = options.include.some((inc)=>typeof inc === 'string' && inc === associationName || typeof inc === 'object' && inc.association === associationName);
218
+ if (!alreadyIncluded && model.associations && model.associations[associationName]) {
219
+ options.include.push({
220
+ model: model.associations[associationName].target,
221
+ as: associationName,
222
+ required: false // Use LEFT JOIN so records without associations are still returned
223
+ });
224
+ }
225
+ });
226
+ }
227
+ return options;
228
+ };
129
229
  const buildQuery = (itemQuery, model)=>{
130
230
  logger.default('build', {
131
231
  itemQuery
@@ -175,8 +275,10 @@ const buildQuery = (itemQuery, model)=>{
175
275
  ];
176
276
  });
177
277
  }
278
+ // Add includes for any associations referenced in conditions
279
+ options = addAssociationIncludes(options, model);
178
280
  return options;
179
281
  };
180
282
 
181
283
  export { addCompoundCondition, addCondition, buildQuery };
182
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUXVlcnlCdWlsZGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
284
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUXVlcnlCdWlsZGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -0,0 +1,30 @@
1
+ const buildReference = async (item, referenceDefinition, registry)=>{
2
+ // Check if there is more than one key type
3
+ if (referenceDefinition.kta.length > 1) {
4
+ throw new Error("The ReferenceBuilder doesn't work with more than one key type yet");
5
+ }
6
+ // Check if dependencies exist
7
+ if (!registry) {
8
+ throw new Error("This model definition has a reference definition, but the registry is not present");
9
+ }
10
+ // Find the Library.Instance for the key type
11
+ const library = registry.get(referenceDefinition.kta);
12
+ if (!library) {
13
+ throw new Error("This model definition has a reference definition, but the dependency is not present");
14
+ }
15
+ // Create a PriKey using the column value from item
16
+ const priKey = {
17
+ kt: referenceDefinition.kta[0],
18
+ pk: item[referenceDefinition.column]
19
+ };
20
+ // Get the referenced item using the Library.Operations get method
21
+ const referencedItem = await library.operations.get(priKey);
22
+ // TODO: In a Fjell-compliant implementation, this value should be stored in the ref property
23
+ // For now, we'll just populate the property directly
24
+ // Store the result in the property on item
25
+ item[referenceDefinition.property] = referencedItem;
26
+ return item;
27
+ };
28
+
29
+ export { buildReference };
30
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVmZXJlbmNlQnVpbGRlci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -0,0 +1,37 @@
1
+ import LibLogger from './logger.js';
2
+ import { addKey } from './KeyMaster.js';
3
+ import { buildReference } from './ReferenceBuilder.js';
4
+ import { buildAggregation } from './AggregationBuilder.js';
5
+ import { stringifyJSON } from './util/general.js';
6
+ import { populateEvents } from './EventCoordinator.js';
7
+
8
+ const logger = LibLogger.get('sequelize', 'RowProcessor');
9
+ const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry)=>{
10
+ logger.default('Processing Row', {
11
+ row
12
+ });
13
+ let item = row.get({
14
+ plain: true
15
+ });
16
+ logger.default('Adding Key to Item with Key Types: %s', stringifyJSON(keyTypes));
17
+ item = addKey(row, item, keyTypes);
18
+ item = populateEvents(item);
19
+ logger.default('Key Added to Item: %s', stringifyJSON(item.key));
20
+ if (referenceDefinitions && referenceDefinitions.length > 0) {
21
+ for (const referenceDefinition of referenceDefinitions){
22
+ logger.default('Processing Reference for %s to %s', item.key.kt, stringifyJSON(referenceDefinition.kta));
23
+ item = await buildReference(item, referenceDefinition, registry);
24
+ }
25
+ }
26
+ if (aggregationDefinitions && aggregationDefinitions.length > 0) {
27
+ for (const aggregationDefinition of aggregationDefinitions){
28
+ logger.default('Processing Aggregation for %s from %s', item.key.kt, stringifyJSON(aggregationDefinition.kta));
29
+ item = await buildAggregation(item, aggregationDefinition, registry);
30
+ }
31
+ }
32
+ logger.default('Processed Row: %j', stringifyJSON(item));
33
+ return item;
34
+ };
35
+
36
+ export { processRow };
37
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm93UHJvY2Vzc29yLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -2,15 +2,16 @@ import { Contained } from '@fjell/lib';
2
2
  import { createDefinition } from '../Definition.js';
3
3
  import { createOperations } from '../Operations.js';
4
4
 
5
- function createInstance(keyTypes, models, libOptions = {}, scopes = []) {
5
+ function createInstance(keyTypes, models, libOptions = {}, scopes = [], registry) {
6
6
  const definition = createDefinition(keyTypes, scopes, libOptions);
7
- const operations = createOperations(models, definition);
7
+ const operations = createOperations(models, definition, registry);
8
8
  return {
9
9
  definition,
10
- operations: Contained.wrapOperations(operations, definition),
11
- models
10
+ operations: Contained.wrapOperations(operations, definition, registry),
11
+ models,
12
+ registry
12
13
  };
13
14
  }
14
15
 
15
16
  export { createInstance };
16
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5zdGFuY2UuanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7OyJ9
17
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiSW5zdGFuY2UuanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -1,7 +1,7 @@
1
1
  export { SCOPE_SEQUELIZE, createCoordinate } from './Coordinate.js';
2
2
  export { createDefinition } from './Definition.js';
3
3
  export { createInstance } from './Instance.js';
4
- export { createOptions, defaultOptions } from './Options.js';
4
+ export { createOptions } from './Options.js';
5
5
  export { createOperations } from './Operations.js';
6
6
  import * as index from './contained/index.js';
7
7
  export { index as Contained };
@@ -5,8 +5,8 @@ import { processRow } from '../RowProcessor.js';
5
5
  import { Op } from 'sequelize';
6
6
 
7
7
  const logger = LibLogger.get('sequelize', 'ops', 'all');
8
- const getAllOperation = (models, definition)=>{
9
- const { coordinate } = definition;
8
+ const getAllOperation = (models, definition, registry)=>{
9
+ const { coordinate, options: { references, aggregations } } = definition;
10
10
  //#region Query
11
11
  const all = async (itemQuery, locations)=>{
12
12
  logger.default('All', {
@@ -46,10 +46,13 @@ const getAllOperation = (models, definition)=>{
46
46
  const matchingItems = await model.findAll(options);
47
47
  // this.logger.default('Matching Items', { matchingItems });
48
48
  // TODO: Move this Up!
49
- return matchingItems.map((row)=>validateKeys(processRow(row, coordinate.kta), coordinate.kta));
49
+ return await Promise.all(matchingItems.map(async (row)=>{
50
+ const processedRow = await processRow(row, coordinate.kta, references, aggregations, registry);
51
+ return validateKeys(processedRow, coordinate.kta);
52
+ }));
50
53
  };
51
54
  return all;
52
55
  };
53
56
 
54
57
  export { getAllOperation };
55
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
58
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWxsLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -0,0 +1,150 @@
1
+ import { isPriKey, isComKey, validateKeys } from '@fjell/core';
2
+ import LibLogger from '../logger.js';
3
+ import { processRow } from '../RowProcessor.js';
4
+ import { extractEvents, removeEvents } from '../EventCoordinator.js';
5
+ import { buildRelationshipPath, buildRelationshipChain } from '../util/relationshipUtils.js';
6
+
7
+ const logger = LibLogger.get('sequelize', 'ops', 'create');
8
+ // Helper function to validate hierarchical chain exists
9
+ async function validateHierarchicalChain(models, locKey, kta) {
10
+ // Find the direct parent model that contains this locator
11
+ const locatorIndex = kta.indexOf(locKey.kt);
12
+ if (locatorIndex === -1) {
13
+ throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
14
+ }
15
+ // Get the model for this locator
16
+ const locatorModel = models[locatorIndex] || models[0]; // Fallback to primary model
17
+ // Build a query to validate the chain exists
18
+ const chainResult = buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
19
+ if (!chainResult.success) {
20
+ // If we can't build a chain, just validate the record exists
21
+ const record = await locatorModel.findByPk(locKey.lk);
22
+ if (!record) {
23
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist`);
24
+ }
25
+ return;
26
+ }
27
+ // Validate that the chain exists
28
+ const queryOptions = {
29
+ where: {
30
+ id: locKey.lk
31
+ }
32
+ };
33
+ if (chainResult.includes && chainResult.includes.length > 0) {
34
+ queryOptions.include = chainResult.includes;
35
+ }
36
+ const record = await locatorModel.findOne(queryOptions);
37
+ if (!record) {
38
+ throw new Error(`Referenced ${locKey.kt} with id ${locKey.lk} does not exist or chain is invalid`);
39
+ }
40
+ }
41
+ const getCreateOperation = (models, definition, registry)=>{
42
+ const create = async (item, options)=>{
43
+ logger.default('Create', {
44
+ item,
45
+ options
46
+ });
47
+ const { coordinate, options: { references, aggregations } } = definition;
48
+ const { kta } = coordinate;
49
+ // Get the primary model (first model in array)
50
+ const model = models[0];
51
+ const modelAttributes = model.getAttributes();
52
+ // Validate that all item attributes exist on the model
53
+ let itemData = {
54
+ ...item
55
+ };
56
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
57
+ itemData = extractEvents(itemData);
58
+ itemData = removeEvents(itemData);
59
+ for (const key of Object.keys(itemData)){
60
+ if (!modelAttributes[key]) {
61
+ throw new Error(`Attribute '${key}' does not exist on model ${model.name}`);
62
+ }
63
+ }
64
+ // Handle key options
65
+ // If a key is supplied, assume its contents are to be assigned to the appropriate ids.
66
+ // For most cases this will be null as key generation is often through autoIncrement.
67
+ // If this is a CItem then the locations will be present.
68
+ if (options === null || options === void 0 ? void 0 : options.key) {
69
+ const key = options.key;
70
+ if (isPriKey(key)) {
71
+ // Set the primary key
72
+ itemData.id = key.pk;
73
+ } else if (isComKey(key)) {
74
+ // Set primary key
75
+ itemData.id = key.pk;
76
+ // Process location keys - only set direct foreign keys, validate hierarchical chains
77
+ const comKey = key;
78
+ const directLocations = [];
79
+ const hierarchicalLocations = [];
80
+ // Categorize location keys as direct or hierarchical
81
+ for (const locKey of comKey.loc){
82
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
83
+ if (!relationshipInfo.found) {
84
+ const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
85
+ logger.error(errorMessage, {
86
+ key: comKey,
87
+ kta
88
+ });
89
+ throw new Error(errorMessage);
90
+ }
91
+ if (relationshipInfo.isDirect) {
92
+ directLocations.push(locKey);
93
+ } else {
94
+ hierarchicalLocations.push(locKey);
95
+ }
96
+ }
97
+ // Set direct foreign keys
98
+ for (const locKey of directLocations){
99
+ const foreignKeyField = locKey.kt + 'Id';
100
+ itemData[foreignKeyField] = locKey.lk;
101
+ }
102
+ // Validate hierarchical chains exist
103
+ for (const locKey of hierarchicalLocations){
104
+ await validateHierarchicalChain(models, locKey, kta);
105
+ }
106
+ }
107
+ }
108
+ // Handle locations options
109
+ // This is the most frequent way relationship ids will be set
110
+ if (options === null || options === void 0 ? void 0 : options.locations) {
111
+ const directLocations = [];
112
+ const hierarchicalLocations = [];
113
+ // Categorize location keys as direct or hierarchical
114
+ for (const locKey of options.locations){
115
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
116
+ if (!relationshipInfo.found) {
117
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
118
+ logger.error(errorMessage, {
119
+ locations: options.locations,
120
+ kta
121
+ });
122
+ throw new Error(errorMessage);
123
+ }
124
+ if (relationshipInfo.isDirect) {
125
+ directLocations.push(locKey);
126
+ } else {
127
+ hierarchicalLocations.push(locKey);
128
+ }
129
+ }
130
+ // Set direct foreign keys
131
+ for (const locKey of directLocations){
132
+ const foreignKeyField = locKey.kt + 'Id';
133
+ itemData[foreignKeyField] = locKey.lk;
134
+ }
135
+ // Validate hierarchical chains exist
136
+ for (const locKey of hierarchicalLocations){
137
+ await validateHierarchicalChain(models, locKey, kta);
138
+ }
139
+ }
140
+ // Create the record
141
+ const createdRecord = await model.create(itemData);
142
+ // Add key and events
143
+ const processedRecord = await processRow(createdRecord, kta, references, aggregations, registry);
144
+ return validateKeys(processedRecord, kta);
145
+ };
146
+ return create;
147
+ };
148
+
149
+ export { getCreateOperation };
150
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9