@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.
@@ -1,19 +1,150 @@
1
+ import { isPriKey, isComKey, validateKeys } from '@fjell/core';
1
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';
2
6
 
3
7
  const logger = LibLogger.get('sequelize', 'ops', 'create');
4
- const getCreateOperation = (// eslint-disable-next-line @typescript-eslint/no-unused-vars
5
- models, // eslint-disable-next-line @typescript-eslint/no-unused-vars
6
- definition, // eslint-disable-next-line @typescript-eslint/no-unused-vars
7
- registry)=>{
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)=>{
8
42
  const create = async (item, options)=>{
9
43
  logger.default('Create', {
10
44
  item,
11
45
  options
12
46
  });
13
- throw new Error('Not implemented');
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);
14
145
  };
15
146
  return create;
16
147
  };
17
148
 
18
149
  export { getCreateOperation };
19
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
150
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY3JlYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -2,8 +2,45 @@ import { isValidItemKey, isPriKey, isComKey, validateKeys } from '@fjell/core';
2
2
  import LibLogger from '../logger.js';
3
3
  import { processRow } from '../RowProcessor.js';
4
4
  import { NotFoundError } from '@fjell/lib';
5
+ import { buildRelationshipPath } from '../util/relationshipUtils.js';
5
6
 
6
7
  const logger = LibLogger.get('sequelize', 'ops', 'get');
8
+ // Helper function to process composite key and build query options
9
+ const processCompositeKey = (comKey, model, kta)=>{
10
+ const where = {
11
+ id: comKey.pk
12
+ };
13
+ const includes = [];
14
+ for (const locator of comKey.loc){
15
+ const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
16
+ if (!relationshipInfo.found) {
17
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
18
+ logger.error(errorMessage, {
19
+ key: comKey,
20
+ kta
21
+ });
22
+ throw new Error(errorMessage);
23
+ }
24
+ if (relationshipInfo.path) {
25
+ // This requires a relationship traversal
26
+ where[relationshipInfo.path] = locator.lk;
27
+ if (relationshipInfo.includes) {
28
+ includes.push(...relationshipInfo.includes);
29
+ }
30
+ } else {
31
+ // This is a direct field
32
+ const fieldName = `${locator.kt}Id`;
33
+ where[fieldName] = locator.lk;
34
+ }
35
+ }
36
+ const result = {
37
+ where
38
+ };
39
+ if (includes.length > 0) {
40
+ result.include = includes;
41
+ }
42
+ return result;
43
+ };
7
44
  const getGetOperation = (models, definition, registry)=>{
8
45
  const { coordinate, options: { references, aggregations } } = definition;
9
46
  const { kta } = coordinate;
@@ -20,17 +57,16 @@ const getGetOperation = (models, definition, registry)=>{
20
57
  const model = models[0];
21
58
  let item;
22
59
  if (isPriKey(itemKey)) {
60
+ // This is the easy case because we can just find the item by its primary key
23
61
  item = await model.findByPk(itemKey.pk);
24
62
  } else if (isComKey(itemKey)) {
25
- var _comKey_loc_, _comKey_loc_1;
63
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
26
64
  const comKey = itemKey;
27
- // TODO: This should probably interrogate the model?
28
- item = await model.findOne({
29
- where: {
30
- id: comKey.pk,
31
- [(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
32
- }
65
+ const queryOptions = processCompositeKey(comKey, model, kta);
66
+ logger.default('Composite key query', {
67
+ queryOptions
33
68
  });
69
+ item = await model.findOne(queryOptions);
34
70
  }
35
71
  if (!item) {
36
72
  throw new NotFoundError('get', coordinate, key);
@@ -42,4 +78,4 @@ const getGetOperation = (models, definition, registry)=>{
42
78
  };
43
79
 
44
80
  export { getGetOperation };
45
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
81
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2V0LmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -2,8 +2,45 @@ import { isValidItemKey, abbrevIK, isPriKey, isComKey } from '@fjell/core';
2
2
  import { populateEvents } from '../EventCoordinator.js';
3
3
  import { addKey } from '../KeyMaster.js';
4
4
  import LibLogger from '../logger.js';
5
+ import { buildRelationshipPath } from '../util/relationshipUtils.js';
5
6
 
6
7
  const logger = LibLogger.get('sequelize', 'ops', 'remove');
8
+ // Helper function to process composite key and build query options
9
+ const processCompositeKey = (comKey, model, kta)=>{
10
+ const where = {
11
+ id: comKey.pk
12
+ };
13
+ const includes = [];
14
+ for (const locator of comKey.loc){
15
+ const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
16
+ if (!relationshipInfo.found) {
17
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
18
+ logger.error(errorMessage, {
19
+ key: comKey,
20
+ kta
21
+ });
22
+ throw new Error(errorMessage);
23
+ }
24
+ if (relationshipInfo.path) {
25
+ // This requires a relationship traversal
26
+ where[relationshipInfo.path] = locator.lk;
27
+ if (relationshipInfo.includes) {
28
+ includes.push(...relationshipInfo.includes);
29
+ }
30
+ } else {
31
+ // This is a direct field
32
+ const fieldName = `${locator.kt}Id`;
33
+ where[fieldName] = locator.lk;
34
+ }
35
+ }
36
+ const result = {
37
+ where
38
+ };
39
+ if (includes.length > 0) {
40
+ result.include = includes;
41
+ }
42
+ return result;
43
+ };
7
44
  const getRemoveOperation = (models, definition, // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
45
  registry)=>{
9
46
  const { coordinate, options } = definition;
@@ -24,14 +61,16 @@ registry)=>{
24
61
  if (isPriKey(key)) {
25
62
  item = await model.findByPk(key.pk);
26
63
  } else if (isComKey(key)) {
27
- var _comKey_loc_, _comKey_loc_1;
64
+ // This is a composite key, so we need to build a where clause based on the composite key's locators
28
65
  const comKey = key;
29
- item = await model.findOne({
30
- where: {
31
- id: comKey.pk,
32
- [(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
33
- }
66
+ const queryOptions = processCompositeKey(comKey, model, kta);
67
+ logger.default('Composite key query', {
68
+ queryOptions
34
69
  });
70
+ item = await model.findOne(queryOptions);
71
+ }
72
+ if (!item) {
73
+ throw new Error(`Item not found for removal with key: ${abbrevIK(key)}`);
35
74
  }
36
75
  const isDeletedAttribute = model.getAttributes().isDeleted;
37
76
  const deletedAtAttribute = model.getAttributes().deletedAt;
@@ -47,14 +86,14 @@ registry)=>{
47
86
  returnItem = item === null || item === void 0 ? void 0 : item.get({
48
87
  plain: true
49
88
  });
50
- returnItem = addKey(returnItem, kta);
89
+ returnItem = addKey(item, returnItem, kta);
51
90
  returnItem = populateEvents(returnItem);
52
91
  } else if (options.deleteOnRemove) {
53
92
  await (item === null || item === void 0 ? void 0 : item.destroy());
54
93
  returnItem = item === null || item === void 0 ? void 0 : item.get({
55
94
  plain: true
56
95
  });
57
- returnItem = addKey(returnItem, kta);
96
+ returnItem = addKey(item, returnItem, kta);
58
97
  returnItem = populateEvents(returnItem);
59
98
  } else {
60
99
  throw new Error('No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set');
@@ -65,4 +104,4 @@ registry)=>{
65
104
  };
66
105
 
67
106
  export { getRemoveOperation };
68
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
107
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVtb3ZlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7In0=
@@ -1,5 +1,5 @@
1
1
  import { abbrevIK, isPriKey, isComKey, validateKeys } from '@fjell/core';
2
- import { removeEvents } from '../EventCoordinator.js';
2
+ import { extractEvents, removeEvents } from '../EventCoordinator.js';
3
3
  import { removeKey } from '../KeyMaster.js';
4
4
  import LibLogger from '../logger.js';
5
5
  import { processRow } from '../RowProcessor.js';
@@ -35,7 +35,9 @@ const getUpdateOperation = (models, definition, registry)=>{
35
35
  if (response) {
36
36
  // Remove the key and events
37
37
  let updateProps = removeKey(item);
38
- updateProps = removeEvents(item);
38
+ // TODO: We need the opposite of processRow, something to step down from fjell to database.
39
+ updateProps = extractEvents(updateProps);
40
+ updateProps = removeEvents(updateProps);
39
41
  logger.default('Response: %s', stringifyJSON(response));
40
42
  logger.default('Update Properties: %s', stringifyJSON(updateProps));
41
43
  // Update the object
@@ -52,4 +54,4 @@ const getUpdateOperation = (models, definition, registry)=>{
52
54
  };
53
55
 
54
56
  export { getUpdateOperation };
55
- //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OzsifQ==
57
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXBkYXRlLmpzIiwic291cmNlcyI6W10sInNvdXJjZXNDb250ZW50IjpbXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9
@@ -0,0 +1,112 @@
1
+ /* eslint-disable indent */ /**
2
+ * Helper function to build relationship chain includes
3
+ */ const buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex)=>{
4
+ // Build the association path and validate relationships exist
5
+ const associationParts = [];
6
+ const modelChain = [
7
+ targetModel
8
+ ];
9
+ let currentModel = targetModel;
10
+ // Validate that all associations exist and build model chain
11
+ for(let i = currentIndex + 1; i <= targetIndex; i++){
12
+ const intermediateType = kta[i];
13
+ const associationName = intermediateType;
14
+ if (!currentModel.associations || !currentModel.associations[associationName]) {
15
+ return {
16
+ success: false
17
+ };
18
+ }
19
+ associationParts.push(associationName);
20
+ currentModel = currentModel.associations[associationName].target;
21
+ modelChain.push(currentModel);
22
+ }
23
+ // Build the full association path for the target field
24
+ const targetType = kta[targetIndex];
25
+ const associationPath = `$${associationParts.join('.')}.${targetType}Id$`;
26
+ // Build nested includes structure iteratively (clearer than recursion)
27
+ let deepestInclude = null;
28
+ // Build from the deepest level back to the root
29
+ for(let i = targetIndex; i > currentIndex; i--){
30
+ const currentType = kta[i];
31
+ const modelIndex = i - currentIndex;
32
+ const includeObj = {
33
+ model: modelChain[modelIndex],
34
+ as: currentType,
35
+ required: true
36
+ };
37
+ if (deepestInclude) {
38
+ includeObj.include = [
39
+ deepestInclude
40
+ ];
41
+ }
42
+ deepestInclude = includeObj;
43
+ }
44
+ const includes = deepestInclude ? [
45
+ deepestInclude
46
+ ] : [];
47
+ return {
48
+ success: true,
49
+ path: associationPath,
50
+ includes
51
+ };
52
+ };
53
+ /**
54
+ * Helper function to build relationship path for a locator
55
+ * @param includeIsDirect Whether to include the isDirect flag in the result
56
+ */ const buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = false)=>{
57
+ // First check if the field exists directly
58
+ const directFieldName = `${locatorType}Id`;
59
+ const attributes = targetModel.getAttributes();
60
+ if (attributes && attributes[directFieldName]) {
61
+ const result = {
62
+ found: true
63
+ };
64
+ if (includeIsDirect) {
65
+ result.isDirect = true;
66
+ }
67
+ return result;
68
+ }
69
+ // If not direct, look for relationship path
70
+ const targetIndex = kta.indexOf(locatorType);
71
+ if (targetIndex === -1) {
72
+ const result = {
73
+ found: false
74
+ };
75
+ if (includeIsDirect) {
76
+ result.isDirect = false;
77
+ }
78
+ return result;
79
+ }
80
+ const currentIndex = 0; // We're always looking from the base model
81
+ if (targetIndex <= currentIndex) {
82
+ const result = {
83
+ found: false
84
+ };
85
+ if (includeIsDirect) {
86
+ result.isDirect = false;
87
+ }
88
+ return result;
89
+ }
90
+ const chainResult = buildRelationshipChain(targetModel, kta, currentIndex, targetIndex);
91
+ if (chainResult.success) {
92
+ const result = {
93
+ found: true,
94
+ path: chainResult.path,
95
+ includes: chainResult.includes
96
+ };
97
+ if (includeIsDirect) {
98
+ result.isDirect = false;
99
+ }
100
+ return result;
101
+ }
102
+ const result = {
103
+ found: false
104
+ };
105
+ if (includeIsDirect) {
106
+ result.isDirect = false;
107
+ }
108
+ return result;
109
+ };
110
+
111
+ export { buildRelationshipChain, buildRelationshipPath };
112
+ //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVsYXRpb25zaGlwVXRpbHMuanMiLCJzb3VyY2VzIjpbXSwic291cmNlc0NvbnRlbnQiOltdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OyJ9