@fjell/lib-sequelize 4.4.1 → 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.
@@ -6,8 +6,45 @@ const core = require('@fjell/core');
6
6
  const EventCoordinator = require('../EventCoordinator.cjs');
7
7
  const KeyMaster = require('../KeyMaster.cjs');
8
8
  const logger$1 = require('../logger.cjs');
9
+ const relationshipUtils = require('../util/relationshipUtils.cjs');
9
10
 
10
11
  const logger = logger$1.default.get('sequelize', 'ops', 'remove');
12
+ // Helper function to process composite key and build query options
13
+ const processCompositeKey = (comKey, model, kta)=>{
14
+ const where = {
15
+ id: comKey.pk
16
+ };
17
+ const includes = [];
18
+ for (const locator of comKey.loc){
19
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locator.kt, kta);
20
+ if (!relationshipInfo.found) {
21
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
22
+ logger.error(errorMessage, {
23
+ key: comKey,
24
+ kta
25
+ });
26
+ throw new Error(errorMessage);
27
+ }
28
+ if (relationshipInfo.path) {
29
+ // This requires a relationship traversal
30
+ where[relationshipInfo.path] = locator.lk;
31
+ if (relationshipInfo.includes) {
32
+ includes.push(...relationshipInfo.includes);
33
+ }
34
+ } else {
35
+ // This is a direct field
36
+ const fieldName = `${locator.kt}Id`;
37
+ where[fieldName] = locator.lk;
38
+ }
39
+ }
40
+ const result = {
41
+ where
42
+ };
43
+ if (includes.length > 0) {
44
+ result.include = includes;
45
+ }
46
+ return result;
47
+ };
11
48
  const getRemoveOperation = (models, definition, // eslint-disable-next-line @typescript-eslint/no-unused-vars
12
49
  registry)=>{
13
50
  const { coordinate, options } = definition;
@@ -28,14 +65,16 @@ registry)=>{
28
65
  if (core.isPriKey(key)) {
29
66
  item = await model.findByPk(key.pk);
30
67
  } else if (core.isComKey(key)) {
31
- var _comKey_loc_, _comKey_loc_1;
68
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
32
69
  const comKey = key;
33
- item = await model.findOne({
34
- where: {
35
- id: comKey.pk,
36
- [(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
37
- }
70
+ const queryOptions = processCompositeKey(comKey, model, kta);
71
+ logger.default('Composite key query', {
72
+ queryOptions
38
73
  });
74
+ item = await model.findOne(queryOptions);
75
+ }
76
+ if (!item) {
77
+ throw new Error(`Item not found for removal with key: ${core.abbrevIK(key)}`);
39
78
  }
40
79
  const isDeletedAttribute = model.getAttributes().isDeleted;
41
80
  const deletedAtAttribute = model.getAttributes().deletedAt;
@@ -51,14 +90,14 @@ registry)=>{
51
90
  returnItem = item === null || item === void 0 ? void 0 : item.get({
52
91
  plain: true
53
92
  });
54
- returnItem = KeyMaster.addKey(returnItem, kta);
93
+ returnItem = KeyMaster.addKey(item, returnItem, kta);
55
94
  returnItem = EventCoordinator.populateEvents(returnItem);
56
95
  } else if (options.deleteOnRemove) {
57
96
  await (item === null || item === void 0 ? void 0 : item.destroy());
58
97
  returnItem = item === null || item === void 0 ? void 0 : item.get({
59
98
  plain: true
60
99
  });
61
- returnItem = KeyMaster.addKey(returnItem, kta);
100
+ returnItem = KeyMaster.addKey(item, returnItem, kta);
62
101
  returnItem = EventCoordinator.populateEvents(returnItem);
63
102
  } else {
64
103
  throw new Error('No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set');
@@ -69,4 +108,4 @@ registry)=>{
69
108
  };
70
109
 
71
110
  exports.getRemoveOperation = getRemoveOperation;
72
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
111
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -39,7 +39,9 @@ const getUpdateOperation = (models, definition, registry)=>{
39
39
  if (response) {
40
40
  // Remove the key and events
41
41
  let updateProps = KeyMaster.removeKey(item);
42
- updateProps = EventCoordinator.removeEvents(item);
42
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
43
+ updateProps = EventCoordinator.extractEvents(updateProps);
44
+ updateProps = EventCoordinator.removeEvents(updateProps);
43
45
  logger.default('Response: %s', general.stringifyJSON(response));
44
46
  logger.default('Update Properties: %s', general.stringifyJSON(updateProps));
45
47
  // Update the object
@@ -56,4 +58,4 @@ const getUpdateOperation = (models, definition, registry)=>{
56
58
  };
57
59
 
58
60
  exports.getUpdateOperation = getUpdateOperation;
59
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
61
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -0,0 +1,117 @@
1
+ 'use strict';
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+
5
+ /* eslint-disable indent */ /**
6
+ * Helper function to build relationship chain includes
7
+ */ const buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex)=>{
8
+ // Build the association path and validate relationships exist
9
+ const associationParts = [];
10
+ const modelChain = [
11
+ targetModel
12
+ ];
13
+ let currentModel = targetModel;
14
+ // Validate that all associations exist and build model chain
15
+ for(let i = currentIndex + 1; i <= targetIndex; i++){
16
+ const intermediateType = kta[i];
17
+ const associationName = intermediateType;
18
+ if (!currentModel.associations || !currentModel.associations[associationName]) {
19
+ return {
20
+ success: false
21
+ };
22
+ }
23
+ associationParts.push(associationName);
24
+ currentModel = currentModel.associations[associationName].target;
25
+ modelChain.push(currentModel);
26
+ }
27
+ // Build the full association path for the target field
28
+ const targetType = kta[targetIndex];
29
+ const associationPath = `$${associationParts.join('.')}.${targetType}Id$`;
30
+ // Build nested includes structure iteratively (clearer than recursion)
31
+ let deepestInclude = null;
32
+ // Build from the deepest level back to the root
33
+ for(let i = targetIndex; i > currentIndex; i--){
34
+ const currentType = kta[i];
35
+ const modelIndex = i - currentIndex;
36
+ const includeObj = {
37
+ model: modelChain[modelIndex],
38
+ as: currentType,
39
+ required: true
40
+ };
41
+ if (deepestInclude) {
42
+ includeObj.include = [
43
+ deepestInclude
44
+ ];
45
+ }
46
+ deepestInclude = includeObj;
47
+ }
48
+ const includes = deepestInclude ? [
49
+ deepestInclude
50
+ ] : [];
51
+ return {
52
+ success: true,
53
+ path: associationPath,
54
+ includes
55
+ };
56
+ };
57
+ /**
58
+ * Helper function to build relationship path for a locator
59
+ * @param includeIsDirect Whether to include the isDirect flag in the result
60
+ */ const buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = false)=>{
61
+ // First check if the field exists directly
62
+ const directFieldName = `${locatorType}Id`;
63
+ const attributes = targetModel.getAttributes();
64
+ if (attributes && attributes[directFieldName]) {
65
+ const result = {
66
+ found: true
67
+ };
68
+ if (includeIsDirect) {
69
+ result.isDirect = true;
70
+ }
71
+ return result;
72
+ }
73
+ // If not direct, look for relationship path
74
+ const targetIndex = kta.indexOf(locatorType);
75
+ if (targetIndex === -1) {
76
+ const result = {
77
+ found: false
78
+ };
79
+ if (includeIsDirect) {
80
+ result.isDirect = false;
81
+ }
82
+ return result;
83
+ }
84
+ const currentIndex = 0; // We're always looking from the base model
85
+ if (targetIndex <= currentIndex) {
86
+ const result = {
87
+ found: false
88
+ };
89
+ if (includeIsDirect) {
90
+ result.isDirect = false;
91
+ }
92
+ return result;
93
+ }
94
+ const chainResult = buildRelationshipChain(targetModel, kta, currentIndex, targetIndex);
95
+ if (chainResult.success) {
96
+ const result = {
97
+ found: true,
98
+ path: chainResult.path,
99
+ includes: chainResult.includes
100
+ };
101
+ if (includeIsDirect) {
102
+ result.isDirect = false;
103
+ }
104
+ return result;
105
+ }
106
+ const result = {
107
+ found: false
108
+ };
109
+ if (includeIsDirect) {
110
+ result.isDirect = false;
111
+ }
112
+ return result;
113
+ };
114
+
115
+ exports.buildRelationshipChain = buildRelationshipChain;
116
+ exports.buildRelationshipPath = buildRelationshipPath;
117
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVsYXRpb25zaGlwVXRpbHMuY2pzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -18,6 +18,24 @@ const populateEvents = (item)=>{
18
18
  item.events = events;
19
19
  return item;
20
20
  };
21
+ const extractEvents = (item)=>{
22
+ logger.default('Extracting Events to database fields', {
23
+ item
24
+ });
25
+ if (item.events) {
26
+ var _item_events_created, _item_events_updated, _item_events_deleted;
27
+ if ((_item_events_created = item.events.created) === null || _item_events_created === void 0 ? void 0 : _item_events_created.at) {
28
+ item.createdAt = item.events.created.at;
29
+ }
30
+ if ((_item_events_updated = item.events.updated) === null || _item_events_updated === void 0 ? void 0 : _item_events_updated.at) {
31
+ item.updatedAt = item.events.updated.at;
32
+ }
33
+ if ((_item_events_deleted = item.events.deleted) === null || _item_events_deleted === void 0 ? void 0 : _item_events_deleted.at) {
34
+ item.deletedAt = item.events.deleted.at;
35
+ }
36
+ }
37
+ return item;
38
+ };
21
39
  const removeEvents = (item)=>{
22
40
  logger.default('Removing Events', {
23
41
  item
@@ -26,5 +44,5 @@ const removeEvents = (item)=>{
26
44
  return item;
27
45
  };
28
46
 
29
- export { populateEvents, removeEvents };
30
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZlbnRDb29yZGluYXRvci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
47
+ export { extractEvents, populateEvents, removeEvents };
48
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZlbnRDb29yZGluYXRvci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -1,6 +1,41 @@
1
1
  import LibLogger from './logger.js';
2
+ import { buildRelationshipPath } from './util/relationshipUtils.js';
2
3
 
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
+ };
4
39
  const removeKey = (item)=>{
5
40
  logger.default('Removing Key', {
6
41
  item
@@ -35,11 +70,13 @@ const removeKey = (item)=>{
35
70
  // }
36
71
  // return item;
37
72
  // }
38
- const addKey = (item, keyTypes)=>{
73
+ const addKey = (model, item, keyTypes)=>{
39
74
  logger.default('Adding Key', {
40
75
  item
41
76
  });
42
77
  const key = {};
78
+ const modelClass = model.constructor;
79
+ const primaryKeyAttr = modelClass.primaryKeyAttribute;
43
80
  if (Array.isArray(keyTypes) && keyTypes.length > 1) {
44
81
  const type = [
45
82
  ...keyTypes
@@ -47,33 +84,34 @@ const addKey = (item, keyTypes)=>{
47
84
  const pkType = type.shift();
48
85
  Object.assign(key, {
49
86
  kt: pkType,
50
- pk: item.id
87
+ pk: item[primaryKeyAttr]
51
88
  });
52
- // TODO: This is really just for primary items
53
- if (type.length === 1) {
54
- // TODO: This should be looking at the model to get the primary key of the reference item or association
55
- const locKeyTypeId = type[0] + 'Id';
56
- Object.assign(key, {
57
- loc: [
58
- {
59
- kt: type[0],
60
- lk: item[locKeyTypeId]
61
- }
62
- ]
63
- });
64
- } else if (type.length === 2) {
65
- throw new Error('Not implemented');
66
- } else if (type.length === 3) {
67
- throw new Error('Not implemented');
68
- } else if (type.length === 4) {
69
- throw new Error('Not implemented');
70
- } else if (type.length === 5) {
71
- throw new Error('Not implemented');
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
+ }
72
107
  }
108
+ Object.assign(key, {
109
+ loc: locationKeys
110
+ });
73
111
  } else {
74
112
  Object.assign(key, {
75
113
  kt: keyTypes[0],
76
- pk: item.id
114
+ pk: item[primaryKeyAttr]
77
115
  });
78
116
  }
79
117
  Object.assign(item, {
@@ -83,4 +121,4 @@ const addKey = (item, keyTypes)=>{
83
121
  };
84
122
 
85
123
  export { addKey, removeKey };
86
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
124
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -10,12 +10,12 @@ const createOperations = (models, definition, registry)=>{
10
10
  const operations = {};
11
11
  operations.all = getAllOperation(models, definition, registry);
12
12
  operations.one = getOneOperation(models, definition, registry);
13
- operations.create = getCreateOperation();
13
+ operations.create = getCreateOperation(models, definition, registry);
14
14
  operations.update = getUpdateOperation(models, definition, registry);
15
15
  operations.get = getGetOperation(models, definition, registry);
16
16
  operations.remove = getRemoveOperation(models, definition);
17
17
  operations.find = getFindOperation(models, definition, registry);
18
- operations.upsert = ()=>{
18
+ operations.upsert = async ()=>{
19
19
  throw new Error('Not implemented');
20
20
  };
21
21
  return operations;
@@ -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=
@@ -14,7 +14,7 @@ const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefini
14
14
  plain: true
15
15
  });
16
16
  logger.default('Adding Key to Item with Key Types: %s', stringifyJSON(keyTypes));
17
- item = addKey(item, keyTypes);
17
+ item = addKey(row, item, keyTypes);
18
18
  item = populateEvents(item);
19
19
  logger.default('Key Added to Item: %s', stringifyJSON(item.key));
20
20
  if (referenceDefinitions && referenceDefinitions.length > 0) {