@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.
- package/dist/cjs/AggregationBuilder.cjs +48 -14
- package/dist/cjs/KeyMaster.cjs +33 -11
- package/dist/cjs/OperationContext.cjs +161 -0
- package/dist/cjs/ReferenceBuilder.cjs +47 -5
- package/dist/cjs/RowProcessor.cjs +37 -22
- package/dist/cjs/ops/all.cjs +77 -18
- package/dist/cjs/ops/get.cjs +5 -2
- package/dist/cjs/ops/update.cjs +63 -7
- package/dist/cjs/util/relationshipUtils.cjs +2 -2
- package/dist/es/AggregationBuilder.js +48 -14
- package/dist/es/KeyMaster.js +33 -11
- package/dist/es/OperationContext.js +155 -0
- package/dist/es/ReferenceBuilder.js +47 -5
- package/dist/es/RowProcessor.js +37 -22
- package/dist/es/ops/all.js +77 -18
- package/dist/es/ops/get.js +5 -2
- package/dist/es/ops/update.js +63 -7
- package/dist/es/util/relationshipUtils.js +2 -2
- package/dist/index.cjs +461 -89
- package/dist/index.cjs.map +1 -1
- package/dist/types/AggregationBuilder.d.ts +2 -1
- package/dist/types/EventCoordinator.d.ts +6 -6
- package/dist/types/KeyMaster.d.ts +2 -2
- package/dist/types/OperationContext.d.ts +72 -0
- package/dist/types/ReferenceBuilder.d.ts +2 -1
- package/dist/types/RowProcessor.d.ts +2 -1
- package/dist/types/ops/all.d.ts +1 -1
- package/dist/types/ops/create.d.ts +2 -2
- package/dist/types/ops/update.d.ts +2 -2
- package/package.json +12 -12
package/dist/cjs/ops/update.cjs
CHANGED
|
@@ -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
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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,
|
|
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
|
|
29
|
-
const associationPath = `$${associationParts.join('.')}.${
|
|
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
|
|
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
|
-
//
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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,
|
|
61
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiQWdncmVnYXRpb25CdWlsZGVyLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
|
package/dist/es/KeyMaster.js
CHANGED
|
@@ -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
|
-
//
|
|
26
|
-
const
|
|
27
|
-
if (
|
|
28
|
-
|
|
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
|
-
//
|
|
31
|
-
|
|
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
|
|
34
|
-
return
|
|
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,
|
|
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
|
-
|
|
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:
|
|
27
|
+
pk: columnValue
|
|
19
28
|
};
|
|
20
|
-
|
|
21
|
-
|
|
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,
|
|
72
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUmVmZXJlbmNlQnVpbGRlci5qcyIsInNvdXJjZXMiOltdLCJzb3VyY2VzQ29udGVudCI6W10sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
|
package/dist/es/RowProcessor.js
CHANGED
|
@@ -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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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,
|
|
52
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiUm93UHJvY2Vzc29yLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
|