@fjell/lib-sequelize 4.4.4 → 4.4.5

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.
@@ -9,8 +9,30 @@ const logger$1 = require('../logger.cjs');
9
9
  const RowProcessor = require('../RowProcessor.cjs');
10
10
  const general = require('../util/general.cjs');
11
11
  const Library = require('@fjell/lib');
12
+ const sequelize = require('sequelize');
13
+ const relationshipUtils = require('../util/relationshipUtils.cjs');
12
14
 
13
15
  const logger = logger$1.default.get('sequelize', 'ops', 'update');
16
+ // Helper function to merge includes avoiding duplicates
17
+ const mergeIncludes = (existingIncludes, newIncludes)=>{
18
+ const mergedIncludes = [
19
+ ...existingIncludes
20
+ ];
21
+ for (const newInclude of newIncludes){
22
+ const existingIndex = mergedIncludes.findIndex((existing)=>existing.as === newInclude.as && existing.model === newInclude.model);
23
+ if (existingIndex === -1) {
24
+ mergedIncludes.push(newInclude);
25
+ } else if (newInclude.include && mergedIncludes[existingIndex].include) {
26
+ mergedIncludes[existingIndex].include = [
27
+ ...mergedIncludes[existingIndex].include,
28
+ ...newInclude.include
29
+ ];
30
+ } else if (newInclude.include) {
31
+ mergedIncludes[existingIndex].include = newInclude.include;
32
+ }
33
+ }
34
+ return mergedIncludes;
35
+ };
14
36
  const getUpdateOperation = (models, definition, registry)=>{
15
37
  const { options: { references, aggregations } } = definition;
16
38
  const update = async (key, item)=>{
@@ -26,15 +48,49 @@ const getUpdateOperation = (models, definition, registry)=>{
26
48
  const priKey = key;
27
49
  response = await model.findByPk(priKey.pk);
28
50
  } else if (core.isComKey(key)) {
29
- var _comKey_loc_, _comKey_loc_1;
30
51
  const comKey = key;
31
- // Find the model by using both of the identifiers.
32
- response = await model.findOne({
33
- where: {
34
- [(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,
35
- id: comKey === null || comKey === void 0 ? void 0 : comKey.pk
52
+ // Build query options for composite key with multiple location keys
53
+ const where = {
54
+ id: comKey.pk
55
+ };
56
+ const additionalIncludes = [];
57
+ // Process all location keys in the composite key
58
+ for (const locator of comKey.loc){
59
+ const relationshipInfo = relationshipUtils.buildRelationshipPath(model, locator.kt, kta, true);
60
+ if (!relationshipInfo.found) {
61
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
62
+ logger.error(errorMessage, {
63
+ key: comKey,
64
+ kta
65
+ });
66
+ throw new Error(errorMessage);
67
+ }
68
+ if (relationshipInfo.isDirect) {
69
+ // Direct foreign key field
70
+ const fieldName = `${locator.kt}Id`;
71
+ where[fieldName] = locator.lk;
72
+ } else if (relationshipInfo.path) {
73
+ // Hierarchical relationship requiring traversal
74
+ where[relationshipInfo.path] = {
75
+ [sequelize.Op.eq]: locator.lk
76
+ };
77
+ // Add necessary includes for relationship traversal
78
+ if (relationshipInfo.includes) {
79
+ additionalIncludes.push(...relationshipInfo.includes);
80
+ }
36
81
  }
82
+ }
83
+ // Build final query options
84
+ const queryOptions = {
85
+ where
86
+ };
87
+ if (additionalIncludes.length > 0) {
88
+ queryOptions.include = mergeIncludes([], additionalIncludes);
89
+ }
90
+ logger.default('Composite key query for update', {
91
+ queryOptions
37
92
  });
93
+ response = await model.findOne(queryOptions);
38
94
  }
39
95
  if (response) {
40
96
  // Remove the key and events
@@ -58,4 +114,4 @@ const getUpdateOperation = (models, definition, registry)=>{
58
114
  };
59
115
 
60
116
  exports.getUpdateOperation = getUpdateOperation;
61
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
117
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmNqcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -25,8 +25,8 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
25
25
  modelChain.push(currentModel);
26
26
  }
27
27
  // Build the full association path for the target field
28
- const targetType = kta[targetIndex];
29
- const associationPath = `$${associationParts.join('.')}.${targetType}Id$`;
28
+ const targetPrimaryKey = currentModel.primaryKeyAttribute || 'id';
29
+ const associationPath = `$${associationParts.join('.')}.${targetPrimaryKey}$`;
30
30
  // Build nested includes structure iteratively (clearer than recursion)
31
31
  let deepestInclude = null;
32
32
  // Build from the deepest level back to the root
@@ -1,27 +1,61 @@
1
1
  import { ikToLKA } from '@fjell/core';
2
+ import { serializeKey, contextManager } from './OperationContext.js';
3
+ import LibLogger from './logger.js';
2
4
 
3
- const buildAggregation = async (item, aggregationDefinition, registry)=>{
5
+ const logger = LibLogger.get('sequelize', 'AggregationBuilder');
6
+ const buildAggregation = async (item, aggregationDefinition, registry, context)=>{
4
7
  const location = ikToLKA(item.key);
5
8
  // Get the library instance from the registry using the key type array
6
9
  const libraryInstance = registry.get(aggregationDefinition.kta);
7
10
  if (!libraryInstance) {
8
11
  throw new Error(`Library instance not found for key type array: ${aggregationDefinition.kta.join(', ')}`);
9
12
  }
10
- // Based on cardinality, use either one or all operation
11
- if (aggregationDefinition.cardinality === 'one') {
12
- // For one-to-one relationship, use the one operation
13
- return libraryInstance.operations.one({}, location).then((result)=>{
14
- item[aggregationDefinition.property] = result;
13
+ // Create a cache key for this aggregation query
14
+ // This helps avoid running the same aggregation multiple times
15
+ const aggregationCacheKey = `${aggregationDefinition.kta.join('.')}_${aggregationDefinition.cardinality}_${serializeKey(item.key)}`;
16
+ if (context) {
17
+ // Check if this aggregation is already cached
18
+ if (context.cache.has(aggregationCacheKey)) {
19
+ const cachedResult = context.cache.get(aggregationCacheKey);
20
+ logger.default('Using cached aggregation result', {
21
+ aggregationCacheKey,
22
+ property: aggregationDefinition.property
23
+ });
24
+ item[aggregationDefinition.property] = cachedResult;
15
25
  return item;
16
- });
17
- } else {
18
- // For one-to-many relationship, use the all operation
19
- return libraryInstance.operations.all({}, location).then((results)=>{
20
- item[aggregationDefinition.property] = results;
21
- return item;
22
- });
26
+ }
27
+ // Note: We don't check for circular dependencies here because:
28
+ // 1. Aggregations are location-based queries, not key-based references
29
+ // 2. They should be allowed to run during normal item processing
30
+ // 3. The main circular dependency concern is with references, not aggregations
23
31
  }
32
+ // Execute aggregation within the current context to ensure context sharing
33
+ return contextManager.withContext(context || contextManager.getCurrentContext() || {
34
+ inProgress: new Set(),
35
+ cache: new Map()
36
+ }, async ()=>{
37
+ // Based on cardinality, use either one or all operation
38
+ if (aggregationDefinition.cardinality === 'one') {
39
+ // For one-to-one relationship, use the one operation
40
+ return libraryInstance.operations.one({}, location).then((result)=>{
41
+ if (context) {
42
+ context.cache.set(aggregationCacheKey, result);
43
+ }
44
+ item[aggregationDefinition.property] = result;
45
+ return item;
46
+ });
47
+ } else {
48
+ // For one-to-many relationship, use the all operation
49
+ return libraryInstance.operations.all({}, location).then((results)=>{
50
+ if (context) {
51
+ context.cache.set(aggregationCacheKey, results);
52
+ }
53
+ item[aggregationDefinition.property] = results;
54
+ return item;
55
+ });
56
+ }
57
+ });
24
58
  };
25
59
 
26
60
  export { buildAggregation };
27
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdncmVnYXRpb25CdWlsZGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
61
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdncmVnYXRpb25CdWlsZGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -21,19 +21,41 @@ const extractLocationKeyValue = (model, item, locatorType, kta)=>{
21
21
  }
22
22
  return value;
23
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;
24
+ // Need to traverse relationship hierarchy
25
+ // Find the path through the key type array
26
+ const locatorIndex = kta.indexOf(locatorType);
27
+ if (locatorIndex === -1) {
28
+ throw new Error(`Locator type '${locatorType}' not found in key type array`);
29
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
30
+ // Start from the current item (index 0 in kta)
31
+ let currentObject = item;
32
+ // Traverse through each intermediate relationship to reach the target
33
+ for(let i = 1; i < locatorIndex; i++){
34
+ const intermediateType = kta[i];
35
+ // Check if the intermediate relationship object is loaded
36
+ if (currentObject[intermediateType] && typeof currentObject[intermediateType] === 'object') {
37
+ currentObject = currentObject[intermediateType];
38
+ } else {
39
+ // Try the foreign key approach if the relationship object isn't loaded
40
+ const foreignKeyField = `${intermediateType}Id`;
41
+ if (typeof currentObject[foreignKeyField] !== 'undefined' && currentObject[foreignKeyField] !== null) {
42
+ // We have the foreign key but not the loaded object, we can't traverse further
43
+ throw new Error(`Intermediate relationship '${intermediateType}' is not loaded. Cannot traverse to '${locatorType}'. Either include the relationship in your query or ensure it's loaded.`);
44
+ }
45
+ throw new Error(`Intermediate relationship '${intermediateType}' is missing in the relationship chain. Expected path: ${kta.slice(0, locatorIndex + 1).join(' → ')}`);
46
+ }
47
+ }
48
+ // Now extract the target locator value from the current object
49
+ // First try to get it from the loaded relationship object
50
+ if (currentObject[locatorType] && typeof currentObject[locatorType] === 'object' && typeof currentObject[locatorType].id !== 'undefined') {
51
+ return currentObject[locatorType].id;
52
+ }
53
+ // If the relationship object isn't loaded, try the foreign key field
32
54
  const foreignKeyField = `${locatorType}Id`;
33
- if (typeof item[foreignKeyField] !== 'undefined' && item[foreignKeyField] !== null) {
34
- return item[foreignKeyField];
55
+ if (typeof currentObject[foreignKeyField] !== 'undefined' && currentObject[foreignKeyField] !== null) {
56
+ return currentObject[foreignKeyField];
35
57
  }
36
- throw new Error(`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available.`);
58
+ throw new Error(`Unable to extract location key for '${locatorType}'. Neither the relationship object nor direct foreign key is available. Traversal path: ${kta.slice(0, locatorIndex + 1).join(' → ')}`);
37
59
  }
38
60
  };
39
61
  const removeKey = (item)=>{
@@ -121,4 +143,4 @@ const addKey = (model, item, keyTypes)=>{
121
143
  };
122
144
 
123
145
  export { addKey, removeKey };
124
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
146
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiS2V5TWFzdGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -0,0 +1,155 @@
1
+ import LibLogger from './logger.js';
2
+
3
+ function _define_property(obj, key, value) {
4
+ if (key in obj) {
5
+ Object.defineProperty(obj, key, {
6
+ value: value,
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true
10
+ });
11
+ } else {
12
+ obj[key] = value;
13
+ }
14
+ return obj;
15
+ }
16
+ const logger = LibLogger.get('sequelize', 'OperationContext');
17
+ /**
18
+ * Serialize an ItemKey to a string for use in sets and maps
19
+ */ const serializeKey = (key)=>{
20
+ if ('pk' in key && 'kt' in key && !('loc' in key)) {
21
+ // PriKey
22
+ return `${key.kt}:${key.pk}`;
23
+ } else if ('pk' in key && 'kt' in key && 'loc' in key) {
24
+ // ComKey
25
+ const locStr = key.loc.map((l)=>`${l.kt}:${l.lk}`).join(',');
26
+ return `${key.kt}:${key.pk}|${locStr}`;
27
+ }
28
+ throw new Error(`Unsupported key type: ${JSON.stringify(key)}`);
29
+ };
30
+ /**
31
+ * Create a new OperationContext
32
+ */ const createOperationContext = ()=>{
33
+ const inProgress = new Set();
34
+ const cache = new Map();
35
+ return {
36
+ inProgress,
37
+ cache,
38
+ markInProgress (key) {
39
+ const serialized = serializeKey(key);
40
+ logger.default('Marking key as in progress', {
41
+ key,
42
+ serialized
43
+ });
44
+ inProgress.add(serialized);
45
+ },
46
+ markComplete (key) {
47
+ const serialized = serializeKey(key);
48
+ logger.default('Marking key as complete', {
49
+ key,
50
+ serialized
51
+ });
52
+ inProgress.delete(serialized);
53
+ },
54
+ isInProgress (key) {
55
+ const serialized = serializeKey(key);
56
+ const result = inProgress.has(serialized);
57
+ logger.default('Checking if key is in progress', {
58
+ key,
59
+ serialized,
60
+ result
61
+ });
62
+ return result;
63
+ },
64
+ getCached (key) {
65
+ const serialized = serializeKey(key);
66
+ const result = cache.get(serialized);
67
+ logger.default('Getting cached item', {
68
+ key,
69
+ serialized,
70
+ found: !!result
71
+ });
72
+ return result;
73
+ },
74
+ setCached (key, item) {
75
+ const serialized = serializeKey(key);
76
+ logger.default('Caching item', {
77
+ key,
78
+ serialized
79
+ });
80
+ cache.set(serialized, item);
81
+ },
82
+ isCached (key) {
83
+ const serialized = serializeKey(key);
84
+ const result = cache.has(serialized);
85
+ logger.default('Checking if key is cached', {
86
+ key,
87
+ serialized,
88
+ result
89
+ });
90
+ return result;
91
+ }
92
+ };
93
+ };
94
+ /**
95
+ * Context Manager for sharing context across operations without changing public interfaces
96
+ */ class ContextManager {
97
+ /**
98
+ * Set the current context for the current operation chain
99
+ */ setCurrentContext(context) {
100
+ const contextId = Math.random().toString(36).substring(7);
101
+ this.contexts.set(contextId, context);
102
+ this.currentContextId = contextId;
103
+ logger.default('Set current context', {
104
+ contextId
105
+ });
106
+ return contextId;
107
+ }
108
+ /**
109
+ * Get the current context if one is set
110
+ */ getCurrentContext() {
111
+ if (this.currentContextId) {
112
+ const context = this.contexts.get(this.currentContextId);
113
+ logger.default('Got current context', {
114
+ contextId: this.currentContextId,
115
+ found: !!context
116
+ });
117
+ return context;
118
+ }
119
+ return;
120
+ }
121
+ /**
122
+ * Clear the current context
123
+ */ clearCurrentContext() {
124
+ if (this.currentContextId) {
125
+ logger.default('Clearing current context', {
126
+ contextId: this.currentContextId
127
+ });
128
+ this.contexts.delete(this.currentContextId);
129
+ this.currentContextId = null;
130
+ }
131
+ }
132
+ /**
133
+ * Execute a function with a specific context set as current
134
+ */ async withContext(context, fn) {
135
+ const previousContextId = this.currentContextId;
136
+ this.setCurrentContext(context);
137
+ try {
138
+ return await fn();
139
+ } finally{
140
+ this.clearCurrentContext();
141
+ if (previousContextId) {
142
+ this.currentContextId = previousContextId;
143
+ }
144
+ }
145
+ }
146
+ constructor(){
147
+ _define_property(this, "contexts", new Map());
148
+ _define_property(this, "currentContextId", null);
149
+ }
150
+ }
151
+ // Global context manager instance
152
+ const contextManager = new ContextManager();
153
+
154
+ export { contextManager, createOperationContext, serializeKey };
155
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiT3BlcmF0aW9uQ29udGV4dC5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -1,4 +1,7 @@
1
- const buildReference = async (item, referenceDefinition, registry)=>{
1
+ import LibLogger from './logger.js';
2
+
3
+ const logger = LibLogger.get('sequelize', 'ReferenceBuilder');
4
+ const buildReference = async (item, referenceDefinition, registry, context)=>{
2
5
  // Check if there is more than one key type
3
6
  if (referenceDefinition.kta.length > 1) {
4
7
  throw new Error("The ReferenceBuilder doesn't work with more than one key type yet");
@@ -12,13 +15,52 @@ const buildReference = async (item, referenceDefinition, registry)=>{
12
15
  if (!library) {
13
16
  throw new Error("This model definition has a reference definition, but the dependency is not present");
14
17
  }
18
+ // Check if the column value is null - if so, skip the reference
19
+ const columnValue = item[referenceDefinition.column];
20
+ if (columnValue == null) {
21
+ item[referenceDefinition.property] = null;
22
+ return item;
23
+ }
15
24
  // Create a PriKey using the column value from item
16
25
  const priKey = {
17
26
  kt: referenceDefinition.kta[0],
18
- pk: item[referenceDefinition.column]
27
+ pk: columnValue
19
28
  };
20
- // Get the referenced item using the Library.Operations get method
21
- const referencedItem = await library.operations.get(priKey);
29
+ let referencedItem;
30
+ if (context) {
31
+ // Check if we already have this item cached
32
+ if (context.isCached(priKey)) {
33
+ logger.default('Using cached reference', {
34
+ priKey,
35
+ property: referenceDefinition.property
36
+ });
37
+ referencedItem = context.getCached(priKey);
38
+ } else if (context.isInProgress(priKey)) {
39
+ logger.default('Circular dependency detected, creating reference placeholder', {
40
+ priKey,
41
+ property: referenceDefinition.property
42
+ });
43
+ // Create a minimal reference object with just the key to break the cycle
44
+ referencedItem = {
45
+ key: priKey
46
+ };
47
+ } else {
48
+ // Mark this key as in progress before loading
49
+ context.markInProgress(priKey);
50
+ try {
51
+ // Get the referenced item using the Library.Operations get method (context now managed internally)
52
+ referencedItem = await library.operations.get(priKey);
53
+ // Cache the result
54
+ context.setCached(priKey, referencedItem);
55
+ } finally{
56
+ // Always mark as complete, even if there was an error
57
+ context.markComplete(priKey);
58
+ }
59
+ }
60
+ } else {
61
+ // Fallback to original behavior if no context provided
62
+ referencedItem = await library.operations.get(priKey);
63
+ }
22
64
  // TODO: In a Fjell-compliant implementation, this value should be stored in the ref property
23
65
  // For now, we'll just populate the property directly
24
66
  // Store the result in the property on item
@@ -27,4 +69,4 @@ const buildReference = async (item, referenceDefinition, registry)=>{
27
69
  };
28
70
 
29
71
  export { buildReference };
30
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVmZXJlbmNlQnVpbGRlci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
72
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVmZXJlbmNlQnVpbGRlci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
@@ -4,34 +4,49 @@ import { buildReference } from './ReferenceBuilder.js';
4
4
  import { buildAggregation } from './AggregationBuilder.js';
5
5
  import { stringifyJSON } from './util/general.js';
6
6
  import { populateEvents } from './EventCoordinator.js';
7
+ import { contextManager, createOperationContext } from './OperationContext.js';
7
8
 
8
9
  const logger = LibLogger.get('sequelize', 'RowProcessor');
9
- const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry)=>{
10
+ const processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry, context)=>{
10
11
  logger.default('Processing Row', {
11
12
  row
12
13
  });
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);
14
+ // Use provided context or create new one
15
+ const operationContext = context || createOperationContext();
16
+ // Process the row within the context to ensure all operations share the same context
17
+ return contextManager.withContext(operationContext, async ()=>{
18
+ let item = row.get({
19
+ plain: true
20
+ });
21
+ logger.default('Adding Key to Item with Key Types: %s', stringifyJSON(keyTypes));
22
+ item = addKey(row, item, keyTypes);
23
+ item = populateEvents(item);
24
+ logger.default('Key Added to Item: %s', stringifyJSON(item.key));
25
+ // Mark this item as in progress to detect circular references
26
+ operationContext.markInProgress(item.key);
27
+ try {
28
+ if (referenceDefinitions && referenceDefinitions.length > 0) {
29
+ for (const referenceDefinition of referenceDefinitions){
30
+ logger.default('Processing Reference for %s to %s', item.key.kt, stringifyJSON(referenceDefinition.kta));
31
+ item = await buildReference(item, referenceDefinition, registry, operationContext);
32
+ }
33
+ }
34
+ if (aggregationDefinitions && aggregationDefinitions.length > 0) {
35
+ for (const aggregationDefinition of aggregationDefinitions){
36
+ logger.default('Processing Aggregation for %s from %s', item.key.kt, stringifyJSON(aggregationDefinition.kta));
37
+ item = await buildAggregation(item, aggregationDefinition, registry, operationContext);
38
+ }
39
+ }
40
+ // Cache the fully processed item
41
+ operationContext.setCached(item.key, item);
42
+ } finally{
43
+ // Mark this item as complete
44
+ operationContext.markComplete(item.key);
30
45
  }
31
- }
32
- logger.default('Processed Row: %j', stringifyJSON(item));
33
- return item;
46
+ logger.default('Processed Row: %j', stringifyJSON(item));
47
+ return item;
48
+ });
34
49
  };
35
50
 
36
51
  export { processRow };
37
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm93UHJvY2Vzc29yLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
52
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm93UHJvY2Vzc29yLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==