@salesforce/lds-runtime-mobile 1.100.3 → 1.100.4

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.
Files changed (3) hide show
  1. package/dist/main.js +172 -138
  2. package/package.json +15 -15
  3. package/sfdc/main.js +172 -138
package/dist/main.js CHANGED
@@ -4344,6 +4344,44 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4344
4344
  return wrapStartEndEvents(storeEval);
4345
4345
  }
4346
4346
 
4347
+ /**
4348
+ * Copyright (c) 2022, Salesforce, Inc.,
4349
+ * All rights reserved.
4350
+ * For full license text, see the LICENSE.txt file
4351
+ */
4352
+
4353
+ class AsyncWorkerPool {
4354
+ constructor(concurrency) {
4355
+ this.queue = [];
4356
+ this.activeWorkers = 0;
4357
+ this.concurrency = concurrency;
4358
+ }
4359
+ push(workFn) {
4360
+ return new Promise((resolve, reject) => {
4361
+ this.queue.push(async () => {
4362
+ return workFn().then(resolve).catch(reject);
4363
+ });
4364
+ this.work();
4365
+ });
4366
+ }
4367
+ cancel() {
4368
+ this.queue = [];
4369
+ // TODO [W-12513105]: thread cancellation through to active workers
4370
+ }
4371
+ work() {
4372
+ while (this.queue.length > 0 && this.activeWorkers < this.concurrency) {
4373
+ this.activeWorkers += 1;
4374
+ const next = this.queue.shift();
4375
+ if (next) {
4376
+ next().finally(() => {
4377
+ this.activeWorkers -= 1;
4378
+ this.work();
4379
+ });
4380
+ }
4381
+ }
4382
+ }
4383
+ }
4384
+
4347
4385
  /**
4348
4386
  * Copyright (c) 2022, Salesforce, Inc.,
4349
4387
  * All rights reserved.
@@ -4762,6 +4800,7 @@ class DurableDraftQueue {
4762
4800
  this.timeoutHandler = undefined;
4763
4801
  this.handlers = {};
4764
4802
  this.draftStore = draftStore;
4803
+ this.workerPool = new AsyncWorkerPool(1);
4765
4804
  }
4766
4805
  addHandler(handler) {
4767
4806
  const id = handler.handlerId;
@@ -4861,20 +4900,22 @@ class DurableDraftQueue {
4861
4900
  });
4862
4901
  }
4863
4902
  async enqueue(handlerId, data) {
4864
- let queue = await this.getQueueActions();
4865
- const handler = this.getHandler(handlerId);
4866
- const pendingAction = (await handler.buildPendingAction(data, queue));
4867
- await this.draftStore.writeAction(pendingAction);
4868
- queue = await this.getQueueActions();
4869
- await this.notifyChangedListeners({
4870
- type: DraftQueueEventType.ActionAdded,
4871
- action: pendingAction,
4903
+ return this.workerPool.push(async () => {
4904
+ let queue = await this.getQueueActions();
4905
+ const handler = this.getHandler(handlerId);
4906
+ const pendingAction = (await handler.buildPendingAction(data, queue));
4907
+ await this.draftStore.writeAction(pendingAction);
4908
+ queue = await this.getQueueActions();
4909
+ await this.notifyChangedListeners({
4910
+ type: DraftQueueEventType.ActionAdded,
4911
+ action: pendingAction,
4912
+ });
4913
+ await handler.handleActionEnqueued(pendingAction, queue);
4914
+ if (this.state === DraftQueueState.Started) {
4915
+ this.processNextAction();
4916
+ }
4917
+ return pendingAction;
4872
4918
  });
4873
- await handler.handleActionEnqueued(pendingAction, queue);
4874
- if (this.state === DraftQueueState.Started) {
4875
- this.processNextAction();
4876
- }
4877
- return pendingAction;
4878
4919
  }
4879
4920
  registerOnChangedListener(listener) {
4880
4921
  this.draftQueueChangedListeners.push(listener);
@@ -4886,27 +4927,29 @@ class DurableDraftQueue {
4886
4927
  };
4887
4928
  }
4888
4929
  async actionCompleted(action) {
4889
- const handler = this.getHandler(action.handler);
4890
- let queue = await this.getQueueActions();
4891
- const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
4892
- const idAndKeyMappings = handler.getRedirectMappings(action);
4893
- const keyMappings = idAndKeyMappings === undefined
4894
- ? undefined
4895
- : idAndKeyMappings.map((m) => {
4896
- return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
4930
+ return this.workerPool.push(async () => {
4931
+ const handler = this.getHandler(action.handler);
4932
+ let queue = await this.getQueueActions();
4933
+ const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
4934
+ const idAndKeyMappings = handler.getRedirectMappings(action);
4935
+ const keyMappings = idAndKeyMappings === undefined
4936
+ ? undefined
4937
+ : idAndKeyMappings.map((m) => {
4938
+ return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
4939
+ });
4940
+ await this.draftStore.completeAction(queueOperations, keyMappings);
4941
+ queue = await this.getQueueActions();
4942
+ this.retryIntervalMilliseconds = 0;
4943
+ this.uploadingActionId = undefined;
4944
+ await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
4945
+ await this.notifyChangedListeners({
4946
+ type: DraftQueueEventType.ActionCompleted,
4947
+ action,
4897
4948
  });
4898
- await this.draftStore.completeAction(queueOperations, keyMappings);
4899
- queue = await this.getQueueActions();
4900
- this.retryIntervalMilliseconds = 0;
4901
- this.uploadingActionId = undefined;
4902
- await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
4903
- await this.notifyChangedListeners({
4904
- type: DraftQueueEventType.ActionCompleted,
4905
- action,
4949
+ if (this.state === DraftQueueState.Started) {
4950
+ this.processNextAction();
4951
+ }
4906
4952
  });
4907
- if (this.state === DraftQueueState.Started) {
4908
- this.processNextAction();
4909
- }
4910
4953
  }
4911
4954
  async actionFailed(action, retry) {
4912
4955
  this.uploadingActionId = undefined;
@@ -5356,6 +5399,13 @@ class AbstractResourceRequestActionHandler {
5356
5399
  this.draftQueue = draftQueue;
5357
5400
  this.networkAdapter = networkAdapter;
5358
5401
  this.getLuvio = getLuvio;
5402
+ // NOTE[W-12567340]: This property stores in-memory mappings between draft
5403
+ // ids and canonical ids for the current session. Having a local copy of
5404
+ // these mappings is necessary to avoid a race condition between publishing
5405
+ // new mappings to the durable store and those mappings being loaded into
5406
+ // the luvio store redirect table, during which a new draft might be enqueued
5407
+ // which would not see a necessary mapping.
5408
+ this.ephemeralRedirects = {};
5359
5409
  }
5360
5410
  enqueue(data) {
5361
5411
  return this.draftQueue.enqueue(this.handlerId, data);
@@ -5423,7 +5473,8 @@ class AbstractResourceRequestActionHandler {
5423
5473
  }
5424
5474
  getQueueOperationsForCompletingDrafts(queue, action) {
5425
5475
  const queueOperations = [];
5426
- if (action.data.method === 'post') {
5476
+ const redirects = this.getRedirectMappings(action);
5477
+ if (redirects !== undefined) {
5427
5478
  const { length } = queue;
5428
5479
  for (let i = 0; i < length; i++) {
5429
5480
  const queueAction = queue[i];
@@ -5433,69 +5484,64 @@ class AbstractResourceRequestActionHandler {
5433
5484
  continue;
5434
5485
  }
5435
5486
  if (isResourceRequestAction(queueAction)) {
5436
- const redirects = this.getRedirectMappings(action);
5437
- if (redirects !== undefined) {
5438
- let queueOperationMutated = false;
5439
- let updatedActionTag = undefined;
5440
- let updatedActionTargetId = undefined;
5441
- const { tag: queueActionTag, data: queueActionRequest, id: queueActionId, } = queueAction;
5442
- let { basePath, body } = queueActionRequest;
5443
- let stringifiedBody = stringify$4(body);
5444
- // for each redirected ID/key we loop over the operation to see if it needs
5445
- // to be updated
5446
- for (const { draftId, draftKey, canonicalId, canonicalKey } of redirects) {
5447
- if (basePath.search(draftId) >= 0 ||
5448
- stringifiedBody.search(draftId) >= 0) {
5449
- basePath = basePath.replace(draftId, canonicalId);
5450
- stringifiedBody = stringifiedBody.replace(draftId, canonicalId);
5451
- queueOperationMutated = true;
5452
- }
5453
- // if the action is performed on a previous draft id, we need to replace the action
5454
- // with a new one at the updated canonical key
5455
- if (queueActionTag === draftKey) {
5456
- updatedActionTag = canonicalKey;
5457
- updatedActionTargetId = canonicalId;
5458
- }
5487
+ let queueOperationMutated = false;
5488
+ let updatedActionTag = undefined;
5489
+ let updatedActionTargetId = undefined;
5490
+ const { tag: queueActionTag, data: queueActionRequest, id: queueActionId, } = queueAction;
5491
+ let { basePath, body } = queueActionRequest;
5492
+ let stringifiedBody = stringify$4(body);
5493
+ // for each redirected ID/key we loop over the operation to see if it needs
5494
+ // to be updated
5495
+ for (const { draftId, draftKey, canonicalId, canonicalKey } of redirects) {
5496
+ if (basePath.search(draftId) >= 0 || stringifiedBody.search(draftId) >= 0) {
5497
+ basePath = basePath.replace(draftId, canonicalId);
5498
+ stringifiedBody = stringifiedBody.replace(draftId, canonicalId);
5499
+ queueOperationMutated = true;
5459
5500
  }
5460
- if (queueOperationMutated) {
5461
- if (updatedActionTag !== undefined &&
5462
- updatedActionTargetId !== undefined) {
5463
- const updatedAction = {
5464
- ...queueAction,
5465
- tag: updatedActionTag,
5466
- targetId: updatedActionTargetId,
5467
- data: {
5468
- ...queueActionRequest,
5469
- basePath: basePath,
5470
- body: parse$4(stringifiedBody),
5471
- },
5472
- };
5473
- // item needs to be replaced with a new item at the new record key
5474
- queueOperations.push({
5475
- type: QueueOperationType.Delete,
5476
- id: queueActionId,
5477
- });
5478
- queueOperations.push({
5479
- type: QueueOperationType.Add,
5480
- action: updatedAction,
5481
- });
5482
- }
5483
- else {
5484
- const updatedAction = {
5485
- ...queueAction,
5486
- data: {
5487
- ...queueActionRequest,
5488
- basePath: basePath,
5489
- body: parse$4(stringifiedBody),
5490
- },
5491
- };
5492
- // item needs to be updated
5493
- queueOperations.push({
5494
- type: QueueOperationType.Update,
5495
- id: queueActionId,
5496
- action: updatedAction,
5497
- });
5498
- }
5501
+ // if the action is performed on a previous draft id, we need to replace the action
5502
+ // with a new one at the updated canonical key
5503
+ if (queueActionTag === draftKey) {
5504
+ updatedActionTag = canonicalKey;
5505
+ updatedActionTargetId = canonicalId;
5506
+ }
5507
+ }
5508
+ if (queueOperationMutated) {
5509
+ if (updatedActionTag !== undefined && updatedActionTargetId !== undefined) {
5510
+ const updatedAction = {
5511
+ ...queueAction,
5512
+ tag: updatedActionTag,
5513
+ targetId: updatedActionTargetId,
5514
+ data: {
5515
+ ...queueActionRequest,
5516
+ basePath: basePath,
5517
+ body: parse$4(stringifiedBody),
5518
+ },
5519
+ };
5520
+ // item needs to be replaced with a new item at the new record key
5521
+ queueOperations.push({
5522
+ type: QueueOperationType.Delete,
5523
+ id: queueActionId,
5524
+ });
5525
+ queueOperations.push({
5526
+ type: QueueOperationType.Add,
5527
+ action: updatedAction,
5528
+ });
5529
+ }
5530
+ else {
5531
+ const updatedAction = {
5532
+ ...queueAction,
5533
+ data: {
5534
+ ...queueActionRequest,
5535
+ basePath: basePath,
5536
+ body: parse$4(stringifiedBody),
5537
+ },
5538
+ };
5539
+ // item needs to be updated
5540
+ queueOperations.push({
5541
+ type: QueueOperationType.Update,
5542
+ id: queueActionId,
5543
+ action: updatedAction,
5544
+ });
5499
5545
  }
5500
5546
  }
5501
5547
  }
@@ -5515,6 +5561,9 @@ class AbstractResourceRequestActionHandler {
5515
5561
  const body = action.response.body;
5516
5562
  const canonicalId = this.getIdFromResponseBody(body);
5517
5563
  const draftId = action.targetId;
5564
+ if (draftId !== undefined && canonicalId !== undefined && draftId !== canonicalId) {
5565
+ this.ephemeralRedirects[draftId] = canonicalId;
5566
+ }
5518
5567
  return [
5519
5568
  {
5520
5569
  draftId,
@@ -11296,12 +11345,18 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
11296
11345
  const fieldName = fieldNames[i];
11297
11346
  const fieldValue = bodyFields[fieldName];
11298
11347
  if (typeof fieldValue === 'string' && this.isDraftId(fieldValue)) {
11299
- const draftKey = this.buildTagForTargetId(fieldValue);
11300
- const canonicalKey = this.getLuvio().storeGetCanonicalKey(draftKey);
11301
- if (draftKey !== canonicalKey) {
11302
- const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11303
- if (canonicalId !== undefined) {
11304
- bodyFields[fieldName] = canonicalId;
11348
+ const canonicalId = this.ephemeralRedirects[fieldValue];
11349
+ if (canonicalId !== undefined) {
11350
+ bodyFields[fieldName] = canonicalId;
11351
+ }
11352
+ else {
11353
+ const draftKey = this.buildTagForTargetId(fieldValue);
11354
+ const canonicalKey = this.getLuvio().storeGetCanonicalKey(draftKey);
11355
+ if (draftKey !== canonicalKey) {
11356
+ const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11357
+ if (canonicalId !== undefined) {
11358
+ bodyFields[fieldName] = canonicalId;
11359
+ }
11305
11360
  }
11306
11361
  }
11307
11362
  }
@@ -11309,17 +11364,24 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
11309
11364
  }
11310
11365
  if (request.method === 'patch' || request.method === 'delete') {
11311
11366
  const recordId = request.urlParams['recordId'];
11312
- const recordKey = this.buildTagForTargetId(recordId);
11313
- const canonicalKey = this.getLuvio().storeGetCanonicalKey(recordKey);
11314
- if (recordKey !== canonicalKey) {
11315
- const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11316
- if (canonicalId === undefined) {
11317
- // could not resolve id from request -- return the request un-modified
11318
- return request;
11319
- }
11367
+ const canonicalId = this.ephemeralRedirects[recordId];
11368
+ if (canonicalId !== undefined) {
11320
11369
  resolvedBasePath = resolvedBasePath.replace(recordId, canonicalId);
11321
11370
  resolvedUrlParams = { ...resolvedUrlParams, recordId: canonicalId };
11322
11371
  }
11372
+ else {
11373
+ const recordKey = this.buildTagForTargetId(recordId);
11374
+ const canonicalKey = this.getLuvio().storeGetCanonicalKey(recordKey);
11375
+ if (recordKey !== canonicalKey) {
11376
+ const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11377
+ if (canonicalId === undefined) {
11378
+ // could not resolve id from request -- return the request un-modified
11379
+ return request;
11380
+ }
11381
+ resolvedBasePath = resolvedBasePath.replace(recordId, canonicalId);
11382
+ resolvedUrlParams = { ...resolvedUrlParams, recordId: canonicalId };
11383
+ }
11384
+ }
11323
11385
  }
11324
11386
  return {
11325
11387
  ...request,
@@ -14667,34 +14729,6 @@ function setupInspection(luvio) {
14667
14729
  * For full license text, see the LICENSE.txt file
14668
14730
  */
14669
14731
 
14670
- class AsyncWorkerPool {
14671
- constructor(concurrency) {
14672
- this.queue = [];
14673
- this.activeWorkers = 0;
14674
- this.concurrency = concurrency;
14675
- }
14676
- push(workFn) {
14677
- this.queue.push(workFn);
14678
- this.work();
14679
- }
14680
- cancel() {
14681
- this.queue = [];
14682
- // TODO [W-12513105]: thread cancellation through to active workers
14683
- }
14684
- work() {
14685
- while (this.queue.length > 0 && this.activeWorkers < this.concurrency) {
14686
- this.activeWorkers += 1;
14687
- const next = this.queue.shift();
14688
- if (next) {
14689
- next().finally(() => {
14690
- this.activeWorkers -= 1;
14691
- this.work();
14692
- });
14693
- }
14694
- }
14695
- }
14696
- }
14697
-
14698
14732
  class EventEmitter {
14699
14733
  constructor() {
14700
14734
  // @ts-ignore typescript doesn't like us setting this to an empty object for some reason
@@ -15247,4 +15281,4 @@ register({
15247
15281
  });
15248
15282
 
15249
15283
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15250
- // version: 1.100.3-8176c84f6
15284
+ // version: 1.100.4-ce1be23b7
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.100.3",
3
+ "version": "1.100.4",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -32,10 +32,10 @@
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.100.3",
36
- "@salesforce/lds-bindings": "^1.100.3",
37
- "@salesforce/lds-instrumentation": "^1.100.3",
38
- "@salesforce/lds-priming": "^1.100.3",
35
+ "@salesforce/lds-adapters-uiapi": "^1.100.4",
36
+ "@salesforce/lds-bindings": "^1.100.4",
37
+ "@salesforce/lds-instrumentation": "^1.100.4",
38
+ "@salesforce/lds-priming": "^1.100.4",
39
39
  "@salesforce/user": "0.0.12",
40
40
  "o11y": "244.0.0"
41
41
  },
@@ -43,16 +43,16 @@
43
43
  "@luvio/engine": "0.135.4",
44
44
  "@luvio/environments": "0.135.4",
45
45
  "@luvio/graphql-parser": "0.135.4",
46
- "@salesforce/lds-adapters-graphql": "^1.100.3",
47
- "@salesforce/lds-drafts": "^1.100.3",
48
- "@salesforce/lds-drafts-adapters-uiapi": "^1.100.3",
49
- "@salesforce/lds-graphql-eval": "^1.100.3",
50
- "@salesforce/lds-network-adapter": "^1.100.3",
51
- "@salesforce/lds-network-nimbus": "^1.100.3",
52
- "@salesforce/lds-store-binary": "^1.100.3",
53
- "@salesforce/lds-store-sql": "^1.100.3",
54
- "@salesforce/lds-utils-adapters": "^1.100.3",
55
- "@salesforce/nimbus-plugin-lds": "^1.100.3",
46
+ "@salesforce/lds-adapters-graphql": "^1.100.4",
47
+ "@salesforce/lds-drafts": "^1.100.4",
48
+ "@salesforce/lds-drafts-adapters-uiapi": "^1.100.4",
49
+ "@salesforce/lds-graphql-eval": "^1.100.4",
50
+ "@salesforce/lds-network-adapter": "^1.100.4",
51
+ "@salesforce/lds-network-nimbus": "^1.100.4",
52
+ "@salesforce/lds-store-binary": "^1.100.4",
53
+ "@salesforce/lds-store-sql": "^1.100.4",
54
+ "@salesforce/lds-utils-adapters": "^1.100.4",
55
+ "@salesforce/nimbus-plugin-lds": "^1.100.4",
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
@@ -4344,6 +4344,44 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4344
4344
  return wrapStartEndEvents(storeEval);
4345
4345
  }
4346
4346
 
4347
+ /**
4348
+ * Copyright (c) 2022, Salesforce, Inc.,
4349
+ * All rights reserved.
4350
+ * For full license text, see the LICENSE.txt file
4351
+ */
4352
+
4353
+ class AsyncWorkerPool {
4354
+ constructor(concurrency) {
4355
+ this.queue = [];
4356
+ this.activeWorkers = 0;
4357
+ this.concurrency = concurrency;
4358
+ }
4359
+ push(workFn) {
4360
+ return new Promise((resolve, reject) => {
4361
+ this.queue.push(async () => {
4362
+ return workFn().then(resolve).catch(reject);
4363
+ });
4364
+ this.work();
4365
+ });
4366
+ }
4367
+ cancel() {
4368
+ this.queue = [];
4369
+ // TODO [W-12513105]: thread cancellation through to active workers
4370
+ }
4371
+ work() {
4372
+ while (this.queue.length > 0 && this.activeWorkers < this.concurrency) {
4373
+ this.activeWorkers += 1;
4374
+ const next = this.queue.shift();
4375
+ if (next) {
4376
+ next().finally(() => {
4377
+ this.activeWorkers -= 1;
4378
+ this.work();
4379
+ });
4380
+ }
4381
+ }
4382
+ }
4383
+ }
4384
+
4347
4385
  /**
4348
4386
  * Copyright (c) 2022, Salesforce, Inc.,
4349
4387
  * All rights reserved.
@@ -4762,6 +4800,7 @@ class DurableDraftQueue {
4762
4800
  this.timeoutHandler = undefined;
4763
4801
  this.handlers = {};
4764
4802
  this.draftStore = draftStore;
4803
+ this.workerPool = new AsyncWorkerPool(1);
4765
4804
  }
4766
4805
  addHandler(handler) {
4767
4806
  const id = handler.handlerId;
@@ -4861,20 +4900,22 @@ class DurableDraftQueue {
4861
4900
  });
4862
4901
  }
4863
4902
  async enqueue(handlerId, data) {
4864
- let queue = await this.getQueueActions();
4865
- const handler = this.getHandler(handlerId);
4866
- const pendingAction = (await handler.buildPendingAction(data, queue));
4867
- await this.draftStore.writeAction(pendingAction);
4868
- queue = await this.getQueueActions();
4869
- await this.notifyChangedListeners({
4870
- type: DraftQueueEventType.ActionAdded,
4871
- action: pendingAction,
4903
+ return this.workerPool.push(async () => {
4904
+ let queue = await this.getQueueActions();
4905
+ const handler = this.getHandler(handlerId);
4906
+ const pendingAction = (await handler.buildPendingAction(data, queue));
4907
+ await this.draftStore.writeAction(pendingAction);
4908
+ queue = await this.getQueueActions();
4909
+ await this.notifyChangedListeners({
4910
+ type: DraftQueueEventType.ActionAdded,
4911
+ action: pendingAction,
4912
+ });
4913
+ await handler.handleActionEnqueued(pendingAction, queue);
4914
+ if (this.state === DraftQueueState.Started) {
4915
+ this.processNextAction();
4916
+ }
4917
+ return pendingAction;
4872
4918
  });
4873
- await handler.handleActionEnqueued(pendingAction, queue);
4874
- if (this.state === DraftQueueState.Started) {
4875
- this.processNextAction();
4876
- }
4877
- return pendingAction;
4878
4919
  }
4879
4920
  registerOnChangedListener(listener) {
4880
4921
  this.draftQueueChangedListeners.push(listener);
@@ -4886,27 +4927,29 @@ class DurableDraftQueue {
4886
4927
  };
4887
4928
  }
4888
4929
  async actionCompleted(action) {
4889
- const handler = this.getHandler(action.handler);
4890
- let queue = await this.getQueueActions();
4891
- const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
4892
- const idAndKeyMappings = handler.getRedirectMappings(action);
4893
- const keyMappings = idAndKeyMappings === undefined
4894
- ? undefined
4895
- : idAndKeyMappings.map((m) => {
4896
- return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
4930
+ return this.workerPool.push(async () => {
4931
+ const handler = this.getHandler(action.handler);
4932
+ let queue = await this.getQueueActions();
4933
+ const queueOperations = handler.getQueueOperationsForCompletingDrafts(queue, action);
4934
+ const idAndKeyMappings = handler.getRedirectMappings(action);
4935
+ const keyMappings = idAndKeyMappings === undefined
4936
+ ? undefined
4937
+ : idAndKeyMappings.map((m) => {
4938
+ return { draftKey: m.draftKey, canonicalKey: m.canonicalKey };
4939
+ });
4940
+ await this.draftStore.completeAction(queueOperations, keyMappings);
4941
+ queue = await this.getQueueActions();
4942
+ this.retryIntervalMilliseconds = 0;
4943
+ this.uploadingActionId = undefined;
4944
+ await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
4945
+ await this.notifyChangedListeners({
4946
+ type: DraftQueueEventType.ActionCompleted,
4947
+ action,
4897
4948
  });
4898
- await this.draftStore.completeAction(queueOperations, keyMappings);
4899
- queue = await this.getQueueActions();
4900
- this.retryIntervalMilliseconds = 0;
4901
- this.uploadingActionId = undefined;
4902
- await handler.handleActionCompleted(action, queueOperations, queue, values$1(this.handlers));
4903
- await this.notifyChangedListeners({
4904
- type: DraftQueueEventType.ActionCompleted,
4905
- action,
4949
+ if (this.state === DraftQueueState.Started) {
4950
+ this.processNextAction();
4951
+ }
4906
4952
  });
4907
- if (this.state === DraftQueueState.Started) {
4908
- this.processNextAction();
4909
- }
4910
4953
  }
4911
4954
  async actionFailed(action, retry) {
4912
4955
  this.uploadingActionId = undefined;
@@ -5356,6 +5399,13 @@ class AbstractResourceRequestActionHandler {
5356
5399
  this.draftQueue = draftQueue;
5357
5400
  this.networkAdapter = networkAdapter;
5358
5401
  this.getLuvio = getLuvio;
5402
+ // NOTE[W-12567340]: This property stores in-memory mappings between draft
5403
+ // ids and canonical ids for the current session. Having a local copy of
5404
+ // these mappings is necessary to avoid a race condition between publishing
5405
+ // new mappings to the durable store and those mappings being loaded into
5406
+ // the luvio store redirect table, during which a new draft might be enqueued
5407
+ // which would not see a necessary mapping.
5408
+ this.ephemeralRedirects = {};
5359
5409
  }
5360
5410
  enqueue(data) {
5361
5411
  return this.draftQueue.enqueue(this.handlerId, data);
@@ -5423,7 +5473,8 @@ class AbstractResourceRequestActionHandler {
5423
5473
  }
5424
5474
  getQueueOperationsForCompletingDrafts(queue, action) {
5425
5475
  const queueOperations = [];
5426
- if (action.data.method === 'post') {
5476
+ const redirects = this.getRedirectMappings(action);
5477
+ if (redirects !== undefined) {
5427
5478
  const { length } = queue;
5428
5479
  for (let i = 0; i < length; i++) {
5429
5480
  const queueAction = queue[i];
@@ -5433,69 +5484,64 @@ class AbstractResourceRequestActionHandler {
5433
5484
  continue;
5434
5485
  }
5435
5486
  if (isResourceRequestAction(queueAction)) {
5436
- const redirects = this.getRedirectMappings(action);
5437
- if (redirects !== undefined) {
5438
- let queueOperationMutated = false;
5439
- let updatedActionTag = undefined;
5440
- let updatedActionTargetId = undefined;
5441
- const { tag: queueActionTag, data: queueActionRequest, id: queueActionId, } = queueAction;
5442
- let { basePath, body } = queueActionRequest;
5443
- let stringifiedBody = stringify$4(body);
5444
- // for each redirected ID/key we loop over the operation to see if it needs
5445
- // to be updated
5446
- for (const { draftId, draftKey, canonicalId, canonicalKey } of redirects) {
5447
- if (basePath.search(draftId) >= 0 ||
5448
- stringifiedBody.search(draftId) >= 0) {
5449
- basePath = basePath.replace(draftId, canonicalId);
5450
- stringifiedBody = stringifiedBody.replace(draftId, canonicalId);
5451
- queueOperationMutated = true;
5452
- }
5453
- // if the action is performed on a previous draft id, we need to replace the action
5454
- // with a new one at the updated canonical key
5455
- if (queueActionTag === draftKey) {
5456
- updatedActionTag = canonicalKey;
5457
- updatedActionTargetId = canonicalId;
5458
- }
5487
+ let queueOperationMutated = false;
5488
+ let updatedActionTag = undefined;
5489
+ let updatedActionTargetId = undefined;
5490
+ const { tag: queueActionTag, data: queueActionRequest, id: queueActionId, } = queueAction;
5491
+ let { basePath, body } = queueActionRequest;
5492
+ let stringifiedBody = stringify$4(body);
5493
+ // for each redirected ID/key we loop over the operation to see if it needs
5494
+ // to be updated
5495
+ for (const { draftId, draftKey, canonicalId, canonicalKey } of redirects) {
5496
+ if (basePath.search(draftId) >= 0 || stringifiedBody.search(draftId) >= 0) {
5497
+ basePath = basePath.replace(draftId, canonicalId);
5498
+ stringifiedBody = stringifiedBody.replace(draftId, canonicalId);
5499
+ queueOperationMutated = true;
5459
5500
  }
5460
- if (queueOperationMutated) {
5461
- if (updatedActionTag !== undefined &&
5462
- updatedActionTargetId !== undefined) {
5463
- const updatedAction = {
5464
- ...queueAction,
5465
- tag: updatedActionTag,
5466
- targetId: updatedActionTargetId,
5467
- data: {
5468
- ...queueActionRequest,
5469
- basePath: basePath,
5470
- body: parse$4(stringifiedBody),
5471
- },
5472
- };
5473
- // item needs to be replaced with a new item at the new record key
5474
- queueOperations.push({
5475
- type: QueueOperationType.Delete,
5476
- id: queueActionId,
5477
- });
5478
- queueOperations.push({
5479
- type: QueueOperationType.Add,
5480
- action: updatedAction,
5481
- });
5482
- }
5483
- else {
5484
- const updatedAction = {
5485
- ...queueAction,
5486
- data: {
5487
- ...queueActionRequest,
5488
- basePath: basePath,
5489
- body: parse$4(stringifiedBody),
5490
- },
5491
- };
5492
- // item needs to be updated
5493
- queueOperations.push({
5494
- type: QueueOperationType.Update,
5495
- id: queueActionId,
5496
- action: updatedAction,
5497
- });
5498
- }
5501
+ // if the action is performed on a previous draft id, we need to replace the action
5502
+ // with a new one at the updated canonical key
5503
+ if (queueActionTag === draftKey) {
5504
+ updatedActionTag = canonicalKey;
5505
+ updatedActionTargetId = canonicalId;
5506
+ }
5507
+ }
5508
+ if (queueOperationMutated) {
5509
+ if (updatedActionTag !== undefined && updatedActionTargetId !== undefined) {
5510
+ const updatedAction = {
5511
+ ...queueAction,
5512
+ tag: updatedActionTag,
5513
+ targetId: updatedActionTargetId,
5514
+ data: {
5515
+ ...queueActionRequest,
5516
+ basePath: basePath,
5517
+ body: parse$4(stringifiedBody),
5518
+ },
5519
+ };
5520
+ // item needs to be replaced with a new item at the new record key
5521
+ queueOperations.push({
5522
+ type: QueueOperationType.Delete,
5523
+ id: queueActionId,
5524
+ });
5525
+ queueOperations.push({
5526
+ type: QueueOperationType.Add,
5527
+ action: updatedAction,
5528
+ });
5529
+ }
5530
+ else {
5531
+ const updatedAction = {
5532
+ ...queueAction,
5533
+ data: {
5534
+ ...queueActionRequest,
5535
+ basePath: basePath,
5536
+ body: parse$4(stringifiedBody),
5537
+ },
5538
+ };
5539
+ // item needs to be updated
5540
+ queueOperations.push({
5541
+ type: QueueOperationType.Update,
5542
+ id: queueActionId,
5543
+ action: updatedAction,
5544
+ });
5499
5545
  }
5500
5546
  }
5501
5547
  }
@@ -5515,6 +5561,9 @@ class AbstractResourceRequestActionHandler {
5515
5561
  const body = action.response.body;
5516
5562
  const canonicalId = this.getIdFromResponseBody(body);
5517
5563
  const draftId = action.targetId;
5564
+ if (draftId !== undefined && canonicalId !== undefined && draftId !== canonicalId) {
5565
+ this.ephemeralRedirects[draftId] = canonicalId;
5566
+ }
5518
5567
  return [
5519
5568
  {
5520
5569
  draftId,
@@ -11296,12 +11345,18 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
11296
11345
  const fieldName = fieldNames[i];
11297
11346
  const fieldValue = bodyFields[fieldName];
11298
11347
  if (typeof fieldValue === 'string' && this.isDraftId(fieldValue)) {
11299
- const draftKey = this.buildTagForTargetId(fieldValue);
11300
- const canonicalKey = this.getLuvio().storeGetCanonicalKey(draftKey);
11301
- if (draftKey !== canonicalKey) {
11302
- const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11303
- if (canonicalId !== undefined) {
11304
- bodyFields[fieldName] = canonicalId;
11348
+ const canonicalId = this.ephemeralRedirects[fieldValue];
11349
+ if (canonicalId !== undefined) {
11350
+ bodyFields[fieldName] = canonicalId;
11351
+ }
11352
+ else {
11353
+ const draftKey = this.buildTagForTargetId(fieldValue);
11354
+ const canonicalKey = this.getLuvio().storeGetCanonicalKey(draftKey);
11355
+ if (draftKey !== canonicalKey) {
11356
+ const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11357
+ if (canonicalId !== undefined) {
11358
+ bodyFields[fieldName] = canonicalId;
11359
+ }
11305
11360
  }
11306
11361
  }
11307
11362
  }
@@ -11309,17 +11364,24 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
11309
11364
  }
11310
11365
  if (request.method === 'patch' || request.method === 'delete') {
11311
11366
  const recordId = request.urlParams['recordId'];
11312
- const recordKey = this.buildTagForTargetId(recordId);
11313
- const canonicalKey = this.getLuvio().storeGetCanonicalKey(recordKey);
11314
- if (recordKey !== canonicalKey) {
11315
- const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11316
- if (canonicalId === undefined) {
11317
- // could not resolve id from request -- return the request un-modified
11318
- return request;
11319
- }
11367
+ const canonicalId = this.ephemeralRedirects[recordId];
11368
+ if (canonicalId !== undefined) {
11320
11369
  resolvedBasePath = resolvedBasePath.replace(recordId, canonicalId);
11321
11370
  resolvedUrlParams = { ...resolvedUrlParams, recordId: canonicalId };
11322
11371
  }
11372
+ else {
11373
+ const recordKey = this.buildTagForTargetId(recordId);
11374
+ const canonicalKey = this.getLuvio().storeGetCanonicalKey(recordKey);
11375
+ if (recordKey !== canonicalKey) {
11376
+ const canonicalId = extractRecordIdFromStoreKey(canonicalKey);
11377
+ if (canonicalId === undefined) {
11378
+ // could not resolve id from request -- return the request un-modified
11379
+ return request;
11380
+ }
11381
+ resolvedBasePath = resolvedBasePath.replace(recordId, canonicalId);
11382
+ resolvedUrlParams = { ...resolvedUrlParams, recordId: canonicalId };
11383
+ }
11384
+ }
11323
11385
  }
11324
11386
  return {
11325
11387
  ...request,
@@ -14667,34 +14729,6 @@ function setupInspection(luvio) {
14667
14729
  * For full license text, see the LICENSE.txt file
14668
14730
  */
14669
14731
 
14670
- class AsyncWorkerPool {
14671
- constructor(concurrency) {
14672
- this.queue = [];
14673
- this.activeWorkers = 0;
14674
- this.concurrency = concurrency;
14675
- }
14676
- push(workFn) {
14677
- this.queue.push(workFn);
14678
- this.work();
14679
- }
14680
- cancel() {
14681
- this.queue = [];
14682
- // TODO [W-12513105]: thread cancellation through to active workers
14683
- }
14684
- work() {
14685
- while (this.queue.length > 0 && this.activeWorkers < this.concurrency) {
14686
- this.activeWorkers += 1;
14687
- const next = this.queue.shift();
14688
- if (next) {
14689
- next().finally(() => {
14690
- this.activeWorkers -= 1;
14691
- this.work();
14692
- });
14693
- }
14694
- }
14695
- }
14696
- }
14697
-
14698
14732
  class EventEmitter {
14699
14733
  constructor() {
14700
14734
  // @ts-ignore typescript doesn't like us setting this to an empty object for some reason
@@ -15247,4 +15281,4 @@ register({
15247
15281
  });
15248
15282
 
15249
15283
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15250
- // version: 1.100.3-8176c84f6
15284
+ // version: 1.100.4-ce1be23b7