@salesforce/lds-drafts 1.451.0-dev2 → 1.452.0
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/ldsDrafts.js
CHANGED
|
@@ -322,7 +322,7 @@ class DurableDraftQueue {
|
|
|
322
322
|
}
|
|
323
323
|
return handler;
|
|
324
324
|
}
|
|
325
|
-
constructor(draftStore) {
|
|
325
|
+
constructor(draftStore, instrumentation) {
|
|
326
326
|
this.retryIntervalMilliseconds = 0;
|
|
327
327
|
this.minimumRetryInterval = 250;
|
|
328
328
|
this.maximumRetryInterval = 32000;
|
|
@@ -339,6 +339,7 @@ class DurableDraftQueue {
|
|
|
339
339
|
this.handlers = {};
|
|
340
340
|
this.draftStore = draftStore;
|
|
341
341
|
this.workerPool = new AsyncWorkerPool(1);
|
|
342
|
+
this.instrumentation = instrumentation;
|
|
342
343
|
}
|
|
343
344
|
addHandler(handler) {
|
|
344
345
|
const id = handler.handlerId;
|
|
@@ -429,15 +430,22 @@ class DurableDraftQueue {
|
|
|
429
430
|
this.state = DraftQueueState.Stopped;
|
|
430
431
|
}
|
|
431
432
|
async getQueueActions() {
|
|
433
|
+
// W-23515682: time the whole call, including this queue's own internal
|
|
434
|
+
// this.getQueueActions() during a drain — the amplification signal an external wrapper
|
|
435
|
+
// cannot see. Measure-only; control flow below is unchanged.
|
|
436
|
+
const { instrumentation } = this;
|
|
437
|
+
const start = instrumentation?.onGetQueueActions !== undefined ? Date.now() : 0;
|
|
432
438
|
const drafts = (await this.draftStore.getAllDrafts());
|
|
433
439
|
const queue = [];
|
|
434
440
|
drafts.forEach((draft) => {
|
|
435
441
|
if (draft.id === this.uploadingActionId) {
|
|
436
|
-
draft
|
|
442
|
+
queue.push({ ...draft, status: DraftActionStatus.Uploading });
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
queue.push(draft);
|
|
437
446
|
}
|
|
438
|
-
queue.push(draft);
|
|
439
447
|
});
|
|
440
|
-
|
|
448
|
+
const sorted = queue.sort((a, b) => {
|
|
441
449
|
const aTime = parseInt(a.id, 10);
|
|
442
450
|
const bTime = parseInt(b.id, 10);
|
|
443
451
|
// safety check
|
|
@@ -449,6 +457,8 @@ class DurableDraftQueue {
|
|
|
449
457
|
}
|
|
450
458
|
return aTime - bTime;
|
|
451
459
|
});
|
|
460
|
+
instrumentation?.onGetQueueActions?.(Date.now() - start, sorted.length);
|
|
461
|
+
return sorted;
|
|
452
462
|
}
|
|
453
463
|
async enqueue(handlerId, data, observabilityContext) {
|
|
454
464
|
return this.workerPool.push({
|
|
@@ -597,6 +607,8 @@ class DurableDraftQueue {
|
|
|
597
607
|
const listener = draftQueueChangedListeners[i];
|
|
598
608
|
results.push(listener(event));
|
|
599
609
|
}
|
|
610
|
+
// W-23515682: listener fan-out per event type. Measure-only.
|
|
611
|
+
this.instrumentation?.onNotifyChangedListeners?.(event.type, draftQueueLen);
|
|
600
612
|
await Promise.all(results);
|
|
601
613
|
}
|
|
602
614
|
/**
|
|
@@ -844,36 +856,66 @@ function buildDraftDurableStoreKey(recordKey, draftActionId) {
|
|
|
844
856
|
*
|
|
845
857
|
*/
|
|
846
858
|
class DurableDraftStore {
|
|
847
|
-
constructor(durableStore) {
|
|
859
|
+
constructor(durableStore, instrumentation, useSnapshot = true) {
|
|
848
860
|
this.draftStore = {};
|
|
861
|
+
// Snapshot: holds deep clones of each draft, refreshed at write time.
|
|
862
|
+
// getAllDrafts serves from this snapshot with zero deep clones per read.
|
|
863
|
+
this.cleanSnapshot = {};
|
|
849
864
|
// queue of writes that were made during the initial sync
|
|
850
865
|
this.writeQueue = [];
|
|
851
866
|
this.durableStore = durableStore;
|
|
867
|
+
this.instrumentation = instrumentation;
|
|
868
|
+
this.useSnapshot = useSnapshot;
|
|
852
869
|
this.resyncDraftStore();
|
|
853
870
|
}
|
|
871
|
+
// When no instrumentation is injected the promise is returned as-is (zero overhead).
|
|
872
|
+
timeDurableOp(op, segment, run) {
|
|
873
|
+
const onDurableOperation = this.instrumentation?.onDurableOperation;
|
|
874
|
+
if (onDurableOperation === undefined) {
|
|
875
|
+
return run();
|
|
876
|
+
}
|
|
877
|
+
const start = Date.now();
|
|
878
|
+
return run().finally(() => {
|
|
879
|
+
onDurableOperation(op, segment, Date.now() - start);
|
|
880
|
+
});
|
|
881
|
+
}
|
|
854
882
|
writeAction(action) {
|
|
855
883
|
const addAction = () => {
|
|
856
884
|
const { id, tag } = action;
|
|
857
885
|
this.draftStore[id] = action;
|
|
886
|
+
this.cleanSnapshot[id] = clone(action);
|
|
858
887
|
const durableEntryKey = buildDraftDurableStoreKey(tag, id);
|
|
859
888
|
const entry = {
|
|
860
889
|
data: action,
|
|
861
890
|
};
|
|
862
891
|
const entries = { [durableEntryKey]: entry };
|
|
863
|
-
return this.durableStore.setEntries(entries, DRAFT_SEGMENT);
|
|
892
|
+
return this.timeDurableOp('setEntries', DRAFT_SEGMENT, () => this.durableStore.setEntries(entries, DRAFT_SEGMENT));
|
|
864
893
|
};
|
|
865
894
|
return this.enqueueAction(addAction);
|
|
866
895
|
}
|
|
867
896
|
getAllDrafts() {
|
|
868
897
|
const waitForOngoingSync = this.syncPromise || Promise.resolve();
|
|
869
898
|
return waitForOngoingSync.then(() => {
|
|
870
|
-
const { draftStore } = this;
|
|
871
|
-
const keys$1 = keys(draftStore);
|
|
872
899
|
const actionArray = [];
|
|
873
|
-
|
|
874
|
-
const
|
|
875
|
-
|
|
876
|
-
|
|
900
|
+
if (this.useSnapshot) {
|
|
901
|
+
const { cleanSnapshot } = this;
|
|
902
|
+
const keys$1 = keys(cleanSnapshot);
|
|
903
|
+
for (let i = 0, len = keys$1.length; i < len; i++) {
|
|
904
|
+
const key = keys$1[i];
|
|
905
|
+
// Return shallow spread of snapshot entries so top-level caller mutations
|
|
906
|
+
// (e.g., .status =) don't persist into the snapshot. The snapshot itself
|
|
907
|
+
// is a deep clone of draftStore, so no nested mutation reaches draftStore.
|
|
908
|
+
actionArray.push({ ...cleanSnapshot[key] });
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
// Killswitch (W-23515682): original pre-fix behavior — deep-clone every draft on
|
|
913
|
+
// every read so the internal draft store is never exposed. O(N) clones per read.
|
|
914
|
+
const { draftStore } = this;
|
|
915
|
+
const keys$1 = keys(draftStore);
|
|
916
|
+
for (let i = 0, len = keys$1.length; i < len; i++) {
|
|
917
|
+
actionArray.push(clone(draftStore[keys$1[i]]));
|
|
918
|
+
}
|
|
877
919
|
}
|
|
878
920
|
return actionArray;
|
|
879
921
|
});
|
|
@@ -883,8 +925,9 @@ class DurableDraftStore {
|
|
|
883
925
|
const draft = this.draftStore[id];
|
|
884
926
|
if (draft !== undefined) {
|
|
885
927
|
delete this.draftStore[id];
|
|
928
|
+
delete this.cleanSnapshot[id];
|
|
886
929
|
const durableKey = buildDraftDurableStoreKey(draft.tag, draft.id);
|
|
887
|
-
return this.durableStore.evictEntries([durableKey], DRAFT_SEGMENT);
|
|
930
|
+
return this.timeDurableOp('evictEntries', DRAFT_SEGMENT, () => this.durableStore.evictEntries([durableKey], DRAFT_SEGMENT));
|
|
888
931
|
}
|
|
889
932
|
return Promise.resolve();
|
|
890
933
|
};
|
|
@@ -900,10 +943,11 @@ class DurableDraftStore {
|
|
|
900
943
|
const action = draftStore[key];
|
|
901
944
|
if (action.tag === tag) {
|
|
902
945
|
delete draftStore[action.id];
|
|
946
|
+
delete this.cleanSnapshot[action.id];
|
|
903
947
|
durableKeys.push(buildDraftDurableStoreKey(action.tag, action.id));
|
|
904
948
|
}
|
|
905
949
|
}
|
|
906
|
-
return this.durableStore.evictEntries(durableKeys, DRAFT_SEGMENT);
|
|
950
|
+
return this.timeDurableOp('evictEntries', DRAFT_SEGMENT, () => this.durableStore.evictEntries(durableKeys, DRAFT_SEGMENT));
|
|
907
951
|
};
|
|
908
952
|
return this.enqueueAction(deleteAction);
|
|
909
953
|
}
|
|
@@ -917,6 +961,7 @@ class DurableDraftStore {
|
|
|
917
961
|
const action = draftStore[operation.id];
|
|
918
962
|
if (action !== undefined) {
|
|
919
963
|
delete draftStore[operation.id];
|
|
964
|
+
delete this.cleanSnapshot[operation.id];
|
|
920
965
|
const key = buildDraftDurableStoreKey(action.tag, action.id);
|
|
921
966
|
durableStoreOperations.push({
|
|
922
967
|
ids: [key],
|
|
@@ -929,6 +974,7 @@ class DurableDraftStore {
|
|
|
929
974
|
const { action } = operation;
|
|
930
975
|
const key = buildDraftDurableStoreKey(action.tag, action.id);
|
|
931
976
|
draftStore[action.id] = action;
|
|
977
|
+
this.cleanSnapshot[action.id] = clone(action);
|
|
932
978
|
durableStoreOperations.push({
|
|
933
979
|
type: 'setEntries',
|
|
934
980
|
segment: DRAFT_SEGMENT,
|
|
@@ -940,7 +986,7 @@ class DurableDraftStore {
|
|
|
940
986
|
});
|
|
941
987
|
}
|
|
942
988
|
}
|
|
943
|
-
return this.durableStore.batchOperations(durableStoreOperations);
|
|
989
|
+
return this.timeDurableOp('batchOperations', DRAFT_SEGMENT, () => this.durableStore.batchOperations(durableStoreOperations));
|
|
944
990
|
};
|
|
945
991
|
return this.enqueueAction(action);
|
|
946
992
|
}
|
|
@@ -983,6 +1029,7 @@ class DurableDraftStore {
|
|
|
983
1029
|
.then((durableEntries) => {
|
|
984
1030
|
if (durableEntries === undefined) {
|
|
985
1031
|
this.draftStore = {};
|
|
1032
|
+
this.cleanSnapshot = {};
|
|
986
1033
|
return this.runQueuedOperations();
|
|
987
1034
|
}
|
|
988
1035
|
const { draftStore } = this;
|
|
@@ -999,6 +1046,7 @@ class DurableDraftStore {
|
|
|
999
1046
|
}
|
|
1000
1047
|
}
|
|
1001
1048
|
draftStore[action.id] = action;
|
|
1049
|
+
this.cleanSnapshot[action.id] = clone(action);
|
|
1002
1050
|
}
|
|
1003
1051
|
return this.runQueuedOperations();
|
|
1004
1052
|
})
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export interface DraftQueueInstrumentation {
|
|
2
|
+
onGetQueueActions?(durationMs: number, actionCount: number): void;
|
|
3
|
+
onNotifyChangedListeners?(eventType: string, listenerCount: number): void;
|
|
4
|
+
onDurableOperation?(op: string, segment: string, durationMs: number): void;
|
|
5
|
+
}
|
|
@@ -3,6 +3,7 @@ import { ProcessActionResult, DraftQueueState } from './DraftQueue';
|
|
|
3
3
|
import type { CustomActionExecutor } from './actionHandlers/CustomActionHandler';
|
|
4
4
|
import type { ActionHandler } from './actionHandlers/ActionHandler';
|
|
5
5
|
import type { DraftStore } from './DraftStore';
|
|
6
|
+
import type { DraftQueueInstrumentation } from './DraftQueueInstrumentation';
|
|
6
7
|
import type { ObservabilityContext } from '@salesforce/nimbus-plugin-lds';
|
|
7
8
|
export declare const DRAFT_SEGMENT = "DRAFT";
|
|
8
9
|
/**
|
|
@@ -27,8 +28,9 @@ export declare class DurableDraftQueue implements DraftQueue {
|
|
|
27
28
|
private logger;
|
|
28
29
|
private workerPool;
|
|
29
30
|
private handlers;
|
|
31
|
+
private instrumentation?;
|
|
30
32
|
private getHandler;
|
|
31
|
-
constructor(draftStore: DraftStore);
|
|
33
|
+
constructor(draftStore: DraftStore, instrumentation?: DraftQueueInstrumentation);
|
|
32
34
|
addHandler<Data>(handler: ActionHandler<Data, unknown>): Promise<void>;
|
|
33
35
|
removeHandler(id: string): Promise<void>;
|
|
34
36
|
addCustomHandler(id: string, executor: CustomActionExecutor): Promise<void>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DurableStore } from '@luvio/environments';
|
|
2
2
|
import type { DraftAction, QueueOperation } from './DraftQueue';
|
|
3
3
|
import type { DraftStore } from './DraftStore';
|
|
4
|
+
import type { DraftQueueInstrumentation } from './DraftQueueInstrumentation';
|
|
4
5
|
/**
|
|
5
6
|
* Implements a write-through InMemoryStore for Drafts, storing all drafts in a
|
|
6
7
|
* in-memory store with a write through to the DurableStore.
|
|
@@ -13,9 +14,13 @@ import type { DraftStore } from './DraftStore';
|
|
|
13
14
|
export declare class DurableDraftStore implements DraftStore {
|
|
14
15
|
private durableStore;
|
|
15
16
|
private draftStore;
|
|
17
|
+
private cleanSnapshot;
|
|
16
18
|
private syncPromise;
|
|
17
19
|
private writeQueue;
|
|
18
|
-
|
|
20
|
+
private instrumentation?;
|
|
21
|
+
private useSnapshot;
|
|
22
|
+
constructor(durableStore: DurableStore, instrumentation?: DraftQueueInstrumentation, useSnapshot?: boolean);
|
|
23
|
+
private timeDurableOp;
|
|
19
24
|
writeAction(action: DraftAction<unknown, unknown>): Promise<void>;
|
|
20
25
|
getAllDrafts<_R, _D>(): Promise<DraftAction<unknown, unknown>[]>;
|
|
21
26
|
deleteDraft(id: string): Promise<void>;
|
package/dist/types/main.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ export { DraftQueue, DraftQueueState, DraftAction, ErrorDraftAction, PendingDraf
|
|
|
2
2
|
export { DurableDraftQueue, DRAFT_SEGMENT, DRAFT_ACTION_RETRY_COUNT_METADATA_KEY, } from './DurableDraftQueue';
|
|
3
3
|
export { generateUniqueDraftActionId, uuidv4 } from './utils/id';
|
|
4
4
|
export { DurableDraftStore } from './DurableDraftStore';
|
|
5
|
+
export type { DraftQueueInstrumentation } from './DraftQueueInstrumentation';
|
|
5
6
|
export { DraftStore } from './DraftStore';
|
|
6
7
|
export { DraftManager, DraftManagerState, DraftActionOperationType, DraftQueueItem, DraftQueueItemMetadata, } from './DraftManager';
|
|
7
8
|
export { ActionHandler, ReplacingActions } from './actionHandlers/ActionHandler';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@salesforce/lds-drafts",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.452.0",
|
|
4
4
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
5
5
|
"description": "LDS Drafts",
|
|
6
6
|
"main": "dist/ldsDrafts.js",
|
|
@@ -26,10 +26,10 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@luvio/engine": "0.161.0",
|
|
28
28
|
"@luvio/environments": "0.161.0",
|
|
29
|
-
"@salesforce/lds-utils-adapters": "^1.
|
|
29
|
+
"@salesforce/lds-utils-adapters": "^1.452.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@salesforce/nimbus-plugin-lds": "^1.
|
|
32
|
+
"@salesforce/nimbus-plugin-lds": "^1.452.0"
|
|
33
33
|
},
|
|
34
34
|
"volta": {
|
|
35
35
|
"extends": "../../package.json"
|