@comake/skl-js-engine 1.3.13 → 1.3.15

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/src/SklEngine.ts CHANGED
@@ -81,6 +81,8 @@ export type CapabilityInterface = Record<string, CapabilityHandler>;
81
81
 
82
82
  export type MappingResponseOption<T extends boolean> = T extends true ? JSONObject : NodeObject;
83
83
 
84
+ export type WriteOptions = { bypassHooks?: boolean };
85
+
84
86
  export class SKLEngine implements SKLEngineInterface {
85
87
  private readonly queryAdapter: QueryAdapter;
86
88
  private readonly functions?: Record<string, (args: any | any[]) => any>;
@@ -149,7 +151,8 @@ export class SKLEngine implements SKLEngineInterface {
149
151
  const context = {
150
152
  entities: [],
151
153
  operation: 'find',
152
- operationParameters: { options }
154
+ operationParameters: { options },
155
+ sklEngine: this
153
156
  };
154
157
 
155
158
  await globalHooks.execute(HookTypes.READ, HookStages.BEFORE, context);
@@ -176,7 +179,8 @@ export class SKLEngine implements SKLEngineInterface {
176
179
  const context = {
177
180
  entities: [],
178
181
  operation: 'findBy',
179
- operationParameters: { where }
182
+ operationParameters: { where },
183
+ sklEngine: this
180
184
  };
181
185
  await globalHooks.execute(HookTypes.READ, HookStages.BEFORE, context);
182
186
  try {
@@ -208,7 +212,8 @@ export class SKLEngine implements SKLEngineInterface {
208
212
  const context = {
209
213
  entities: [],
210
214
  operation: 'findAll',
211
- operationParameters: { options }
215
+ operationParameters: { options },
216
+ sklEngine: this
212
217
  };
213
218
  await globalHooks.execute(HookTypes.READ, HookStages.BEFORE, context);
214
219
  try {
@@ -228,7 +233,8 @@ export class SKLEngine implements SKLEngineInterface {
228
233
  const context = {
229
234
  entities: [],
230
235
  operation: 'groupBy',
231
- operationParameters: { options }
236
+ operationParameters: { options },
237
+ sklEngine: this
232
238
  };
233
239
  await globalHooks.execute(HookTypes.READ, HookStages.BEFORE, context);
234
240
  try {
@@ -248,7 +254,8 @@ export class SKLEngine implements SKLEngineInterface {
248
254
  const context = {
249
255
  entities: [],
250
256
  operation: 'findAllBy',
251
- operationParameters: { where }
257
+ operationParameters: { where },
258
+ sklEngine: this
252
259
  };
253
260
  await globalHooks.execute(HookTypes.READ, HookStages.BEFORE, context);
254
261
  try {
@@ -271,30 +278,30 @@ export class SKLEngine implements SKLEngineInterface {
271
278
  return PerformanceLogger.withSpanRoot('SklEngine.count', async() => this.queryAdapter.count(options), { options });
272
279
  }
273
280
 
274
- public async save(entity: Entity): Promise<Entity>;
275
- public async save(entities: Entity[]): Promise<Entity[]>;
276
- public async save(entityOrEntities: Entity | Entity[]): Promise<Entity | Entity[]> {
281
+ public async save(entity: Entity, options?: WriteOptions): Promise<Entity>;
282
+ public async save(entities: Entity[], options?: WriteOptions): Promise<Entity[]>;
283
+ public async save(entityOrEntities: Entity | Entity[], options?: WriteOptions): Promise<Entity | Entity[]> {
277
284
  return PerformanceLogger.withSpanRoot('SklEngine.save', async() => {
278
285
  const entityArray = Array.isArray(entityOrEntities) ? entityOrEntities : [entityOrEntities];
279
286
  const isSingleEntity = !Array.isArray(entityOrEntities);
280
287
 
281
- await globalHooks.executeBeforeCreate(entityArray);
288
+ await globalHooks.executeBeforeCreate(entityArray, { sklEngine: this, bypassHooks: options?.bypassHooks });
282
289
 
283
290
  try {
284
291
  await this.validateEntitiesConformToObjectSchema(entityArray);
285
292
  const savedEntities = await this.queryAdapter.save(entityArray);
286
- await globalHooks.executeAfterCreate(savedEntities);
293
+ await globalHooks.executeAfterCreate(savedEntities, { sklEngine: this, bypassHooks: options?.bypassHooks });
287
294
  return isSingleEntity ? savedEntities[0] : savedEntities;
288
295
  } catch (error) {
289
- await globalHooks.executeErrorCreate(entityArray, error as Error);
296
+ await globalHooks.executeErrorCreate(entityArray, error as Error, { sklEngine: this, bypassHooks: options?.bypassHooks });
290
297
  throw error;
291
298
  }
292
299
  }, { entityCount: Array.isArray(entityOrEntities) ? entityOrEntities.length : 1 });
293
300
  }
294
301
 
295
- public async update(id: string, attributes: Partial<Entity>): Promise<void>;
296
- public async update(ids: string[], attributes: Partial<Entity>): Promise<void>;
297
- public async update(idOrIds: string | string[], attributes: Partial<Entity>): Promise<void> {
302
+ public async update(id: string, attributes: Partial<Entity>, options?: WriteOptions): Promise<void>;
303
+ public async update(ids: string[], attributes: Partial<Entity>, options?: WriteOptions): Promise<void>;
304
+ public async update(idOrIds: string | string[], attributes: Partial<Entity>, options?: WriteOptions): Promise<void> {
298
305
  return PerformanceLogger.withSpanRoot('SklEngine.update', async() => {
299
306
  const idArray = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
300
307
  const isSingleEntity = !Array.isArray(idOrIds);
@@ -302,7 +309,9 @@ export class SKLEngine implements SKLEngineInterface {
302
309
  await globalHooks.execute(HookTypes.UPDATE, HookStages.BEFORE, {
303
310
  entities: [],
304
311
  operation: 'update',
305
- operationParameters: { idArray, attributes }
312
+ operationParameters: { idArray, attributes },
313
+ sklEngine: this,
314
+ bypassHooks: options?.bypassHooks
306
315
  });
307
316
 
308
317
  try {
@@ -316,13 +325,17 @@ export class SKLEngine implements SKLEngineInterface {
316
325
  await globalHooks.execute(HookTypes.UPDATE, HookStages.AFTER, {
317
326
  entities: [],
318
327
  operation: 'update',
319
- operationParameters: { idArray, attributes }
328
+ operationParameters: { idArray, attributes },
329
+ sklEngine: this,
330
+ bypassHooks: options?.bypassHooks
320
331
  });
321
332
  } catch (error: unknown) {
322
333
  await globalHooks.execute(HookTypes.UPDATE, HookStages.ERROR, {
323
334
  entities: [],
324
335
  operation: 'update',
325
- operationParameters: { idArray, attributes }
336
+ operationParameters: { idArray, attributes },
337
+ sklEngine: this,
338
+ bypassHooks: options?.bypassHooks
326
339
  }, error);
327
340
  throw error;
328
341
  }
@@ -470,16 +483,18 @@ export class SKLEngine implements SKLEngineInterface {
470
483
  }
471
484
  }
472
485
 
473
- public async delete(id: string): Promise<void>;
474
- public async delete(ids: string[]): Promise<void>;
475
- public async delete(idOrIds: string | string[]): Promise<void> {
486
+ public async delete(id: string, options?: WriteOptions): Promise<void>;
487
+ public async delete(ids: string[], options?: WriteOptions): Promise<void>;
488
+ public async delete(idOrIds: string | string[], options?: WriteOptions): Promise<void> {
476
489
  return PerformanceLogger.withSpanRoot('SklEngine.delete', async() => {
477
490
  const idArray = Array.isArray(idOrIds) ? idOrIds : [idOrIds];
478
491
 
479
492
  await globalHooks.execute(HookTypes.DELETE, HookStages.BEFORE, {
480
493
  entities: [],
481
494
  operation: 'delete',
482
- operationParameters: { idArray }
495
+ operationParameters: { idArray },
496
+ sklEngine: this,
497
+ bypassHooks: options?.bypassHooks
483
498
  });
484
499
 
485
500
  try {
@@ -488,13 +503,17 @@ export class SKLEngine implements SKLEngineInterface {
488
503
  await globalHooks.execute(HookTypes.DELETE, HookStages.AFTER, {
489
504
  entities: [],
490
505
  operation: 'delete',
491
- operationParameters: { idArray }
506
+ operationParameters: { idArray },
507
+ sklEngine: this,
508
+ bypassHooks: options?.bypassHooks
492
509
  });
493
510
  } catch (error) {
494
511
  await globalHooks.execute(HookTypes.DELETE, HookStages.ERROR, {
495
512
  entities: [],
496
513
  operation: 'delete',
497
- operationParameters: { idArray }
514
+ operationParameters: { idArray },
515
+ sklEngine: this,
516
+ bypassHooks: options?.bypassHooks
498
517
  }, error);
499
518
  throw error;
500
519
  }
@@ -609,7 +628,7 @@ export class SKLEngine implements SKLEngineInterface {
609
628
  // Execute capability mapping before hook if appropriate -
610
629
  // works for any mapping that can be used as a verb mapping
611
630
  if (mapping) {
612
- await globalHooks.executeBeforeExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping);
631
+ await globalHooks.executeBeforeExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping, { sklEngine: this });
613
632
  }
614
633
 
615
634
  const verbReturnValue = await this.executeMapping(mapping, capabilityArgs, capabilityConfig, account);
@@ -620,7 +639,7 @@ export class SKLEngine implements SKLEngineInterface {
620
639
 
621
640
  // Execute capability mapping after hook if appropriate
622
641
  if (mapping) {
623
- await globalHooks.executeAfterExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping, verbReturnValue);
642
+ await globalHooks.executeAfterExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping, verbReturnValue, { sklEngine: this });
624
643
  }
625
644
 
626
645
  this.globalCallbacks?.onCapabilityEnd?.(capability[PROP_ENTITY_ID], verbReturnValue);
@@ -632,7 +651,7 @@ export class SKLEngine implements SKLEngineInterface {
632
651
  } catch (error) {
633
652
  // Execute capability mapping error hook if appropriate
634
653
  if (mapping) {
635
- await globalHooks.executeErrorExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping, error as Error);
654
+ await globalHooks.executeErrorExecuteCapabilityMapping([capabilityArgs] as Entity[], mapping, error as Error, { sklEngine: this });
636
655
  }
637
656
  throw error;
638
657
  }
@@ -100,6 +100,10 @@ class GlobalHooksRegistry {
100
100
  * Execute hooks for a specific operation and stage
101
101
  */
102
102
  async execute(type: symbol, stage: symbol, context: SklHookContext, resultOrError?: any): Promise<any> {
103
+ // Allow bypassing hooks to prevent re-entrancy from within hooks
104
+ if (context?.bypassHooks) {
105
+ return resultOrError;
106
+ }
103
107
  const hooksList = this.hooks.get(type)?.get(stage);
104
108
 
105
109
  if (!hooksList || hooksList.length === 0) {
@@ -166,43 +170,53 @@ class GlobalHooksRegistry {
166
170
  }
167
171
 
168
172
  // Execution convenience methods
169
- async executeBeforeCreate(entities: Entity[]): Promise<void> {
170
- await this.execute(HookTypes.CREATE, HookStages.BEFORE, { entities, operation: 'save', operationParameters: {}});
173
+ async executeBeforeCreate(entities: Entity[], extras?: Partial<SklHookContext>): Promise<void> {
174
+ await this.execute(
175
+ HookTypes.CREATE,
176
+ HookStages.BEFORE,
177
+ { entities, operation: 'save', operationParameters: {}, ...extras}
178
+ );
171
179
  }
172
180
 
173
- async executeAfterCreate(entities: Entity | Entity[]): Promise<any> {
181
+ async executeAfterCreate(entities: Entity | Entity[], extras?: Partial<SklHookContext>): Promise<any> {
174
182
  if (!Array.isArray(entities)) {
175
183
  entities = [ entities ];
176
184
  }
177
185
  return this.execute(
178
186
  HookTypes.CREATE,
179
187
  HookStages.AFTER,
180
- { entities, operation: 'save', operationParameters: {}},
188
+ { entities, operation: 'save', operationParameters: {}, ...extras},
181
189
  entities
182
190
  );
183
191
  }
184
192
 
185
- async executeErrorCreate(entities: Entity[], error: Error): Promise<void> {
193
+ async executeErrorCreate(entities: Entity[], error: Error, extras?: Partial<SklHookContext>): Promise<void> {
186
194
  await this.execute(
187
195
  HookTypes.CREATE,
188
196
  HookStages.ERROR,
189
- { entities, operation: 'save', operationParameters: {}},
197
+ { entities, operation: 'save', operationParameters: {}, ...extras},
190
198
  error
191
199
  );
192
200
  }
193
201
 
194
- async executeBeforeExecuteCapabilityMapping(entities: Entity[], capabilityMapping: CapabilityMapping): Promise<void> {
202
+ async executeBeforeExecuteCapabilityMapping(
203
+ entities: Entity[],
204
+ capabilityMapping: CapabilityMapping,
205
+ extras?: Partial<SklHookContext>
206
+ ): Promise<void> {
195
207
  await this.execute(HookTypes.EXECUTE_CAPABILITY_MAPPING, HookStages.BEFORE, {
196
208
  entities,
197
209
  operation: 'executeCapabilityMapping',
198
- operationParameters: { capabilityMapping }
210
+ operationParameters: { capabilityMapping },
211
+ ...extras
199
212
  });
200
213
  }
201
214
 
202
215
  async executeAfterExecuteCapabilityMapping(
203
216
  entities: Entity[],
204
217
  capabilityMapping: CapabilityMapping,
205
- result: any
218
+ result: any,
219
+ extras?: Partial<SklHookContext>
206
220
  ): Promise<any> {
207
221
  return this.execute(
208
222
  HookTypes.EXECUTE_CAPABILITY_MAPPING,
@@ -210,7 +224,8 @@ class GlobalHooksRegistry {
210
224
  {
211
225
  entities,
212
226
  operation: 'executeCapabilityMapping',
213
- operationParameters: { capabilityMapping }
227
+ operationParameters: { capabilityMapping },
228
+ ...extras
214
229
  },
215
230
  result
216
231
  );
@@ -219,7 +234,8 @@ class GlobalHooksRegistry {
219
234
  async executeErrorExecuteCapabilityMapping(
220
235
  entities: Entity[],
221
236
  capabilityMapping: CapabilityMapping,
222
- error: Error
237
+ error: Error,
238
+ extras?: Partial<SklHookContext>
223
239
  ): Promise<void> {
224
240
  await this.execute(
225
241
  HookTypes.EXECUTE_CAPABILITY_MAPPING,
@@ -227,7 +243,8 @@ class GlobalHooksRegistry {
227
243
  {
228
244
  entities,
229
245
  operation: 'executeCapabilityMapping',
230
- operationParameters: { capabilityMapping }
246
+ operationParameters: { capabilityMapping },
247
+ ...extras
231
248
  },
232
249
  error
233
250
  );
@@ -1,7 +1,9 @@
1
- import type { Entity } from '../util/Types';
1
+ import type { Entity, SKLEngineInterface } from '../util/Types';
2
2
 
3
3
  export type SklHookContext = {
4
4
  entities: Entity[];
5
5
  operation: string;
6
6
  operationParameters: Record<string, any>;
7
+ sklEngine?: SKLEngineInterface;
8
+ bypassHooks?: boolean;
7
9
  };
@@ -43,6 +43,7 @@ export interface EntityUpdateQueries {
43
43
  export interface EntityUpdateTriples {
44
44
  entityTriples: Triple[];
45
45
  timestampTriples: Triple[];
46
+ insertions: GraphQuads[];
46
47
  }
47
48
 
48
49
  export interface SparqlUpdateBuilderArgs {
@@ -106,9 +107,9 @@ export class SparqlUpdateBuilder {
106
107
  return ids.flatMap((id): InsertDeleteOperation[] => {
107
108
  const subject = DataFactory.namedNode(id);
108
109
  const { attributesToUpdate, attributesToDelete } = this.partitionAttributes(attributes);
109
- const deletionTriples = this.partialEntityToDeletionTriples(attributesToUpdate, subject);
110
- const insertionTriples = this.partialEntityToTriples(subject, attributesToUpdate);
111
- const deleteOnlyTriples = this.partialEntityToDeletionTriples(attributesToDelete, subject);
110
+ const { triples: deletionTriples, deletions: deletionDeletions } = this.partialEntityToDeletionTriples(attributesToUpdate, subject);
111
+ const { triples: insertionTriples, insertions: insertionInsertions } = this.partialEntityToTriples(subject, attributesToUpdate);
112
+ const { triples: deleteOnlyTriples } = this.partialEntityToDeletionTriples(attributesToDelete, subject);
112
113
 
113
114
  const updates: InsertDeleteOperation[] = [];
114
115
 
@@ -126,10 +127,26 @@ export class SparqlUpdateBuilder {
126
127
  } as InsertDeleteOperation);
127
128
  }
128
129
 
130
+ for (const deletion of deletionDeletions) {
131
+ updates.push({
132
+ updateType: 'insertdelete',
133
+ delete: [ deletion ],
134
+ insert: [],
135
+ where: [
136
+ ...deletion.triples.map(
137
+ (triple): Pattern => createSparqlOptional([ createSparqlBasicGraphPattern([ triple ]) ])
138
+ )
139
+ ],
140
+ using: {
141
+ default: [ deletion.name ]
142
+ }
143
+ } as InsertDeleteOperation);
144
+ }
145
+
129
146
  if (insertionTriples.length > 0) {
130
147
  updates.push({
131
148
  updateType: 'insert',
132
- insert: [ createSparqlGraphQuads(subject, insertionTriples) ]
149
+ insert: [ createSparqlGraphQuads(subject, insertionTriples), ...insertionInsertions ]
133
150
  } as InsertDeleteOperation);
134
151
  }
135
152
 
@@ -192,9 +209,9 @@ export class SparqlUpdateBuilder {
192
209
  return entities.reduce(
193
210
  (obj: EntityUpdateQueries, entity): EntityUpdateQueries => {
194
211
  const entityGraphName = DataFactory.namedNode(entity['@id']);
195
- const { entityTriples, timestampTriples } = this.entityToTriples(entity, entityGraphName);
212
+ const { entityTriples, timestampTriples, insertions } = this.entityToTriples(entity, entityGraphName);
196
213
  obj.clear.push(createSparqlClearUpdate(entityGraphName));
197
- obj.insertions.push(createSparqlGraphQuads(entityGraphName, entityTriples));
214
+ obj.insertions.push(createSparqlGraphQuads(entityGraphName, entityTriples), ...insertions);
198
215
  if (timestampTriples.length > 0) {
199
216
  obj.timestampInsertions.push(createSparqlGraphQuads(entityGraphName, timestampTriples));
200
217
  }
@@ -218,45 +235,52 @@ export class SparqlUpdateBuilder {
218
235
  });
219
236
  }
220
237
 
221
- private partialEntityToDeletionTriples(entity: NodeObject, subject: NamedNode): Triple[] {
222
- return Object.keys(entity).reduce((triples: Triple[], key): Triple[] => {
238
+ private partialEntityToDeletionTriples(entity: NodeObject, subject: NamedNode): { triples: Triple[]; deletions: GraphQuads[] } {
239
+ return Object.entries(entity).reduce((acc: { triples: Triple[]; deletions: GraphQuads[] }, [ key, value ]): { triples: Triple[]; deletions: GraphQuads[] } => {
223
240
  if (key !== '@id') {
224
- return [
225
- ...triples,
226
- this.buildTriplesWithSubjectPredicateAndVariableValue(
227
- subject,
228
- DataFactory.namedNode(key),
229
- this.variableGenerator.getNext()
230
- )
231
- ];
241
+ let deletions: GraphQuads[] = [];
242
+ if (value && typeof value === 'object' && '@id' in value && typeof value['@id'] === 'string' && '@type' in value) {
243
+ const { triples: nestedTriples, deletions: nestedDeletions } = this.partialEntityToDeletionTriples(value as NodeObject, DataFactory.namedNode(value['@id']));
244
+ deletions = [ ...deletions, createSparqlGraphQuads(DataFactory.namedNode(value['@id']), nestedTriples), ...nestedDeletions ];
245
+ }
246
+ return {
247
+ triples: [
248
+ ...acc.triples,
249
+ this.buildTriplesWithSubjectPredicateAndVariableValue(
250
+ subject,
251
+ key === '@type' ? rdfTypeNamedNode : DataFactory.namedNode(key),
252
+ this.variableGenerator.getNext()
253
+ )
254
+ ],
255
+ deletions: [ ...acc.deletions, ...deletions ]
256
+ };
232
257
  }
233
- return triples;
234
- }, []);
258
+ return acc;
259
+ }, { triples: [], deletions: [] });
235
260
  }
236
261
 
237
- private partialEntityToTriples(subject: NamedNode, entity: NodeObject): Triple[] {
238
- const entityTriples = Object.entries(entity).reduce((triples: Triple[], [ key, value ]): Triple[] => {
262
+ private partialEntityToTriples(subject: NamedNode, entity: NodeObject): { triples: Triple[]; insertions: GraphQuads[] } {
263
+ return Object.entries(entity).reduce((acc: { triples: Triple[]; insertions: GraphQuads[] }, [ key, value ]): { triples: Triple[]; insertions: GraphQuads[] } => {
239
264
  const values = ensureArray(value);
240
265
  if (key !== '@id') {
241
- let predicateTriples: Triple[];
266
+ let predicateTriples: { triples: Triple[]; insertions: GraphQuads[] };
242
267
  if (key === '@type') {
243
- predicateTriples = this.buildTriplesWithSubjectPredicateAndIriValue(
268
+ predicateTriples = { triples: this.buildTriplesWithSubjectPredicateAndIriValue(
244
269
  subject,
245
270
  rdfTypeNamedNode,
246
271
  values as string[]
247
- );
272
+ ), insertions: [] };
248
273
  } else {
249
274
  predicateTriples = this.buildTriplesForSubjectPredicateAndValues(subject, key, values);
250
275
  }
251
- return [ ...triples, ...predicateTriples ];
276
+ return { triples: [ ...acc.triples, ...predicateTriples.triples ], insertions: [ ...acc.insertions, ...predicateTriples.insertions ] };
252
277
  }
253
- return triples;
254
- }, []);
255
- return entityTriples;
278
+ return acc;
279
+ }, { triples: [], insertions: [] });
256
280
  }
257
281
 
258
282
  private entityToTriples(entity: NodeObject, subject: BlankNode | NamedNode): EntityUpdateTriples {
259
- const entityTriples = Object.entries(entity).reduce((triples: Triple[], [ key, value ]): Triple[] => {
283
+ const entityTriples = Object.entries(entity).reduce((acc: { triples: Triple[]; insertions: GraphQuads[] }, [ key, value ]): { triples: Triple[]; insertions: GraphQuads[] } => {
260
284
  const values = ensureArray(value);
261
285
  if (key !== '@id') {
262
286
  if (key === '@type') {
@@ -265,15 +289,15 @@ export class SparqlUpdateBuilder {
265
289
  rdfTypeNamedNode,
266
290
  values as string[]
267
291
  );
268
- return [ ...triples, ...predicateTriples ];
292
+ return { triples: [ ...acc.triples, ...predicateTriples ], insertions: acc.insertions };
269
293
  }
270
294
  if (!(this.setTimestamps && key === EngineConstants.prop.dateModified)) {
271
295
  const predicateTriples = this.buildTriplesForSubjectPredicateAndValues(subject, key, values);
272
- return [ ...triples, ...predicateTriples ];
296
+ return { triples: [ ...acc.triples, ...predicateTriples.triples ], insertions: [ ...acc.insertions, ...predicateTriples.insertions ] };
273
297
  }
274
298
  }
275
- return triples;
276
- }, []);
299
+ return acc;
300
+ }, { triples: [], insertions: [] });
277
301
 
278
302
  const timestampTriples = [];
279
303
  if (this.setTimestamps && subject.termType === 'NamedNode') {
@@ -283,7 +307,8 @@ export class SparqlUpdateBuilder {
283
307
  timestampTriples.push({ subject, predicate: modified, object: now });
284
308
  }
285
309
  return {
286
- entityTriples,
310
+ entityTriples: entityTriples.triples,
311
+ insertions: entityTriples.insertions,
287
312
  timestampTriples
288
313
  };
289
314
  }
@@ -292,10 +317,14 @@ export class SparqlUpdateBuilder {
292
317
  subject: BlankNode | NamedNode,
293
318
  predicate: string,
294
319
  values: any[]
295
- ): Triple[] {
320
+ ): { triples: Triple[]; insertions: GraphQuads[] } {
296
321
  const predicateTerm = DataFactory.namedNode(predicate);
297
- return values.flatMap((value: any): Triple[] =>
298
- this.buildTriplesWithSubjectPredicateAndValue(subject, predicateTerm, value));
322
+ // Return values.flatMap((value: any): { triples: Triple[]; insertions: GraphQuads[] } =>
323
+ // this.buildTriplesWithSubjectPredicateAndValue(subject, predicateTerm, value));
324
+ return values.reduce((acc: { triples: Triple[]; insertions: GraphQuads[] }, value: any) => {
325
+ const { triples, insertions } = this.buildTriplesWithSubjectPredicateAndValue(subject, predicateTerm, value);
326
+ return { triples: [ ...acc.triples, ...triples ], insertions: [ ...acc.insertions, ...insertions ] };
327
+ }, { triples: [], insertions: [] });
299
328
  }
300
329
 
301
330
  private buildTriplesWithSubjectPredicateAndIriValue(
@@ -329,26 +358,33 @@ export class SparqlUpdateBuilder {
329
358
  subject: BlankNode | NamedNode,
330
359
  predicate: NamedNode,
331
360
  value: any
332
- ): Triple[] {
361
+ ): { triples: Triple[]; insertions: GraphQuads[] } {
333
362
  const isObject = typeof value === 'object';
334
363
  if (isObject) {
335
364
  if ('@list' in value) {
336
- return this.buildTriplesForList(subject, predicate, value['@list']);
365
+ return { triples: this.buildTriplesForList(subject, predicate, value['@list']), insertions: [] };
337
366
  }
338
367
  if ('@value' in value) {
339
- return [ { subject, predicate, object: this.jsonLdValueObjectToLiteral(value) } as Triple ];
368
+ return { triples: [ { subject, predicate, object: this.jsonLdValueObjectToLiteral(value) } as Triple ], insertions: [] };
340
369
  }
341
370
 
342
371
  const isReferenceObject = '@id' in value;
343
372
  const isBlankNodeReferenceObject = !isReferenceObject || (value['@id'] as string).startsWith('_:');
344
373
  if (isBlankNodeReferenceObject) {
345
- return this.buildTriplesForBlankNode(subject, predicate, value as NodeObject);
374
+ return { triples: this.buildTriplesForBlankNode(subject, predicate, value as NodeObject), insertions: [] };
346
375
  }
347
376
  if (isReferenceObject) {
348
- return [ { subject, predicate, object: DataFactory.namedNode(value['@id']) } as Triple ];
377
+ const triples = [
378
+ { subject, predicate, object: DataFactory.namedNode(value['@id']) } as Triple
379
+ ];
380
+ if (value['@type']) {
381
+ const { entityTriples, insertions } = this.entityToTriples(value as NodeObject, DataFactory.namedNode(value['@id']));
382
+ return { triples, insertions: [ ...insertions, createSparqlGraphQuads(DataFactory.namedNode(value['@id']), entityTriples) ] };
383
+ }
384
+ return { triples, insertions: [] };
349
385
  }
350
386
  }
351
- return [ { subject, predicate, object: valueToLiteral(value) } as Triple ];
387
+ return { triples: [ { subject, predicate, object: valueToLiteral(value) } as Triple ], insertions: [] };
352
388
  }
353
389
 
354
390
  private jsonLdValueObjectToLiteral(value: ValueObject): Term {
@@ -372,7 +408,7 @@ export class SparqlUpdateBuilder {
372
408
  : [{ subject: blankNode, predicate: restPredicate, object: nilPredicate }];
373
409
  return [
374
410
  { subject, predicate, object: blankNode },
375
- ...this.buildTriplesWithSubjectPredicateAndValue(blankNode, firstPredicate, value[0]),
411
+ ...this.buildTriplesWithSubjectPredicateAndValue(blankNode, firstPredicate, value[0]).triples,
376
412
  ...rest
377
413
  ];
378
414
  }