@salesforce/lds-worker-api 1.112.0 → 1.112.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.
@@ -731,6 +731,9 @@ function createPrimingSession(config) {
731
731
  return {
732
732
  enqueue: session.enqueue.bind(session),
733
733
  cancel: session.cancel.bind(session),
734
+ on: session.on.bind(session),
735
+ once: session.once.bind(session),
736
+ off: session.off.bind(session),
734
737
  };
735
738
  }
736
739
 
@@ -744,4 +747,4 @@ if (process.env.NODE_ENV !== 'production') {
744
747
  }
745
748
 
746
749
  export { createPrimingSession, draftManager, draftQueue, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, invokeAdapter, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, setMetadataTTL, setUiApiRecordTTL, subscribeToAdapter };
747
- // version: 1.112.0-725d7e6e3
750
+ // version: 1.112.2-9a4fdc6da
@@ -16,5 +16,8 @@ export interface PrimingSessionConfig {
16
16
  export declare function createPrimingSession(config: PrimingSessionConfig): {
17
17
  enqueue: (work: PrimingWork) => Promise<void>;
18
18
  cancel: () => void;
19
+ on: <K extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K]) => void) => import("@salesforce/lds-priming").PrimingSession;
20
+ once: <K_1 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_1, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_1]) => void) => import("@salesforce/lds-priming").PrimingSession;
21
+ off: <K_2 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_2, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_2]) => void) => import("@salesforce/lds-priming").PrimingSession;
19
22
  };
20
23
  export {};
@@ -3649,7 +3649,7 @@ class Luvio {
3649
3649
  return this.environment.buildStructuredKey(namespace, representationName, idValues);
3650
3650
  }
3651
3651
  }
3652
- // engine version: 0.136.5-77eb3bb4
3652
+ // engine version: 0.137.1-cdbdc5cd
3653
3653
 
3654
3654
  /**
3655
3655
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3776,7 +3776,7 @@ function withDefaultLuvio(callback) {
3776
3776
  }
3777
3777
  callbacks.push(callback);
3778
3778
  }
3779
- // version: 1.112.0-725d7e6e3
3779
+ // version: 1.112.2-9a4fdc6da
3780
3780
 
3781
3781
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
3782
3782
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -15200,7 +15200,7 @@ function parseAndVisit(source) {
15200
15200
  updateReferenceMapWithKnownKey(ast, luvioDocumentNode);
15201
15201
  return luvioDocumentNode;
15202
15202
  }
15203
- // version: 1.112.0-725d7e6e3
15203
+ // version: 1.112.2-9a4fdc6da
15204
15204
 
15205
15205
  function unwrap(data) {
15206
15206
  // The lwc-luvio bindings import a function from lwc called "unwrap".
@@ -15971,15 +15971,31 @@ function createInvalidConfigError() {
15971
15971
  function createGraphQLImperativeAdapter(luvio, adapter, metadata, astResolver) {
15972
15972
  const { name } = metadata;
15973
15973
  const imperativeAdapterInvoke = (config, requestContext, callback) => {
15974
- const ast = astResolver(config.query);
15975
- if (ast === undefined) {
15976
- callback(createInvalidConfigError());
15977
- return;
15974
+ let coercedConfig = null;
15975
+ if ('batchQuery' in config) {
15976
+ coercedConfig = {
15977
+ batchQuery: config.batchQuery.map((individualConfig) => ({
15978
+ ...individualConfig,
15979
+ query: astResolver(individualConfig.query),
15980
+ })),
15981
+ };
15982
+ // If any of the configurations are invalid, we bail out of calling the adapter.
15983
+ if (coercedConfig.batchQuery.some((individualConfig) => individualConfig.query === undefined)) {
15984
+ callback(createInvalidConfigError());
15985
+ return;
15986
+ }
15987
+ }
15988
+ else if ('query' in config) {
15989
+ const ast = astResolver(config.query);
15990
+ if (ast === undefined) {
15991
+ callback(createInvalidConfigError());
15992
+ return;
15993
+ }
15994
+ coercedConfig = {
15995
+ ...config,
15996
+ query: ast,
15997
+ };
15978
15998
  }
15979
- const coercedConfig = {
15980
- ...config,
15981
- query: ast,
15982
- };
15983
15999
  const snapshotOrPromise = adapter(coercedConfig, requestContext);
15984
16000
  if (snapshotOrPromise === null) {
15985
16001
  callback(createInvalidConfigError());
@@ -16005,15 +16021,31 @@ function createGraphQLImperativeAdapter(luvio, adapter, metadata, astResolver) {
16005
16021
  const imperativeAdapterSubscribe = (config, requestContext, callback) => {
16006
16022
  let subscriberCallback = callback;
16007
16023
  let unsub;
16008
- const ast = astResolver(config.query);
16009
- if (ast === undefined) {
16010
- callback(createInvalidConfigError());
16011
- return () => { };
16024
+ let coercedConfig = null;
16025
+ if ('batchQuery' in config) {
16026
+ coercedConfig = {
16027
+ batchQuery: config.batchQuery.map((individualConfig) => ({
16028
+ ...individualConfig,
16029
+ query: astResolver(individualConfig.query),
16030
+ })),
16031
+ };
16032
+ // If any of the configurations are invalid, we bail out of calling the adapter.
16033
+ if (coercedConfig.batchQuery.some((individualConfig) => individualConfig.query === undefined)) {
16034
+ callback(createInvalidConfigError());
16035
+ return () => { };
16036
+ }
16037
+ }
16038
+ else if ('query' in config) {
16039
+ const ast = astResolver(config.query);
16040
+ if (ast === undefined) {
16041
+ callback(createInvalidConfigError());
16042
+ return () => { };
16043
+ }
16044
+ coercedConfig = {
16045
+ ...config,
16046
+ query: ast,
16047
+ };
16012
16048
  }
16013
- const coercedConfig = {
16014
- ...config,
16015
- query: ast,
16016
- };
16017
16049
  const snapshotOrPromise = adapter(coercedConfig, requestContext);
16018
16050
  if (snapshotOrPromise === null) {
16019
16051
  subscriberCallback(createInvalidConfigError());
@@ -16081,7 +16113,7 @@ function createGraphQLWireAdapterConstructor(luvio, adapter, metadata, astResolv
16081
16113
  const { apiFamily, name } = metadata;
16082
16114
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
16083
16115
  }
16084
- // version: 1.112.0-725d7e6e3
16116
+ // version: 1.112.2-9a4fdc6da
16085
16117
 
16086
16118
  /**
16087
16119
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -16164,7 +16196,7 @@ var FragmentReadResultState;
16164
16196
  ({
16165
16197
  state: FragmentReadResultState.Missing,
16166
16198
  });
16167
- // engine version: 0.136.5-77eb3bb4
16199
+ // engine version: 0.137.1-cdbdc5cd
16168
16200
 
16169
16201
  const { keys: ObjectKeys$3, freeze: ObjectFreeze$3, create: ObjectCreate$3 } = Object;
16170
16202
 
@@ -44269,7 +44301,7 @@ withDefaultLuvio((luvio) => {
44269
44301
  dropFunction: instrumentation$2.notifyRecordUpdateAvailableDropped,
44270
44302
  });
44271
44303
  });
44272
- // version: 1.112.0-725d7e6e3
44304
+ // version: 1.112.2-9a4fdc6da
44273
44305
 
44274
44306
  var caseSensitiveUserId = '005B0000000GR4OIAW';
44275
44307
 
@@ -58280,8 +58312,6 @@ function reportDraftAwareContentVersionSynthesizeCalls(mimeType) {
58280
58312
  }
58281
58313
  function reportPrimingError(errorType, recordCount) {
58282
58314
  }
58283
- function reportPrimingSuccess(recordCount) {
58284
- }
58285
58315
 
58286
58316
  function instrumentDraftQueue(queue) {
58287
58317
  queue.registerOnChangedListener((draftQueueEvent) => {
@@ -59171,8 +59201,14 @@ class PrimingSession extends EventEmitter {
59171
59201
  }
59172
59202
  // pop any pending work off the queue
59173
59203
  const batches = typedBatches.splice(0, typedBatches.length);
59204
+ const before = Date.now();
59174
59205
  // fetch the metadata for the batches
59175
- const { availableBatches, unavailableTypes, unavailableIds } = await this.fetchMetadata(batches);
59206
+ const { availableBatches, unavailableTypes, unavailableIds, availableTypes } = await this.fetchMetadata(batches);
59207
+ this.emit('metadata-fetched', {
59208
+ duration: Date.now() - before,
59209
+ availableTypes,
59210
+ unavailableTypes,
59211
+ });
59176
59212
  if (unavailableIds.length > 0) {
59177
59213
  this.emit('error', {
59178
59214
  ids: unavailableIds,
@@ -59191,14 +59227,21 @@ class PrimingSession extends EventEmitter {
59191
59227
  // parallelizes batches of priming work
59192
59228
  enqueueBatches(batches) {
59193
59229
  for (const batch of batches) {
59230
+ const queuedTime = Date.now();
59194
59231
  this.networkWorkerPool.push({
59195
59232
  workFn: (abortController) => {
59233
+ const workTime = Date.now();
59234
+ this.emit('batch-starting', { queuedTime: workTime - queuedTime });
59196
59235
  return this.recordLoader
59197
59236
  .fetchRecordData(batch, abortController)
59198
59237
  .then(async (result) => {
59199
59238
  if (abortController.signal.aborted) {
59200
59239
  return;
59201
59240
  }
59241
+ this.emit('batch-fetched', {
59242
+ ids: batch.ids,
59243
+ duration: Date.now() - workTime,
59244
+ });
59202
59245
  if (result.ok === false) {
59203
59246
  const { error } = result;
59204
59247
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -59218,13 +59261,31 @@ class PrimingSession extends EventEmitter {
59218
59261
  });
59219
59262
  }
59220
59263
  const { records } = result;
59264
+ const beforeWrite = Date.now();
59221
59265
  // dispatch the write but DO NOT wait on it to unblock the network pool
59222
59266
  this.recordIngestor
59223
59267
  .insertRecords(records)
59224
- .then(({ written, conflicted }) => {
59268
+ .then(({ written, conflicted, errors }) => {
59269
+ this.emit('batch-written', {
59270
+ written,
59271
+ conflicted,
59272
+ errors: errors
59273
+ .map((e) => e.ids)
59274
+ .reduce((a, b) => a.concat(b), []),
59275
+ duration: Date.now() - beforeWrite,
59276
+ });
59225
59277
  if (abortController.signal.aborted) {
59226
59278
  return;
59227
59279
  }
59280
+ if (errors.length > 0) {
59281
+ errors.forEach(({ ids, message }) => {
59282
+ this.emit('error', {
59283
+ ids,
59284
+ code: 'unknown',
59285
+ message: message,
59286
+ });
59287
+ });
59288
+ }
59228
59289
  // now that the records are persisted, emit the primed event
59229
59290
  if (written.length > 0) {
59230
59291
  this.emit('primed', Array.from(written));
@@ -59257,6 +59318,7 @@ class PrimingSession extends EventEmitter {
59257
59318
  }, new Set()));
59258
59319
  const objectInfoMap = await this.objectInfoLoader.getObjectInfos(apiNames);
59259
59320
  const unavailableTypes = apiNames.filter((x) => !objectInfoMap[x]);
59321
+ const availableTypes = apiNames.filter((x) => objectInfoMap[x]);
59260
59322
  const unavilableBatches = batches.filter((x) => unavailableTypes.includes(x.type));
59261
59323
  const availableBatches = batches.filter((x) => !unavailableTypes.includes(x.type));
59262
59324
  const unavailableIds = unavilableBatches.reduce((acc, x) => {
@@ -59264,9 +59326,11 @@ class PrimingSession extends EventEmitter {
59264
59326
  return acc;
59265
59327
  }, []);
59266
59328
  return {
59329
+ apiNames,
59267
59330
  availableBatches,
59268
59331
  unavilableBatches,
59269
59332
  unavailableTypes,
59333
+ availableTypes,
59270
59334
  unavailableIds,
59271
59335
  };
59272
59336
  }
@@ -59426,44 +59490,28 @@ class RecordIngestor {
59426
59490
  */
59427
59491
  async insertRecords(syntheticRecords) {
59428
59492
  if (syntheticRecords.length === 0) {
59429
- return { written: [], conflicted: [] };
59493
+ return { written: [], conflicted: [], errors: [] };
59430
59494
  }
59431
59495
  const luvio = this.getLuvio();
59432
59496
  const ingestionTimestamp = Date.now();
59433
59497
  const ttlOverride = await luvio.storeGetTTLOverride(keyPrefix$1, RepresentationType$N);
59434
- const metadata = JSON.stringify({
59498
+ const metadata = {
59435
59499
  ingestionTimestamp,
59436
59500
  expirationTimestamp: (ttlOverride !== null && ttlOverride !== void 0 ? ttlOverride : TTL$v) + ingestionTimestamp,
59437
59501
  namespace: keyPrefix$1,
59438
59502
  representationName: RepresentationType$N,
59439
59503
  metadataVersion: DURABLE_METADATA_VERSION,
59440
59504
  version: VERSION$14,
59441
- });
59442
- const idsToPrime = new Set();
59443
- const written = [];
59444
- const rows = syntheticRecords.map((record) => {
59445
- idsToPrime.add(record.id);
59446
- return `('${keyBuilder$1Q(luvio, { recordId: record.id })}', '${JSON.stringify(record)}', '${metadata}')`;
59447
- });
59448
- const dml = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${rows.join(',\n')} returning key;`;
59449
- const result = await this.store.query(dml, []);
59450
- for (const row of result.rows) {
59451
- const id = extractRecordIdFromStoreKey(row[0]);
59452
- if (id) {
59453
- written.push(id);
59454
- idsToPrime.delete(id);
59455
- }
59456
- }
59457
- return { written: Array.from(written), conflicted: Array.from(idsToPrime) };
59505
+ };
59506
+ return this.store.writeRecords(syntheticRecords.map((record) => ({ record, metadata })));
59458
59507
  }
59459
59508
  }
59460
59509
 
59461
59510
  function instrumentPrimingSession(session) {
59462
- session.on('error', (payload) => {
59463
- reportPrimingError(payload.code, payload.ids.length);
59511
+ session.on('error', ({ code, ids }) => {
59512
+ reportPrimingError(code, ids.length);
59464
59513
  });
59465
- session.on('primed', (payload) => {
59466
- reportPrimingSuccess(payload.length);
59514
+ session.on('primed', ({ length }) => {
59467
59515
  });
59468
59516
  return session;
59469
59517
  }
@@ -59516,11 +59564,78 @@ class NimbusPrimingNetworkAdapter {
59516
59564
  }
59517
59565
  }
59518
59566
 
59567
+ // ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
59568
+ const SQLITE_MAX_VARIABLE_NUMBER = 999;
59569
+ const PARAMS_PER_RECORD = 3;
59570
+ // We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
59571
+ const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
59572
+ class SqlitePrimingStore {
59573
+ constructor(getLuvio, store) {
59574
+ this.getLuvio = getLuvio;
59575
+ this.store = store;
59576
+ }
59577
+ async writeRecords(records) {
59578
+ const batches = batchArray(records);
59579
+ const writeResult = { written: [], conflicted: [], errors: [] };
59580
+ return (await Promise.all(batches.map((batch) => this.writeBatch(batch)))).reduce((acc, curr) => {
59581
+ acc.written.push(...curr.written);
59582
+ acc.conflicted.push(...curr.conflicted);
59583
+ acc.errors.push(...curr.errors);
59584
+ return acc;
59585
+ }, writeResult);
59586
+ }
59587
+ async writeBatch(records) {
59588
+ const idsToPrime = new Set();
59589
+ const written = [];
59590
+ const statement = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${records
59591
+ .map((_) => `(?,?,?)`)
59592
+ .join(',')} returning key;`;
59593
+ const params = [];
59594
+ records.forEach(({ record, metadata }) => {
59595
+ idsToPrime.add(record.id);
59596
+ const key = keyBuilder$1Q(this.getLuvio(), { recordId: record.id });
59597
+ params.push(key);
59598
+ params.push(JSON.stringify(record));
59599
+ params.push(JSON.stringify(metadata));
59600
+ });
59601
+ try {
59602
+ const result = await this.store.query(statement, params);
59603
+ for (const row of result.rows) {
59604
+ const id = extractRecordIdFromStoreKey(row[0]);
59605
+ if (id) {
59606
+ written.push(id);
59607
+ idsToPrime.delete(id);
59608
+ }
59609
+ }
59610
+ return { written, conflicted: Array.from(idsToPrime), errors: [] };
59611
+ }
59612
+ catch (e) {
59613
+ return {
59614
+ written: [],
59615
+ conflicted: [],
59616
+ errors: [{ ids: Array.from(idsToPrime), message: e }],
59617
+ };
59618
+ }
59619
+ }
59620
+ }
59621
+ function batchArray(arr, batchSize = BATCH_SIZE) {
59622
+ const batches = [];
59623
+ // If the array length is less than or equal to the batch size, return the array as a single batch
59624
+ if (arr.length <= batchSize) {
59625
+ return [arr];
59626
+ }
59627
+ // Split the array into batches of size batchSize
59628
+ for (let i = 0; i < arr.length; i += batchSize) {
59629
+ batches.push(arr.slice(i, i + batchSize));
59630
+ }
59631
+ return batches;
59632
+ }
59633
+
59519
59634
  function primingSessionFactory(config) {
59520
59635
  const { store, objectInfoService, getLuvio } = config;
59521
59636
  const networkAdapter = new NimbusPrimingNetworkAdapter();
59522
59637
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
59523
- const recordIngestor = new RecordIngestor(store, getLuvio);
59638
+ const recordIngestor = new RecordIngestor(new SqlitePrimingStore(getLuvio, store), getLuvio);
59524
59639
  const session = new PrimingSession({
59525
59640
  recordLoader,
59526
59641
  recordIngestor,
@@ -59663,7 +59778,7 @@ register({
59663
59778
  id: '@salesforce/lds-network-adapter',
59664
59779
  instrument: instrument$1,
59665
59780
  });
59666
- // version: 1.112.0-725d7e6e3
59781
+ // version: 1.112.2-9a4fdc6da
59667
59782
 
59668
59783
  const { create: create$2, keys: keys$2 } = Object;
59669
59784
  const { stringify: stringify$1, parse: parse$1 } = JSON;
@@ -77389,7 +77504,7 @@ register({
77389
77504
  configuration: { ...configurationForGraphQLAdapters },
77390
77505
  instrument,
77391
77506
  });
77392
- // version: 1.112.0-725d7e6e3
77507
+ // version: 1.112.2-9a4fdc6da
77393
77508
 
77394
77509
  // On core the unstable adapters are re-exported with different names,
77395
77510
 
@@ -79518,7 +79633,7 @@ withDefaultLuvio((luvio) => {
79518
79633
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
79519
79634
  graphQLImperative = ldsAdapter;
79520
79635
  });
79521
- // version: 1.112.0-725d7e6e3
79636
+ // version: 1.112.2-9a4fdc6da
79522
79637
 
79523
79638
  var gqlApi = /*#__PURE__*/Object.freeze({
79524
79639
  __proto__: null,
@@ -80185,6 +80300,9 @@ function createPrimingSession(config) {
80185
80300
  return {
80186
80301
  enqueue: session.enqueue.bind(session),
80187
80302
  cancel: session.cancel.bind(session),
80303
+ on: session.on.bind(session),
80304
+ once: session.once.bind(session),
80305
+ off: session.off.bind(session),
80188
80306
  };
80189
80307
  }
80190
80308
 
@@ -80193,4 +80311,4 @@ const { luvio } = getRuntime();
80193
80311
  setDefaultLuvio({ luvio });
80194
80312
 
80195
80313
  export { createPrimingSession, draftManager, draftQueue, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, invokeAdapter, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, registerReportObserver, setMetadataTTL, setUiApiRecordTTL, subscribeToAdapter };
80196
- // version: 1.112.0-725d7e6e3
80314
+ // version: 1.112.2-9a4fdc6da
@@ -16,5 +16,8 @@ export interface PrimingSessionConfig {
16
16
  export declare function createPrimingSession(config: PrimingSessionConfig): {
17
17
  enqueue: (work: PrimingWork) => Promise<void>;
18
18
  cancel: () => void;
19
+ on: <K extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K]) => void) => import("@salesforce/lds-priming").PrimingSession;
20
+ once: <K_1 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_1, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_1]) => void) => import("@salesforce/lds-priming").PrimingSession;
21
+ off: <K_2 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_2, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_2]) => void) => import("@salesforce/lds-priming").PrimingSession;
19
22
  };
20
23
  export {};
@@ -3655,7 +3655,7 @@
3655
3655
  return this.environment.buildStructuredKey(namespace, representationName, idValues);
3656
3656
  }
3657
3657
  }
3658
- // engine version: 0.136.5-77eb3bb4
3658
+ // engine version: 0.137.1-cdbdc5cd
3659
3659
 
3660
3660
  /**
3661
3661
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -3782,7 +3782,7 @@
3782
3782
  }
3783
3783
  callbacks.push(callback);
3784
3784
  }
3785
- // version: 1.112.0-725d7e6e3
3785
+ // version: 1.112.2-9a4fdc6da
3786
3786
 
3787
3787
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
3788
3788
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -15206,7 +15206,7 @@
15206
15206
  updateReferenceMapWithKnownKey(ast, luvioDocumentNode);
15207
15207
  return luvioDocumentNode;
15208
15208
  }
15209
- // version: 1.112.0-725d7e6e3
15209
+ // version: 1.112.2-9a4fdc6da
15210
15210
 
15211
15211
  function unwrap(data) {
15212
15212
  // The lwc-luvio bindings import a function from lwc called "unwrap".
@@ -15977,15 +15977,31 @@
15977
15977
  function createGraphQLImperativeAdapter(luvio, adapter, metadata, astResolver) {
15978
15978
  const { name } = metadata;
15979
15979
  const imperativeAdapterInvoke = (config, requestContext, callback) => {
15980
- const ast = astResolver(config.query);
15981
- if (ast === undefined) {
15982
- callback(createInvalidConfigError());
15983
- return;
15980
+ let coercedConfig = null;
15981
+ if ('batchQuery' in config) {
15982
+ coercedConfig = {
15983
+ batchQuery: config.batchQuery.map((individualConfig) => ({
15984
+ ...individualConfig,
15985
+ query: astResolver(individualConfig.query),
15986
+ })),
15987
+ };
15988
+ // If any of the configurations are invalid, we bail out of calling the adapter.
15989
+ if (coercedConfig.batchQuery.some((individualConfig) => individualConfig.query === undefined)) {
15990
+ callback(createInvalidConfigError());
15991
+ return;
15992
+ }
15993
+ }
15994
+ else if ('query' in config) {
15995
+ const ast = astResolver(config.query);
15996
+ if (ast === undefined) {
15997
+ callback(createInvalidConfigError());
15998
+ return;
15999
+ }
16000
+ coercedConfig = {
16001
+ ...config,
16002
+ query: ast,
16003
+ };
15984
16004
  }
15985
- const coercedConfig = {
15986
- ...config,
15987
- query: ast,
15988
- };
15989
16005
  const snapshotOrPromise = adapter(coercedConfig, requestContext);
15990
16006
  if (snapshotOrPromise === null) {
15991
16007
  callback(createInvalidConfigError());
@@ -16011,15 +16027,31 @@
16011
16027
  const imperativeAdapterSubscribe = (config, requestContext, callback) => {
16012
16028
  let subscriberCallback = callback;
16013
16029
  let unsub;
16014
- const ast = astResolver(config.query);
16015
- if (ast === undefined) {
16016
- callback(createInvalidConfigError());
16017
- return () => { };
16030
+ let coercedConfig = null;
16031
+ if ('batchQuery' in config) {
16032
+ coercedConfig = {
16033
+ batchQuery: config.batchQuery.map((individualConfig) => ({
16034
+ ...individualConfig,
16035
+ query: astResolver(individualConfig.query),
16036
+ })),
16037
+ };
16038
+ // If any of the configurations are invalid, we bail out of calling the adapter.
16039
+ if (coercedConfig.batchQuery.some((individualConfig) => individualConfig.query === undefined)) {
16040
+ callback(createInvalidConfigError());
16041
+ return () => { };
16042
+ }
16043
+ }
16044
+ else if ('query' in config) {
16045
+ const ast = astResolver(config.query);
16046
+ if (ast === undefined) {
16047
+ callback(createInvalidConfigError());
16048
+ return () => { };
16049
+ }
16050
+ coercedConfig = {
16051
+ ...config,
16052
+ query: ast,
16053
+ };
16018
16054
  }
16019
- const coercedConfig = {
16020
- ...config,
16021
- query: ast,
16022
- };
16023
16055
  const snapshotOrPromise = adapter(coercedConfig, requestContext);
16024
16056
  if (snapshotOrPromise === null) {
16025
16057
  subscriberCallback(createInvalidConfigError());
@@ -16087,7 +16119,7 @@
16087
16119
  const { apiFamily, name } = metadata;
16088
16120
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
16089
16121
  }
16090
- // version: 1.112.0-725d7e6e3
16122
+ // version: 1.112.2-9a4fdc6da
16091
16123
 
16092
16124
  /**
16093
16125
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -16170,7 +16202,7 @@
16170
16202
  ({
16171
16203
  state: FragmentReadResultState.Missing,
16172
16204
  });
16173
- // engine version: 0.136.5-77eb3bb4
16205
+ // engine version: 0.137.1-cdbdc5cd
16174
16206
 
16175
16207
  const { keys: ObjectKeys$3, freeze: ObjectFreeze$3, create: ObjectCreate$3 } = Object;
16176
16208
 
@@ -44275,7 +44307,7 @@
44275
44307
  dropFunction: instrumentation$2.notifyRecordUpdateAvailableDropped,
44276
44308
  });
44277
44309
  });
44278
- // version: 1.112.0-725d7e6e3
44310
+ // version: 1.112.2-9a4fdc6da
44279
44311
 
44280
44312
  var caseSensitiveUserId = '005B0000000GR4OIAW';
44281
44313
 
@@ -58286,8 +58318,6 @@
58286
58318
  }
58287
58319
  function reportPrimingError(errorType, recordCount) {
58288
58320
  }
58289
- function reportPrimingSuccess(recordCount) {
58290
- }
58291
58321
 
58292
58322
  function instrumentDraftQueue(queue) {
58293
58323
  queue.registerOnChangedListener((draftQueueEvent) => {
@@ -59177,8 +59207,14 @@
59177
59207
  }
59178
59208
  // pop any pending work off the queue
59179
59209
  const batches = typedBatches.splice(0, typedBatches.length);
59210
+ const before = Date.now();
59180
59211
  // fetch the metadata for the batches
59181
- const { availableBatches, unavailableTypes, unavailableIds } = await this.fetchMetadata(batches);
59212
+ const { availableBatches, unavailableTypes, unavailableIds, availableTypes } = await this.fetchMetadata(batches);
59213
+ this.emit('metadata-fetched', {
59214
+ duration: Date.now() - before,
59215
+ availableTypes,
59216
+ unavailableTypes,
59217
+ });
59182
59218
  if (unavailableIds.length > 0) {
59183
59219
  this.emit('error', {
59184
59220
  ids: unavailableIds,
@@ -59197,14 +59233,21 @@
59197
59233
  // parallelizes batches of priming work
59198
59234
  enqueueBatches(batches) {
59199
59235
  for (const batch of batches) {
59236
+ const queuedTime = Date.now();
59200
59237
  this.networkWorkerPool.push({
59201
59238
  workFn: (abortController) => {
59239
+ const workTime = Date.now();
59240
+ this.emit('batch-starting', { queuedTime: workTime - queuedTime });
59202
59241
  return this.recordLoader
59203
59242
  .fetchRecordData(batch, abortController)
59204
59243
  .then(async (result) => {
59205
59244
  if (abortController.signal.aborted) {
59206
59245
  return;
59207
59246
  }
59247
+ this.emit('batch-fetched', {
59248
+ ids: batch.ids,
59249
+ duration: Date.now() - workTime,
59250
+ });
59208
59251
  if (result.ok === false) {
59209
59252
  const { error } = result;
59210
59253
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -59224,13 +59267,31 @@
59224
59267
  });
59225
59268
  }
59226
59269
  const { records } = result;
59270
+ const beforeWrite = Date.now();
59227
59271
  // dispatch the write but DO NOT wait on it to unblock the network pool
59228
59272
  this.recordIngestor
59229
59273
  .insertRecords(records)
59230
- .then(({ written, conflicted }) => {
59274
+ .then(({ written, conflicted, errors }) => {
59275
+ this.emit('batch-written', {
59276
+ written,
59277
+ conflicted,
59278
+ errors: errors
59279
+ .map((e) => e.ids)
59280
+ .reduce((a, b) => a.concat(b), []),
59281
+ duration: Date.now() - beforeWrite,
59282
+ });
59231
59283
  if (abortController.signal.aborted) {
59232
59284
  return;
59233
59285
  }
59286
+ if (errors.length > 0) {
59287
+ errors.forEach(({ ids, message }) => {
59288
+ this.emit('error', {
59289
+ ids,
59290
+ code: 'unknown',
59291
+ message: message,
59292
+ });
59293
+ });
59294
+ }
59234
59295
  // now that the records are persisted, emit the primed event
59235
59296
  if (written.length > 0) {
59236
59297
  this.emit('primed', Array.from(written));
@@ -59263,6 +59324,7 @@
59263
59324
  }, new Set()));
59264
59325
  const objectInfoMap = await this.objectInfoLoader.getObjectInfos(apiNames);
59265
59326
  const unavailableTypes = apiNames.filter((x) => !objectInfoMap[x]);
59327
+ const availableTypes = apiNames.filter((x) => objectInfoMap[x]);
59266
59328
  const unavilableBatches = batches.filter((x) => unavailableTypes.includes(x.type));
59267
59329
  const availableBatches = batches.filter((x) => !unavailableTypes.includes(x.type));
59268
59330
  const unavailableIds = unavilableBatches.reduce((acc, x) => {
@@ -59270,9 +59332,11 @@
59270
59332
  return acc;
59271
59333
  }, []);
59272
59334
  return {
59335
+ apiNames,
59273
59336
  availableBatches,
59274
59337
  unavilableBatches,
59275
59338
  unavailableTypes,
59339
+ availableTypes,
59276
59340
  unavailableIds,
59277
59341
  };
59278
59342
  }
@@ -59432,44 +59496,28 @@
59432
59496
  */
59433
59497
  async insertRecords(syntheticRecords) {
59434
59498
  if (syntheticRecords.length === 0) {
59435
- return { written: [], conflicted: [] };
59499
+ return { written: [], conflicted: [], errors: [] };
59436
59500
  }
59437
59501
  const luvio = this.getLuvio();
59438
59502
  const ingestionTimestamp = Date.now();
59439
59503
  const ttlOverride = await luvio.storeGetTTLOverride(keyPrefix$1, RepresentationType$N);
59440
- const metadata = JSON.stringify({
59504
+ const metadata = {
59441
59505
  ingestionTimestamp,
59442
59506
  expirationTimestamp: (ttlOverride !== null && ttlOverride !== void 0 ? ttlOverride : TTL$v) + ingestionTimestamp,
59443
59507
  namespace: keyPrefix$1,
59444
59508
  representationName: RepresentationType$N,
59445
59509
  metadataVersion: DURABLE_METADATA_VERSION,
59446
59510
  version: VERSION$14,
59447
- });
59448
- const idsToPrime = new Set();
59449
- const written = [];
59450
- const rows = syntheticRecords.map((record) => {
59451
- idsToPrime.add(record.id);
59452
- return `('${keyBuilder$1Q(luvio, { recordId: record.id })}', '${JSON.stringify(record)}', '${metadata}')`;
59453
- });
59454
- const dml = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${rows.join(',\n')} returning key;`;
59455
- const result = await this.store.query(dml, []);
59456
- for (const row of result.rows) {
59457
- const id = extractRecordIdFromStoreKey(row[0]);
59458
- if (id) {
59459
- written.push(id);
59460
- idsToPrime.delete(id);
59461
- }
59462
- }
59463
- return { written: Array.from(written), conflicted: Array.from(idsToPrime) };
59511
+ };
59512
+ return this.store.writeRecords(syntheticRecords.map((record) => ({ record, metadata })));
59464
59513
  }
59465
59514
  }
59466
59515
 
59467
59516
  function instrumentPrimingSession(session) {
59468
- session.on('error', (payload) => {
59469
- reportPrimingError(payload.code, payload.ids.length);
59517
+ session.on('error', ({ code, ids }) => {
59518
+ reportPrimingError(code, ids.length);
59470
59519
  });
59471
- session.on('primed', (payload) => {
59472
- reportPrimingSuccess(payload.length);
59520
+ session.on('primed', ({ length }) => {
59473
59521
  });
59474
59522
  return session;
59475
59523
  }
@@ -59522,11 +59570,78 @@
59522
59570
  }
59523
59571
  }
59524
59572
 
59573
+ // ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
59574
+ const SQLITE_MAX_VARIABLE_NUMBER = 999;
59575
+ const PARAMS_PER_RECORD = 3;
59576
+ // We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
59577
+ const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
59578
+ class SqlitePrimingStore {
59579
+ constructor(getLuvio, store) {
59580
+ this.getLuvio = getLuvio;
59581
+ this.store = store;
59582
+ }
59583
+ async writeRecords(records) {
59584
+ const batches = batchArray(records);
59585
+ const writeResult = { written: [], conflicted: [], errors: [] };
59586
+ return (await Promise.all(batches.map((batch) => this.writeBatch(batch)))).reduce((acc, curr) => {
59587
+ acc.written.push(...curr.written);
59588
+ acc.conflicted.push(...curr.conflicted);
59589
+ acc.errors.push(...curr.errors);
59590
+ return acc;
59591
+ }, writeResult);
59592
+ }
59593
+ async writeBatch(records) {
59594
+ const idsToPrime = new Set();
59595
+ const written = [];
59596
+ const statement = `INSERT or IGNORE INTO lds_data (key, data, metadata) VALUES ${records
59597
+ .map((_) => `(?,?,?)`)
59598
+ .join(',')} returning key;`;
59599
+ const params = [];
59600
+ records.forEach(({ record, metadata }) => {
59601
+ idsToPrime.add(record.id);
59602
+ const key = keyBuilder$1Q(this.getLuvio(), { recordId: record.id });
59603
+ params.push(key);
59604
+ params.push(JSON.stringify(record));
59605
+ params.push(JSON.stringify(metadata));
59606
+ });
59607
+ try {
59608
+ const result = await this.store.query(statement, params);
59609
+ for (const row of result.rows) {
59610
+ const id = extractRecordIdFromStoreKey(row[0]);
59611
+ if (id) {
59612
+ written.push(id);
59613
+ idsToPrime.delete(id);
59614
+ }
59615
+ }
59616
+ return { written, conflicted: Array.from(idsToPrime), errors: [] };
59617
+ }
59618
+ catch (e) {
59619
+ return {
59620
+ written: [],
59621
+ conflicted: [],
59622
+ errors: [{ ids: Array.from(idsToPrime), message: e }],
59623
+ };
59624
+ }
59625
+ }
59626
+ }
59627
+ function batchArray(arr, batchSize = BATCH_SIZE) {
59628
+ const batches = [];
59629
+ // If the array length is less than or equal to the batch size, return the array as a single batch
59630
+ if (arr.length <= batchSize) {
59631
+ return [arr];
59632
+ }
59633
+ // Split the array into batches of size batchSize
59634
+ for (let i = 0; i < arr.length; i += batchSize) {
59635
+ batches.push(arr.slice(i, i + batchSize));
59636
+ }
59637
+ return batches;
59638
+ }
59639
+
59525
59640
  function primingSessionFactory(config) {
59526
59641
  const { store, objectInfoService, getLuvio } = config;
59527
59642
  const networkAdapter = new NimbusPrimingNetworkAdapter();
59528
59643
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
59529
- const recordIngestor = new RecordIngestor(store, getLuvio);
59644
+ const recordIngestor = new RecordIngestor(new SqlitePrimingStore(getLuvio, store), getLuvio);
59530
59645
  const session = new PrimingSession({
59531
59646
  recordLoader,
59532
59647
  recordIngestor,
@@ -59669,7 +59784,7 @@
59669
59784
  id: '@salesforce/lds-network-adapter',
59670
59785
  instrument: instrument$1,
59671
59786
  });
59672
- // version: 1.112.0-725d7e6e3
59787
+ // version: 1.112.2-9a4fdc6da
59673
59788
 
59674
59789
  const { create: create$2, keys: keys$2 } = Object;
59675
59790
  const { stringify: stringify$1, parse: parse$1 } = JSON;
@@ -77395,7 +77510,7 @@
77395
77510
  configuration: { ...configurationForGraphQLAdapters },
77396
77511
  instrument,
77397
77512
  });
77398
- // version: 1.112.0-725d7e6e3
77513
+ // version: 1.112.2-9a4fdc6da
77399
77514
 
77400
77515
  // On core the unstable adapters are re-exported with different names,
77401
77516
 
@@ -79524,7 +79639,7 @@
79524
79639
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
79525
79640
  graphQLImperative = ldsAdapter;
79526
79641
  });
79527
- // version: 1.112.0-725d7e6e3
79642
+ // version: 1.112.2-9a4fdc6da
79528
79643
 
79529
79644
  var gqlApi = /*#__PURE__*/Object.freeze({
79530
79645
  __proto__: null,
@@ -80191,6 +80306,9 @@
80191
80306
  return {
80192
80307
  enqueue: session.enqueue.bind(session),
80193
80308
  cancel: session.cancel.bind(session),
80309
+ on: session.on.bind(session),
80310
+ once: session.once.bind(session),
80311
+ off: session.off.bind(session),
80194
80312
  };
80195
80313
  }
80196
80314
 
@@ -80216,4 +80334,4 @@
80216
80334
  Object.defineProperty(exports, '__esModule', { value: true });
80217
80335
 
80218
80336
  }));
80219
- // version: 1.112.0-725d7e6e3
80337
+ // version: 1.112.2-9a4fdc6da
@@ -16,5 +16,8 @@ export interface PrimingSessionConfig {
16
16
  export declare function createPrimingSession(config: PrimingSessionConfig): {
17
17
  enqueue: (work: PrimingWork) => Promise<void>;
18
18
  cancel: () => void;
19
+ on: <K extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K]) => void) => import("@salesforce/lds-priming").PrimingSession;
20
+ once: <K_1 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_1, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_1]) => void) => import("@salesforce/lds-priming").PrimingSession;
21
+ off: <K_2 extends keyof import("@salesforce/lds-priming").PrimingEvents>(eventName: K_2, callback: (payload: import("@salesforce/lds-priming").PrimingEvents[K_2]) => void) => import("@salesforce/lds-priming").PrimingSession;
19
22
  };
20
23
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-worker-api",
3
- "version": "1.112.0",
3
+ "version": "1.112.2",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "",
6
6
  "main": "dist/standalone/umd/lds-worker-api.js",
@@ -33,19 +33,19 @@
33
33
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-worker-api"
34
34
  },
35
35
  "devDependencies": {
36
- "@luvio/adapter-test-library": "0.136.5",
37
- "@luvio/engine": "0.136.5",
38
- "@luvio/environments": "0.136.5",
36
+ "@luvio/adapter-test-library": "0.137.1",
37
+ "@luvio/engine": "0.137.1",
38
+ "@luvio/environments": "0.137.1",
39
39
  "@oat-sa/rollup-plugin-wildcard-external": "^0.1.0",
40
- "@salesforce/lds-adapters-graphql": "^1.112.0",
41
- "@salesforce/lds-adapters-uiapi": "^1.112.0",
42
- "@salesforce/lds-default-luvio": "^1.112.0",
43
- "@salesforce/lds-drafts": "^1.112.0",
44
- "@salesforce/lds-graphql-parser": "^1.112.0",
45
- "@salesforce/lds-luvio-engine": "^1.112.0",
46
- "@salesforce/lds-priming": "^1.112.0",
47
- "@salesforce/lds-runtime-mobile": "^1.112.0",
48
- "@salesforce/nimbus-plugin-lds": "^1.112.0",
40
+ "@salesforce/lds-adapters-graphql": "^1.112.2",
41
+ "@salesforce/lds-adapters-uiapi": "^1.112.2",
42
+ "@salesforce/lds-default-luvio": "^1.112.2",
43
+ "@salesforce/lds-drafts": "^1.112.2",
44
+ "@salesforce/lds-graphql-parser": "^1.112.2",
45
+ "@salesforce/lds-luvio-engine": "^1.112.2",
46
+ "@salesforce/lds-priming": "^1.112.2",
47
+ "@salesforce/lds-runtime-mobile": "^1.112.2",
48
+ "@salesforce/nimbus-plugin-lds": "^1.112.2",
49
49
  "ajv": "^8.11.0",
50
50
  "glob": "^7.1.5",
51
51
  "nimbus-types": "^2.0.0-alpha1",