@salesforce/lds-runtime-mobile 1.110.2 → 1.111.1

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/main.js CHANGED
@@ -1055,10 +1055,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1055
1055
  };
1056
1056
  const storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
1057
1057
  validateNotDisposed();
1058
- // return resolved promise here and wait for the L2 flush to happen in handleSuccessResponse,
1059
- // that flush will cause the onChanged handler to fire which will revive
1060
- // records to the main L1 store and call the base storeBroadcast
1061
- return Promise.resolve();
1058
+ // publishing to L2 is essentially "broadcasting" because the onChanged
1059
+ // handler will fire which will revive records to the main L1 store and
1060
+ // call the base storeBroadcast
1061
+ return publishChangesToDurableStore();
1062
1062
  };
1063
1063
  const publishChangesToDurableStore = function () {
1064
1064
  validateNotDisposed();
@@ -1279,7 +1279,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1279
1279
  // we don't need to prime metadata
1280
1280
  () => { });
1281
1281
  snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
1282
- await publishChangesToDurableStore();
1283
1282
  })();
1284
1283
  for (const key of keysToReviveAsArray) {
1285
1284
  // we are overwriting the previous promise at this key, but that
@@ -1307,7 +1306,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1307
1306
  // so all we have to do is ingest then write to L2
1308
1307
  ingestStagingStore = buildIngestStagingStore(environment);
1309
1308
  snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
1310
- await publishChangesToDurableStore();
1311
1309
  }
1312
1310
  if (snapshotFromMemoryIngest === undefined) {
1313
1311
  return undefined;
@@ -1320,12 +1318,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1320
1318
  const result = await reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(select, environment.createSnapshot, refresh));
1321
1319
  return result.snapshot;
1322
1320
  };
1323
- const handleErrorResponse = function (ingestAndBroadcastFunc) {
1321
+ const handleErrorResponse = async function (ingestAndBroadcastFunc) {
1324
1322
  validateNotDisposed();
1325
- const snapshotFromMemoryIngest = ingestAndBroadcastFunc();
1326
- return publishChangesToDurableStore().then(() => {
1327
- return snapshotFromMemoryIngest;
1328
- });
1323
+ ingestStagingStore = buildIngestStagingStore(environment);
1324
+ return ingestAndBroadcastFunc();
1329
1325
  };
1330
1326
  const getNotifyChangeStoreEntries = function (keys) {
1331
1327
  validateNotDisposed();
@@ -4350,6 +4346,58 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4350
4346
  * For full license text, see the LICENSE.txt file
4351
4347
  */
4352
4348
 
4349
+ /* Ideally we would use AbortController but it does not exist in V8, if it is ever polyfilled we can swap for it */
4350
+ class LdsAbortController {
4351
+ constructor() {
4352
+ this.signal = new AbortSignal();
4353
+ }
4354
+ abort() {
4355
+ this.signal.abort();
4356
+ }
4357
+ }
4358
+ class AbortSignal {
4359
+ constructor() {
4360
+ this._aborted = false;
4361
+ this.listeners = new Map();
4362
+ }
4363
+ get aborted() {
4364
+ return this._aborted;
4365
+ }
4366
+ addEventListener(type, listener) {
4367
+ let listeners = this.listeners.get(type);
4368
+ if (!listeners) {
4369
+ listeners = new Set();
4370
+ this.listeners.set(type, listeners);
4371
+ }
4372
+ listeners.add(listener);
4373
+ }
4374
+ removeEventListener(type, listener) {
4375
+ const listeners = this.listeners.get(type);
4376
+ if (listeners) {
4377
+ listeners.delete(listener);
4378
+ if (listeners.size === 0) {
4379
+ this.listeners.delete(type);
4380
+ }
4381
+ }
4382
+ }
4383
+ dispatchEvent(event) {
4384
+ const listeners = this.listeners.get(event.type);
4385
+ if (listeners) {
4386
+ for (const listener of listeners) {
4387
+ listener(this, event);
4388
+ }
4389
+ }
4390
+ return !event.defaultPrevented;
4391
+ }
4392
+ abort() {
4393
+ if (!this.aborted) {
4394
+ this._aborted = true;
4395
+ const abortEvent = new Event('abort');
4396
+ this.dispatchEvent(abortEvent);
4397
+ }
4398
+ }
4399
+ }
4400
+
4353
4401
  class AsyncWorkerPool {
4354
4402
  constructor(concurrency) {
4355
4403
  this.queue = [];
@@ -4360,51 +4408,57 @@ class AsyncWorkerPool {
4360
4408
  return new Promise((resolve, reject) => {
4361
4409
  this.queue.push({
4362
4410
  ...work,
4363
- workFn: () => work.workFn().then(resolve).catch(reject),
4411
+ workFn: (abortController) => work.workFn(abortController).then(resolve).catch(reject),
4364
4412
  });
4365
4413
  this.doWork();
4366
4414
  });
4367
4415
  }
4368
- async cancel() {
4369
- const promises = [];
4370
- const cancellingWork = [
4371
- ...this.activeWork.splice(0, this.activeWork.length),
4372
- ...this.queue.splice(0, this.queue.length),
4373
- ];
4374
- cancellingWork.forEach((work) => {
4375
- const { cancelFn } = work;
4416
+ /**
4417
+ * cancel all work in the queue and active work
4418
+ * @returns true if all work was cancelled, false if any work could not be cancelled
4419
+ */
4420
+ cancel() {
4421
+ let success = true;
4422
+ for (const { cancelFn } of this.queue) {
4423
+ if (cancelFn) {
4424
+ try {
4425
+ cancelFn();
4426
+ }
4427
+ catch (_a) {
4428
+ success = false;
4429
+ }
4430
+ }
4431
+ }
4432
+ this.queue = [];
4433
+ for (const { abortController, cancelFn } of this.activeWork) {
4434
+ abortController.abort();
4376
4435
  if (cancelFn) {
4377
- promises.push(cancelFn());
4436
+ try {
4437
+ cancelFn();
4438
+ }
4439
+ catch (_b) {
4440
+ success = false;
4441
+ }
4378
4442
  }
4379
- });
4380
- await promiseAllSettled(promises);
4443
+ }
4444
+ this.activeWork = [];
4445
+ return success;
4381
4446
  }
4382
4447
  doWork() {
4383
4448
  while (this.queue.length > 0 && this.activeWork.length < this.concurrency) {
4384
4449
  const work = this.queue.shift();
4385
4450
  if (work) {
4386
- this.activeWork.push(work);
4451
+ const abortController = new LdsAbortController();
4452
+ const newWork = { ...work, abortController };
4453
+ this.activeWork.push(newWork);
4387
4454
  const { workFn } = work;
4388
- workFn()
4389
- .then()
4390
- .finally(() => {
4391
- this.activeWork = this.activeWork.filter((w) => w !== work);
4455
+ workFn(abortController).finally(() => {
4456
+ this.activeWork = this.activeWork.filter((w) => w !== newWork);
4392
4457
  this.doWork();
4393
4458
  });
4394
4459
  }
4395
4460
  }
4396
4461
  }
4397
- }
4398
- function promiseAllSettled(promises) {
4399
- return Promise.all(promises.map((promise) => promise
4400
- .then((value) => ({
4401
- status: 'fulfilled',
4402
- value,
4403
- }))
4404
- .catch((reason) => ({
4405
- status: 'rejected',
4406
- reason,
4407
- }))));
4408
4462
  }
4409
4463
 
4410
4464
  /**
@@ -14934,9 +14988,10 @@ const DEFAULT_BATCH_SIZE = 500;
14934
14988
  const DEFAULT_CONCURRENCY = 6;
14935
14989
  class PrimingSession extends EventEmitter {
14936
14990
  constructor(config) {
14991
+ var _a, _b;
14937
14992
  super();
14938
- this.batchSize = config.batchSize || DEFAULT_BATCH_SIZE;
14939
- this.concurrency = config.concurrency || DEFAULT_CONCURRENCY;
14993
+ this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
14994
+ this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
14940
14995
  this.recordLoader = config.recordLoader;
14941
14996
  this.recordIngestor = config.recordIngestor;
14942
14997
  this.objectInfoLoader = config.objectInfoLoader;
@@ -14964,12 +15019,20 @@ class PrimingSession extends EventEmitter {
14964
15019
  this.enqueueBatches(availableBatches);
14965
15020
  }
14966
15021
  }
15022
+ cancel() {
15023
+ this.networkWorkerPool.cancel();
15024
+ }
14967
15025
  // parallelizes batches of priming work
14968
15026
  enqueueBatches(batches) {
14969
15027
  for (const batch of batches) {
14970
15028
  this.networkWorkerPool.push({
14971
- workFn: () => {
14972
- return this.recordLoader.fetchRecordData(batch).then(async (result) => {
15029
+ workFn: (abortController) => {
15030
+ return this.recordLoader
15031
+ .fetchRecordData(batch, abortController)
15032
+ .then(async (result) => {
15033
+ if (abortController.signal.aborted) {
15034
+ return;
15035
+ }
14973
15036
  if (result.ok === false) {
14974
15037
  const { error } = result;
14975
15038
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -14993,6 +15056,9 @@ class PrimingSession extends EventEmitter {
14993
15056
  this.recordIngestor
14994
15057
  .insertRecords(records)
14995
15058
  .then(({ written, conflicted }) => {
15059
+ if (abortController.signal.aborted) {
15060
+ return;
15061
+ }
14996
15062
  // now that the records are persisted, emit the primed event
14997
15063
  if (written.length > 0) {
14998
15064
  this.emit('primed', Array.from(written));
@@ -15009,6 +15075,13 @@ class PrimingSession extends EventEmitter {
15009
15075
  });
15010
15076
  });
15011
15077
  },
15078
+ cancelFn: () => {
15079
+ this.emit('error', {
15080
+ ids: batch.ids,
15081
+ code: 'canceled',
15082
+ message: `batch canceled`,
15083
+ });
15084
+ },
15012
15085
  });
15013
15086
  }
15014
15087
  }
@@ -15034,8 +15107,6 @@ class PrimingSession extends EventEmitter {
15034
15107
  }
15035
15108
 
15036
15109
  const requiredPrefix = `required_`;
15037
- // note this is automatically incremented by scripts/release/bump-api-version.js at each release
15038
- const apiVersion = `v58.0`;
15039
15110
  const requiredFieldMap = {
15040
15111
  ApiName: 'ApiName',
15041
15112
  Id: 'Id',
@@ -15049,10 +15120,10 @@ class RecordLoaderGraphQL {
15049
15120
  constructor(networkAdapter) {
15050
15121
  this.networkAdapter = networkAdapter;
15051
15122
  }
15052
- async fetchRecordData(batch) {
15123
+ async fetchRecordData(batch, abortController) {
15053
15124
  let rep;
15054
15125
  try {
15055
- rep = await this.callGraphQL(batch);
15126
+ rep = await this.callGraphQL(batch, abortController);
15056
15127
  }
15057
15128
  catch (e) {
15058
15129
  return {
@@ -15094,25 +15165,9 @@ class RecordLoaderGraphQL {
15094
15165
  missingIds: Array.from(seenRecords),
15095
15166
  };
15096
15167
  }
15097
- async callGraphQL(batch) {
15168
+ callGraphQL(batch, abortController) {
15098
15169
  const query = this.generateGraphQLQuery(batch.type, batch.fields);
15099
- const response = await this.networkAdapter({
15100
- baseUri: `/services/data/${apiVersion}`,
15101
- basePath: '/graphql',
15102
- method: 'POST',
15103
- priority: 'background',
15104
- body: {
15105
- query,
15106
- variables: {
15107
- ids: batch.ids,
15108
- first: batch.ids.length,
15109
- },
15110
- },
15111
- queryParams: {},
15112
- urlParams: {},
15113
- headers: {},
15114
- }, {});
15115
- return response.body;
15170
+ return this.networkAdapter.postGraphQL(query, { ids: batch.ids, first: batch.ids.length }, abortController);
15116
15171
  }
15117
15172
  generateGraphQLQuery(type, fields) {
15118
15173
  const fieldList = Object.keys(requiredFieldMap)
@@ -15248,8 +15303,57 @@ function instrumentPrimingSession(session) {
15248
15303
  return session;
15249
15304
  }
15250
15305
 
15306
+ // so eslint doesn't complain about nimbus
15307
+ /* global __nimbus */
15308
+ // note this is automatically incremented by scripts/release/bump-api-version.js at each release
15309
+ const apiVersion = `v58.0`;
15310
+ class NimbusPrimingNetworkAdapter {
15311
+ postGraphQL(query, variables, abortController) {
15312
+ return new Promise((resolve, reject) => {
15313
+ let listener;
15314
+ const unregisterListener = () => {
15315
+ if (listener) {
15316
+ abortController.signal.removeEventListener('abort', listener);
15317
+ }
15318
+ };
15319
+ __nimbus.plugins.LdsNetworkAdapter
15320
+ .sendRequest({
15321
+ method: 'POST',
15322
+ path: `/services/data/${apiVersion}/graphql`,
15323
+ body: JSON.stringify({
15324
+ query,
15325
+ variables,
15326
+ }),
15327
+ headers: {},
15328
+ queryParams: {},
15329
+ priority: 'background',
15330
+ observabilityContext: {},
15331
+ }, (response) => {
15332
+ unregisterListener();
15333
+ const { body } = response;
15334
+ if (body) {
15335
+ resolve(JSON.parse(body));
15336
+ }
15337
+ else {
15338
+ reject(new Error('No body returned from graphql endpoint'));
15339
+ }
15340
+ }, (error) => {
15341
+ unregisterListener();
15342
+ reject(error);
15343
+ })
15344
+ .then((cancellationToken) => {
15345
+ listener = () => {
15346
+ __nimbus.plugins.LdsNetworkAdapter.cancelRequest(cancellationToken);
15347
+ };
15348
+ abortController.signal.addEventListener('abort', listener);
15349
+ });
15350
+ });
15351
+ }
15352
+ }
15353
+
15251
15354
  function primingSessionFactory(config) {
15252
- const { networkAdapter, store, objectInfoService, getLuvio } = config;
15355
+ const { store, objectInfoService, getLuvio } = config;
15356
+ const networkAdapter = new NimbusPrimingNetworkAdapter();
15253
15357
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
15254
15358
  const recordIngestor = new RecordIngestor(store, getLuvio);
15255
15359
  const session = new PrimingSession({
@@ -15375,7 +15479,6 @@ function getRuntime() {
15375
15479
  createPrimingSession: (config) => {
15376
15480
  return primingSessionFactory({
15377
15481
  store: lazyBaseDurableStore,
15378
- networkAdapter: lazyNetworkAdapter,
15379
15482
  objectInfoService: lazyObjectInfoService,
15380
15483
  getLuvio: () => lazyLuvio,
15381
15484
  concurrency: config.concurrency,
@@ -15398,4 +15501,4 @@ register({
15398
15501
  });
15399
15502
 
15400
15503
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15401
- // version: 1.110.2-c54b4a458
15504
+ // version: 1.111.1-dfc59aa0e
@@ -0,0 +1,6 @@
1
+ import type { PrimingNetworkAdapter } from '@salesforce/lds-priming';
2
+ import type { GraphQLRepresentation } from '@salesforce/lds-adapters-uiapi';
3
+ import type { LdsAbortController } from '@salesforce/lds-utils-adapters';
4
+ export declare class NimbusPrimingNetworkAdapter implements PrimingNetworkAdapter {
5
+ postGraphQL(query: string, variables: Record<string, any>, abortController: LdsAbortController): Promise<GraphQLRepresentation>;
6
+ }
@@ -1,10 +1,9 @@
1
1
  import { PrimingSession } from '@salesforce/lds-priming';
2
- import type { Luvio, NetworkAdapter } from '@luvio/engine';
2
+ import type { Luvio } from '@luvio/engine';
3
3
  import type { NimbusSqliteStore } from '../durableStore/NimbusSqliteStore/NimbusSqliteStore';
4
4
  import type { ObjectInfoService } from '../main';
5
5
  export interface PrimingSessionFactoryConfig {
6
6
  store: NimbusSqliteStore;
7
- networkAdapter: NetworkAdapter;
8
7
  objectInfoService: ObjectInfoService;
9
8
  getLuvio: () => Luvio;
10
9
  concurrency?: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.110.2",
3
+ "version": "1.111.1",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -32,27 +32,27 @@
32
32
  "release:corejar": "yarn build && ../core-build/scripts/core.js --name=lds-runtime-mobile"
33
33
  },
34
34
  "dependencies": {
35
- "@salesforce/lds-adapters-uiapi": "^1.110.2",
36
- "@salesforce/lds-bindings": "^1.110.2",
37
- "@salesforce/lds-instrumentation": "^1.110.2",
38
- "@salesforce/lds-priming": "^1.110.2",
35
+ "@salesforce/lds-adapters-uiapi": "^1.111.1",
36
+ "@salesforce/lds-bindings": "^1.111.1",
37
+ "@salesforce/lds-instrumentation": "^1.111.1",
38
+ "@salesforce/lds-priming": "^1.111.1",
39
39
  "@salesforce/user": "0.0.12",
40
40
  "o11y": "244.0.0"
41
41
  },
42
42
  "devDependencies": {
43
- "@luvio/engine": "0.135.4",
44
- "@luvio/environments": "0.135.4",
45
- "@luvio/graphql-parser": "0.135.4",
46
- "@salesforce/lds-adapters-graphql": "^1.110.2",
47
- "@salesforce/lds-drafts": "^1.110.2",
48
- "@salesforce/lds-drafts-adapters-uiapi": "^1.110.2",
49
- "@salesforce/lds-graphql-eval": "^1.110.2",
50
- "@salesforce/lds-network-adapter": "^1.110.2",
51
- "@salesforce/lds-network-nimbus": "^1.110.2",
52
- "@salesforce/lds-store-binary": "^1.110.2",
53
- "@salesforce/lds-store-sql": "^1.110.2",
54
- "@salesforce/lds-utils-adapters": "^1.110.2",
55
- "@salesforce/nimbus-plugin-lds": "^1.110.2",
43
+ "@luvio/engine": "0.136.5",
44
+ "@luvio/environments": "0.136.5",
45
+ "@luvio/graphql-parser": "0.136.5",
46
+ "@salesforce/lds-adapters-graphql": "^1.111.1",
47
+ "@salesforce/lds-drafts": "^1.111.1",
48
+ "@salesforce/lds-drafts-adapters-uiapi": "^1.111.1",
49
+ "@salesforce/lds-graphql-eval": "^1.111.1",
50
+ "@salesforce/lds-network-adapter": "^1.111.1",
51
+ "@salesforce/lds-network-nimbus": "^1.111.1",
52
+ "@salesforce/lds-store-binary": "^1.111.1",
53
+ "@salesforce/lds-store-sql": "^1.111.1",
54
+ "@salesforce/lds-utils-adapters": "^1.111.1",
55
+ "@salesforce/nimbus-plugin-lds": "^1.111.1",
56
56
  "babel-plugin-dynamic-import-node": "^2.3.3",
57
57
  "wait-for-expect": "^3.0.2"
58
58
  },
package/sfdc/main.js CHANGED
@@ -1055,10 +1055,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1055
1055
  };
1056
1056
  const storeBroadcast = function (_rebuildSnapshot, _snapshotDataAvailable) {
1057
1057
  validateNotDisposed();
1058
- // return resolved promise here and wait for the L2 flush to happen in handleSuccessResponse,
1059
- // that flush will cause the onChanged handler to fire which will revive
1060
- // records to the main L1 store and call the base storeBroadcast
1061
- return Promise.resolve();
1058
+ // publishing to L2 is essentially "broadcasting" because the onChanged
1059
+ // handler will fire which will revive records to the main L1 store and
1060
+ // call the base storeBroadcast
1061
+ return publishChangesToDurableStore();
1062
1062
  };
1063
1063
  const publishChangesToDurableStore = function () {
1064
1064
  validateNotDisposed();
@@ -1279,7 +1279,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1279
1279
  // we don't need to prime metadata
1280
1280
  () => { });
1281
1281
  snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
1282
- await publishChangesToDurableStore();
1283
1282
  })();
1284
1283
  for (const key of keysToReviveAsArray) {
1285
1284
  // we are overwriting the previous promise at this key, but that
@@ -1307,7 +1306,6 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1307
1306
  // so all we have to do is ingest then write to L2
1308
1307
  ingestStagingStore = buildIngestStagingStore(environment);
1309
1308
  snapshotFromMemoryIngest = await ingestAndBroadcastFunc();
1310
- await publishChangesToDurableStore();
1311
1309
  }
1312
1310
  if (snapshotFromMemoryIngest === undefined) {
1313
1311
  return undefined;
@@ -1320,12 +1318,10 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1320
1318
  const result = await reviveSnapshot(environment, durableStore, snapshotFromMemoryIngest, durableStoreErrorHandler, () => environment.storeLookup(select, environment.createSnapshot, refresh));
1321
1319
  return result.snapshot;
1322
1320
  };
1323
- const handleErrorResponse = function (ingestAndBroadcastFunc) {
1321
+ const handleErrorResponse = async function (ingestAndBroadcastFunc) {
1324
1322
  validateNotDisposed();
1325
- const snapshotFromMemoryIngest = ingestAndBroadcastFunc();
1326
- return publishChangesToDurableStore().then(() => {
1327
- return snapshotFromMemoryIngest;
1328
- });
1323
+ ingestStagingStore = buildIngestStagingStore(environment);
1324
+ return ingestAndBroadcastFunc();
1329
1325
  };
1330
1326
  const getNotifyChangeStoreEntries = function (keys) {
1331
1327
  validateNotDisposed();
@@ -4350,6 +4346,58 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4350
4346
  * For full license text, see the LICENSE.txt file
4351
4347
  */
4352
4348
 
4349
+ /* Ideally we would use AbortController but it does not exist in V8, if it is ever polyfilled we can swap for it */
4350
+ class LdsAbortController {
4351
+ constructor() {
4352
+ this.signal = new AbortSignal();
4353
+ }
4354
+ abort() {
4355
+ this.signal.abort();
4356
+ }
4357
+ }
4358
+ class AbortSignal {
4359
+ constructor() {
4360
+ this._aborted = false;
4361
+ this.listeners = new Map();
4362
+ }
4363
+ get aborted() {
4364
+ return this._aborted;
4365
+ }
4366
+ addEventListener(type, listener) {
4367
+ let listeners = this.listeners.get(type);
4368
+ if (!listeners) {
4369
+ listeners = new Set();
4370
+ this.listeners.set(type, listeners);
4371
+ }
4372
+ listeners.add(listener);
4373
+ }
4374
+ removeEventListener(type, listener) {
4375
+ const listeners = this.listeners.get(type);
4376
+ if (listeners) {
4377
+ listeners.delete(listener);
4378
+ if (listeners.size === 0) {
4379
+ this.listeners.delete(type);
4380
+ }
4381
+ }
4382
+ }
4383
+ dispatchEvent(event) {
4384
+ const listeners = this.listeners.get(event.type);
4385
+ if (listeners) {
4386
+ for (const listener of listeners) {
4387
+ listener(this, event);
4388
+ }
4389
+ }
4390
+ return !event.defaultPrevented;
4391
+ }
4392
+ abort() {
4393
+ if (!this.aborted) {
4394
+ this._aborted = true;
4395
+ const abortEvent = new Event('abort');
4396
+ this.dispatchEvent(abortEvent);
4397
+ }
4398
+ }
4399
+ }
4400
+
4353
4401
  class AsyncWorkerPool {
4354
4402
  constructor(concurrency) {
4355
4403
  this.queue = [];
@@ -4360,51 +4408,57 @@ class AsyncWorkerPool {
4360
4408
  return new Promise((resolve, reject) => {
4361
4409
  this.queue.push({
4362
4410
  ...work,
4363
- workFn: () => work.workFn().then(resolve).catch(reject),
4411
+ workFn: (abortController) => work.workFn(abortController).then(resolve).catch(reject),
4364
4412
  });
4365
4413
  this.doWork();
4366
4414
  });
4367
4415
  }
4368
- async cancel() {
4369
- const promises = [];
4370
- const cancellingWork = [
4371
- ...this.activeWork.splice(0, this.activeWork.length),
4372
- ...this.queue.splice(0, this.queue.length),
4373
- ];
4374
- cancellingWork.forEach((work) => {
4375
- const { cancelFn } = work;
4416
+ /**
4417
+ * cancel all work in the queue and active work
4418
+ * @returns true if all work was cancelled, false if any work could not be cancelled
4419
+ */
4420
+ cancel() {
4421
+ let success = true;
4422
+ for (const { cancelFn } of this.queue) {
4423
+ if (cancelFn) {
4424
+ try {
4425
+ cancelFn();
4426
+ }
4427
+ catch (_a) {
4428
+ success = false;
4429
+ }
4430
+ }
4431
+ }
4432
+ this.queue = [];
4433
+ for (const { abortController, cancelFn } of this.activeWork) {
4434
+ abortController.abort();
4376
4435
  if (cancelFn) {
4377
- promises.push(cancelFn());
4436
+ try {
4437
+ cancelFn();
4438
+ }
4439
+ catch (_b) {
4440
+ success = false;
4441
+ }
4378
4442
  }
4379
- });
4380
- await promiseAllSettled(promises);
4443
+ }
4444
+ this.activeWork = [];
4445
+ return success;
4381
4446
  }
4382
4447
  doWork() {
4383
4448
  while (this.queue.length > 0 && this.activeWork.length < this.concurrency) {
4384
4449
  const work = this.queue.shift();
4385
4450
  if (work) {
4386
- this.activeWork.push(work);
4451
+ const abortController = new LdsAbortController();
4452
+ const newWork = { ...work, abortController };
4453
+ this.activeWork.push(newWork);
4387
4454
  const { workFn } = work;
4388
- workFn()
4389
- .then()
4390
- .finally(() => {
4391
- this.activeWork = this.activeWork.filter((w) => w !== work);
4455
+ workFn(abortController).finally(() => {
4456
+ this.activeWork = this.activeWork.filter((w) => w !== newWork);
4392
4457
  this.doWork();
4393
4458
  });
4394
4459
  }
4395
4460
  }
4396
4461
  }
4397
- }
4398
- function promiseAllSettled(promises) {
4399
- return Promise.all(promises.map((promise) => promise
4400
- .then((value) => ({
4401
- status: 'fulfilled',
4402
- value,
4403
- }))
4404
- .catch((reason) => ({
4405
- status: 'rejected',
4406
- reason,
4407
- }))));
4408
4462
  }
4409
4463
 
4410
4464
  /**
@@ -14934,9 +14988,10 @@ const DEFAULT_BATCH_SIZE = 500;
14934
14988
  const DEFAULT_CONCURRENCY = 6;
14935
14989
  class PrimingSession extends EventEmitter {
14936
14990
  constructor(config) {
14991
+ var _a, _b;
14937
14992
  super();
14938
- this.batchSize = config.batchSize || DEFAULT_BATCH_SIZE;
14939
- this.concurrency = config.concurrency || DEFAULT_CONCURRENCY;
14993
+ this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
14994
+ this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
14940
14995
  this.recordLoader = config.recordLoader;
14941
14996
  this.recordIngestor = config.recordIngestor;
14942
14997
  this.objectInfoLoader = config.objectInfoLoader;
@@ -14964,12 +15019,20 @@ class PrimingSession extends EventEmitter {
14964
15019
  this.enqueueBatches(availableBatches);
14965
15020
  }
14966
15021
  }
15022
+ cancel() {
15023
+ this.networkWorkerPool.cancel();
15024
+ }
14967
15025
  // parallelizes batches of priming work
14968
15026
  enqueueBatches(batches) {
14969
15027
  for (const batch of batches) {
14970
15028
  this.networkWorkerPool.push({
14971
- workFn: () => {
14972
- return this.recordLoader.fetchRecordData(batch).then(async (result) => {
15029
+ workFn: (abortController) => {
15030
+ return this.recordLoader
15031
+ .fetchRecordData(batch, abortController)
15032
+ .then(async (result) => {
15033
+ if (abortController.signal.aborted) {
15034
+ return;
15035
+ }
14973
15036
  if (result.ok === false) {
14974
15037
  const { error } = result;
14975
15038
  const primingError = error === 'network-error' ? 'service-unavailable' : 'unknown';
@@ -14993,6 +15056,9 @@ class PrimingSession extends EventEmitter {
14993
15056
  this.recordIngestor
14994
15057
  .insertRecords(records)
14995
15058
  .then(({ written, conflicted }) => {
15059
+ if (abortController.signal.aborted) {
15060
+ return;
15061
+ }
14996
15062
  // now that the records are persisted, emit the primed event
14997
15063
  if (written.length > 0) {
14998
15064
  this.emit('primed', Array.from(written));
@@ -15009,6 +15075,13 @@ class PrimingSession extends EventEmitter {
15009
15075
  });
15010
15076
  });
15011
15077
  },
15078
+ cancelFn: () => {
15079
+ this.emit('error', {
15080
+ ids: batch.ids,
15081
+ code: 'canceled',
15082
+ message: `batch canceled`,
15083
+ });
15084
+ },
15012
15085
  });
15013
15086
  }
15014
15087
  }
@@ -15034,8 +15107,6 @@ class PrimingSession extends EventEmitter {
15034
15107
  }
15035
15108
 
15036
15109
  const requiredPrefix = `required_`;
15037
- // note this is automatically incremented by scripts/release/bump-api-version.js at each release
15038
- const apiVersion = `v58.0`;
15039
15110
  const requiredFieldMap = {
15040
15111
  ApiName: 'ApiName',
15041
15112
  Id: 'Id',
@@ -15049,10 +15120,10 @@ class RecordLoaderGraphQL {
15049
15120
  constructor(networkAdapter) {
15050
15121
  this.networkAdapter = networkAdapter;
15051
15122
  }
15052
- async fetchRecordData(batch) {
15123
+ async fetchRecordData(batch, abortController) {
15053
15124
  let rep;
15054
15125
  try {
15055
- rep = await this.callGraphQL(batch);
15126
+ rep = await this.callGraphQL(batch, abortController);
15056
15127
  }
15057
15128
  catch (e) {
15058
15129
  return {
@@ -15094,25 +15165,9 @@ class RecordLoaderGraphQL {
15094
15165
  missingIds: Array.from(seenRecords),
15095
15166
  };
15096
15167
  }
15097
- async callGraphQL(batch) {
15168
+ callGraphQL(batch, abortController) {
15098
15169
  const query = this.generateGraphQLQuery(batch.type, batch.fields);
15099
- const response = await this.networkAdapter({
15100
- baseUri: `/services/data/${apiVersion}`,
15101
- basePath: '/graphql',
15102
- method: 'POST',
15103
- priority: 'background',
15104
- body: {
15105
- query,
15106
- variables: {
15107
- ids: batch.ids,
15108
- first: batch.ids.length,
15109
- },
15110
- },
15111
- queryParams: {},
15112
- urlParams: {},
15113
- headers: {},
15114
- }, {});
15115
- return response.body;
15170
+ return this.networkAdapter.postGraphQL(query, { ids: batch.ids, first: batch.ids.length }, abortController);
15116
15171
  }
15117
15172
  generateGraphQLQuery(type, fields) {
15118
15173
  const fieldList = Object.keys(requiredFieldMap)
@@ -15248,8 +15303,57 @@ function instrumentPrimingSession(session) {
15248
15303
  return session;
15249
15304
  }
15250
15305
 
15306
+ // so eslint doesn't complain about nimbus
15307
+ /* global __nimbus */
15308
+ // note this is automatically incremented by scripts/release/bump-api-version.js at each release
15309
+ const apiVersion = `v58.0`;
15310
+ class NimbusPrimingNetworkAdapter {
15311
+ postGraphQL(query, variables, abortController) {
15312
+ return new Promise((resolve, reject) => {
15313
+ let listener;
15314
+ const unregisterListener = () => {
15315
+ if (listener) {
15316
+ abortController.signal.removeEventListener('abort', listener);
15317
+ }
15318
+ };
15319
+ __nimbus.plugins.LdsNetworkAdapter
15320
+ .sendRequest({
15321
+ method: 'POST',
15322
+ path: `/services/data/${apiVersion}/graphql`,
15323
+ body: JSON.stringify({
15324
+ query,
15325
+ variables,
15326
+ }),
15327
+ headers: {},
15328
+ queryParams: {},
15329
+ priority: 'background',
15330
+ observabilityContext: {},
15331
+ }, (response) => {
15332
+ unregisterListener();
15333
+ const { body } = response;
15334
+ if (body) {
15335
+ resolve(JSON.parse(body));
15336
+ }
15337
+ else {
15338
+ reject(new Error('No body returned from graphql endpoint'));
15339
+ }
15340
+ }, (error) => {
15341
+ unregisterListener();
15342
+ reject(error);
15343
+ })
15344
+ .then((cancellationToken) => {
15345
+ listener = () => {
15346
+ __nimbus.plugins.LdsNetworkAdapter.cancelRequest(cancellationToken);
15347
+ };
15348
+ abortController.signal.addEventListener('abort', listener);
15349
+ });
15350
+ });
15351
+ }
15352
+ }
15353
+
15251
15354
  function primingSessionFactory(config) {
15252
- const { networkAdapter, store, objectInfoService, getLuvio } = config;
15355
+ const { store, objectInfoService, getLuvio } = config;
15356
+ const networkAdapter = new NimbusPrimingNetworkAdapter();
15253
15357
  const recordLoader = new RecordLoaderGraphQL(networkAdapter);
15254
15358
  const recordIngestor = new RecordIngestor(store, getLuvio);
15255
15359
  const session = new PrimingSession({
@@ -15375,7 +15479,6 @@ function getRuntime() {
15375
15479
  createPrimingSession: (config) => {
15376
15480
  return primingSessionFactory({
15377
15481
  store: lazyBaseDurableStore,
15378
- networkAdapter: lazyNetworkAdapter,
15379
15482
  objectInfoService: lazyObjectInfoService,
15380
15483
  getLuvio: () => lazyLuvio,
15381
15484
  concurrency: config.concurrency,
@@ -15398,4 +15501,4 @@ register({
15398
15501
  });
15399
15502
 
15400
15503
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15401
- // version: 1.110.2-c54b4a458
15504
+ // version: 1.111.1-dfc59aa0e
@@ -0,0 +1,6 @@
1
+ import type { PrimingNetworkAdapter } from '@salesforce/lds-priming';
2
+ import type { GraphQLRepresentation } from '@salesforce/lds-adapters-uiapi';
3
+ import type { LdsAbortController } from '@salesforce/lds-utils-adapters';
4
+ export declare class NimbusPrimingNetworkAdapter implements PrimingNetworkAdapter {
5
+ postGraphQL(query: string, variables: Record<string, any>, abortController: LdsAbortController): Promise<GraphQLRepresentation>;
6
+ }
@@ -1,10 +1,9 @@
1
1
  import { PrimingSession } from '@salesforce/lds-priming';
2
- import type { Luvio, NetworkAdapter } from '@luvio/engine';
2
+ import type { Luvio } from '@luvio/engine';
3
3
  import type { NimbusSqliteStore } from '../durableStore/NimbusSqliteStore/NimbusSqliteStore';
4
4
  import type { ObjectInfoService } from '../main';
5
5
  export interface PrimingSessionFactoryConfig {
6
6
  store: NimbusSqliteStore;
7
- networkAdapter: NetworkAdapter;
8
7
  objectInfoService: ObjectInfoService;
9
8
  getLuvio: () => Luvio;
10
9
  concurrency?: number;