@salesforce/lds-worker-api 1.461.0 → 1.463.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.
@@ -4276,7 +4276,7 @@ function withDefaultLuvio(callback) {
4276
4276
  }
4277
4277
  callbacks.push(callback);
4278
4278
  }
4279
- // version: 1.461.0-37b1c9d1cb
4279
+ // version: 1.463.0-ea9ecd17fc
4280
4280
 
4281
4281
  // TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
4282
4282
  function instrumentAdapter$1(createFunction, _metadata) {
@@ -5320,7 +5320,7 @@ function createGraphQLWireAdapterConstructor(luvio, adapter, metadata, astResolv
5320
5320
  const { apiFamily, name } = metadata;
5321
5321
  return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
5322
5322
  }
5323
- // version: 1.461.0-37b1c9d1cb
5323
+ // version: 1.463.0-ea9ecd17fc
5324
5324
 
5325
5325
  function isSupportedEntity(_objectApiName) {
5326
5326
  return true;
@@ -32547,7 +32547,7 @@ withDefaultLuvio((luvio) => {
32547
32547
  throttle(60, 60000, setupNotifyAllListRecordUpdateAvailable(luvio));
32548
32548
  throttle(60, 60000, setupNotifyAllListInfoSummaryUpdateAvailable(luvio));
32549
32549
  });
32550
- // version: 1.461.0-55ae54e753
32550
+ // version: 1.463.0-8515018faf
32551
32551
 
32552
32552
  var draftQueueMaxRetryAttemptsGate = {
32553
32553
  isOpen: function (e) {
@@ -44179,24 +44179,6 @@ function formattingOptions(pattern) {
44179
44179
  return pattern;
44180
44180
  }
44181
44181
 
44182
- var ldsPrimingGraphqlBatch = {
44183
- isOpen: function (e) {
44184
- return e.fallback;
44185
- },
44186
- hasError: function () {
44187
- return !0;
44188
- },
44189
- };
44190
-
44191
- var lmrPrimingUseSoql = {
44192
- isOpen: function (e) {
44193
- return e.fallback;
44194
- },
44195
- hasError: function () {
44196
- return !0;
44197
- },
44198
- };
44199
-
44200
44182
  var aggressiveTrimCountKillSwitchGate = {
44201
44183
  isOpen: function (e) {
44202
44184
  return e.fallback;
@@ -44215,15 +44197,6 @@ var graphqlL2AdapterGate = {
44215
44197
  },
44216
44198
  };
44217
44199
 
44218
- var useOneStore = {
44219
- isOpen: function (e) {
44220
- return e.fallback;
44221
- },
44222
- hasError: function () {
44223
- return !0;
44224
- },
44225
- };
44226
-
44227
44200
  const { create: create$7, freeze: freeze$4, keys: keys$8, entries: entries$5 } = Object;
44228
44201
  const { hasOwnProperty: hasOwnProperty$3 } = Object.prototype;
44229
44202
  const { isArray: isArray$7 } = Array;
@@ -44768,9 +44741,18 @@ function validateDiscriminatedOneOf$1(data, discriminator, path, document, visit
44768
44741
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
44769
44742
  return invalidSchemaResponseWithError$1(incorrectTypeError$1("object", typeof data, path));
44770
44743
  }
44771
- const { propertyName, mapping } = discriminator;
44744
+ const { propertyName, mapping, defaultMapping } = discriminator;
44772
44745
  const discriminatorValue = data[propertyName];
44773
44746
  if (discriminatorValue === void 0) {
44747
+ if (defaultMapping !== void 0) {
44748
+ return validateJsonSchemaInternal$1(
44749
+ data,
44750
+ { $ref: defaultMapping },
44751
+ path,
44752
+ document,
44753
+ visited
44754
+ );
44755
+ }
44774
44756
  return invalidSchemaResponseWithError$1(
44775
44757
  new MissingRequiredPropertyError$1(
44776
44758
  `Object at path '${path}' is missing required discriminator property '${propertyName}'.`
@@ -44782,7 +44764,7 @@ function validateDiscriminatedOneOf$1(data, discriminator, path, document, visit
44782
44764
  incorrectTypeError$1("string", typeof discriminatorValue, `${path}.${propertyName}`)
44783
44765
  );
44784
44766
  }
44785
- const targetRef = mapping[discriminatorValue];
44767
+ const targetRef = mapping[discriminatorValue] ?? defaultMapping;
44786
44768
  if (targetRef === void 0) {
44787
44769
  return invalidSchemaResponseWithError$1(
44788
44770
  new JsonSchemaViolationError$1(
@@ -45943,7 +45925,7 @@ var productConsumedSideEffects = {
45943
45925
  },
45944
45926
  };
45945
45927
 
45946
- var reviveOnlyRequestedFields = {
45928
+ var isProduct2SerializedSideEffectKillSwitch = {
45947
45929
  isOpen: function (e) {
45948
45930
  return e.fallback;
45949
45931
  },
@@ -81464,6 +81446,35 @@ async function getAdapterData(adapter, config) {
81464
81446
  return snapshot.data;
81465
81447
  }
81466
81448
 
81449
+ /**
81450
+ * A read-through cache over `DurableRecordStore.getRecord` scoped to a single draft-action
81451
+ * dispatch. Multiple side-effect hooks fire for one action (e.g. the van-stock and
81452
+ * IsProduct2Serialized ProductConsumed hooks) and are awaited one at a time, so they otherwise
81453
+ * each re-read the same draft record — and the IsProduct2Serialized chain re-reads the same
81454
+ * related ProductItem / Product2 — from the durable store. Sharing one reader across the
81455
+ * action's hooks collapses those repeated reads to a single durable call per key.
81456
+ *
81457
+ * The cache stores the in-flight promise (not the resolved value) so concurrent reads of the
81458
+ * same key share one durable call, and it caches `undefined` results so a miss is not re-fetched.
81459
+ * A reader must not outlive one action dispatch: durable state changes between actions, so a
81460
+ * longer-lived cache would return stale records.
81461
+ */
81462
+ class MemoizingRecordReader {
81463
+ constructor(durableRecordStore) {
81464
+ this.durableRecordStore = durableRecordStore;
81465
+ this.cache = new Map();
81466
+ }
81467
+ getRecord(key) {
81468
+ const cached = this.cache.get(key);
81469
+ if (cached !== undefined) {
81470
+ return cached;
81471
+ }
81472
+ const promise = this.durableRecordStore.getRecord(key);
81473
+ this.cache.set(key, promise);
81474
+ return promise;
81475
+ }
81476
+ }
81477
+
81467
81478
  const LDS_ACTION_HANDLER_ID = 'LDS_ACTION_HANDLER';
81468
81479
  const LDS_ACTION_METADATA_API_NAME = 'LDS_ACTION_METADATA_API_NAME';
81469
81480
  class UiApiActionHandler extends AbstractResourceRequestActionHandler {
@@ -81584,9 +81595,9 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
81584
81595
  return super.getRedirectMappings(action);
81585
81596
  }
81586
81597
  async handleActionCompleted(completedAction, queueOperations) {
81587
- if (completedAction.response.status === 204 && completedAction.data.method === 'post') {
81588
- // if we get a 204 it means the record creation was successful but we don't have the record to ingest
81589
- // remove the synthesized draft and do not try to ingest the response
81598
+ const { method } = completedAction.data;
81599
+ if (completedAction.response.status === 204 && (method === 'post' || method === 'patch')) {
81600
+ await this.recordService.removeSideEffectsForAction(completedAction);
81590
81601
  await this.evictKey(completedAction.tag);
81591
81602
  return;
81592
81603
  }
@@ -81667,8 +81678,15 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
81667
81678
  break;
81668
81679
  }
81669
81680
  }
81681
+ // One reader shared across every hook for this action so repeated reads of the same
81682
+ // record (the draft record, and any related records the hooks resolve) collapse to a
81683
+ // single durable call. Scoped to this dispatch only — durable state changes between
81684
+ // actions, so it must not be reused across them. Built only when hooks will run.
81685
+ const recordReader = hookTriggers.length > 0
81686
+ ? new MemoizingRecordReader(this.durableRecordStore)
81687
+ : undefined;
81670
81688
  for (const trigger of hookTriggers) {
81671
- const hookEffects = await trigger.then(action.id, action.timestamp, action.targetId, action.data.body?.fields ?? {});
81689
+ const hookEffects = await trigger.then(action.id, action.timestamp, action.targetId, action.data.body?.fields ?? {}, recordReader);
81672
81690
  for (const hookEffect of hookEffects) {
81673
81691
  effects.push(hookEffect);
81674
81692
  }
@@ -92740,211 +92758,6 @@ class NetworkRecordLoader {
92740
92758
  }
92741
92759
  }
92742
92760
 
92743
- const apiPath$1 = basePath + '/graphql';
92744
- const requiredPrefix = `required_`;
92745
- const requiredFieldMap = {
92746
- ApiName: 'ApiName',
92747
- Id: 'Id',
92748
- LastModifiedById: 'LastModifiedById { value }',
92749
- LastModifiedDate: 'LastModifiedDate { value }',
92750
- RecordTypeId: 'RecordTypeId(fallback: true) { value }',
92751
- SystemModstamp: 'SystemModstamp { value }',
92752
- WeakEtag: 'WeakEtag',
92753
- };
92754
- class RecordLoaderGraphQL extends NetworkRecordLoader {
92755
- makeRequest(batch, observabilityContext) {
92756
- const query = this.generateGraphQLQuery(batch.type, batch.fields);
92757
- return {
92758
- request: {
92759
- method: 'POST',
92760
- path: apiPath$1,
92761
- body: JSON.stringify({
92762
- query,
92763
- variables: {
92764
- ids: batch.ids,
92765
- first: batch.ids.length,
92766
- },
92767
- }),
92768
- headers: {},
92769
- queryParams: {},
92770
- priority: 'background',
92771
- observabilityContext: observabilityContext !== undefined ? observabilityContext : {},
92772
- },
92773
- context: undefined,
92774
- };
92775
- }
92776
- generateFetchResults(repResult, batchInput, _context) {
92777
- const { data, errors } = repResult;
92778
- if (errors !== undefined && errors.length > 0) {
92779
- // right now if there are any errors in the batch we throw out the entire batch
92780
- // for now this is ok all errors will originate on the same node so there shouldn't be a mix of errors and data
92781
- return [
92782
- {
92783
- missingIds: batchInput.ids,
92784
- ok: false,
92785
- error: 'request-error',
92786
- messages: errors.map((x) => x.message),
92787
- },
92788
- ];
92789
- }
92790
- // this shouldn't really happen if there's no errors
92791
- if (!data || !data.uiapi) {
92792
- return [
92793
- {
92794
- ok: false,
92795
- error: 'unknown',
92796
- messages: ['unexpected response retrieved from graphql endpoint'],
92797
- missingIds: batchInput.ids,
92798
- },
92799
- ];
92800
- }
92801
- const objectInfo = batchInput.objectInfo;
92802
- const seenRecords = new Set(batchInput.ids);
92803
- const records = data.uiapi.query[batchInput.type].edges.map((edge) => {
92804
- const record = this.generateDurableRecordRepresentation(edge.node, objectInfo);
92805
- seenRecords.delete(record.id);
92806
- return record;
92807
- });
92808
- return [
92809
- {
92810
- ok: true,
92811
- records,
92812
- missingIds: Array.from(seenRecords),
92813
- },
92814
- ];
92815
- }
92816
- generateGraphQLQuery(type, fields) {
92817
- const fieldList = Object.keys(requiredFieldMap)
92818
- .map((field) => {
92819
- return `${requiredPrefix}${field}: ${requiredFieldMap[field]}`;
92820
- })
92821
- .join('\n');
92822
- return /* GraphQL */ `
92823
- fragment requiredRecordFields on Record {
92824
- ${fieldList}
92825
- }
92826
- query prime${type}($ids: [ID], $first: Int){
92827
- uiapi {
92828
- query {
92829
- ${type} (where: {Id: {in: $ids}} first: $first) {
92830
- edges {
92831
- node {
92832
- ...requiredRecordFields
92833
- ${fields
92834
- .filter((x) => x !== 'Id' && x !== 'RecordTypeId')
92835
- .map((field) => `${field} { value displayValue }`)}
92836
- }
92837
- }
92838
- }
92839
- }
92840
- }
92841
- }
92842
- `;
92843
- }
92844
- generateDurableRecordRepresentation(node, objectInfo) {
92845
- const standardRecordFields = new Set(Object.keys(requiredFieldMap).map((x) => `${requiredPrefix}${x}`));
92846
- const id = node[`${requiredPrefix}Id`];
92847
- const recordTypeId = node[`${requiredPrefix}RecordTypeId`].value;
92848
- const requestedFields = Object.keys(node).filter((field) => !standardRecordFields.has(field));
92849
- const fields = requestedFields.reduce((acc, field) => {
92850
- const fieldNode = node[field];
92851
- if (fieldNode.displayValue === undefined) {
92852
- fieldNode.displayValue = null;
92853
- }
92854
- acc[field] = node[field];
92855
- return acc;
92856
- }, {});
92857
- fields['Id'] = { value: id, displayValue: null };
92858
- if (objectInfo.fields['RecordTypeId'] !== undefined) {
92859
- fields['RecordTypeId'] = { value: recordTypeId, displayValue: null };
92860
- }
92861
- let recordTypeInfo = null;
92862
- if (recordTypeId !== null &&
92863
- objectInfo.recordTypeInfos &&
92864
- objectInfo.recordTypeInfos[recordTypeId]) {
92865
- const appliedRecordTypeInfo = objectInfo.recordTypeInfos[recordTypeId];
92866
- // ui-api only applies the record type if its not the default type, otherwise it sets it to null
92867
- if (appliedRecordTypeInfo.defaultRecordTypeMapping === false) {
92868
- recordTypeInfo = appliedRecordTypeInfo;
92869
- }
92870
- }
92871
- return {
92872
- apiName: node[`${requiredPrefix}ApiName`],
92873
- childRelationships: {},
92874
- eTag: '',
92875
- id,
92876
- lastModifiedById: node[`${requiredPrefix}LastModifiedById`] &&
92877
- node[`${requiredPrefix}LastModifiedById`].value
92878
- ? node[`${requiredPrefix}LastModifiedById`].value
92879
- : null,
92880
- lastModifiedDate: node[`${requiredPrefix}LastModifiedDate`] &&
92881
- node[`${requiredPrefix}LastModifiedDate`].value
92882
- ? node[`${requiredPrefix}LastModifiedDate`].value
92883
- : null,
92884
- recordTypeId,
92885
- recordTypeInfo: recordTypeInfo,
92886
- systemModstamp: node[`${requiredPrefix}SystemModstamp`] &&
92887
- node[`${requiredPrefix}SystemModstamp`].value
92888
- ? node[`${requiredPrefix}SystemModstamp`].value
92889
- : null,
92890
- weakEtag: node[`${requiredPrefix}WeakEtag`],
92891
- fields,
92892
- links: {},
92893
- };
92894
- }
92895
- }
92896
-
92897
- const DEFAULT_GQL_QUERY_BATCH_SIZE = 100;
92898
- const apiPath = basePath + '/graphql/batch';
92899
- class RecordLoaderGraphQLBatch extends RecordLoaderGraphQL {
92900
- makeRequest(batch, observabilityContext) {
92901
- const query = this.generateGraphQLQuery(batch.type, batch.fields);
92902
- const chunks = chunk(batch.ids, DEFAULT_GQL_QUERY_BATCH_SIZE);
92903
- const batches = chunks.map((chunk) => {
92904
- return {
92905
- query,
92906
- variables: {
92907
- ids: chunk,
92908
- first: chunk.length,
92909
- },
92910
- };
92911
- });
92912
- return {
92913
- request: {
92914
- method: 'POST',
92915
- path: apiPath,
92916
- body: JSON.stringify({
92917
- batchQuery: batches,
92918
- }),
92919
- headers: {},
92920
- queryParams: {},
92921
- priority: 'background',
92922
- observabilityContext: observabilityContext !== undefined ? observabilityContext : {},
92923
- },
92924
- context: chunks,
92925
- };
92926
- }
92927
- generateFetchResults(body, batchInput, context) {
92928
- const { results } = body;
92929
- const fetchResults = context.flatMap((chunk, index) => {
92930
- const result = results[index];
92931
- if (!result || result.statusCode !== 200) {
92932
- return {
92933
- ok: false,
92934
- error: 'request-error',
92935
- messages: ['unexpected response from graphql batch endpoint'],
92936
- missingIds: chunk,
92937
- };
92938
- }
92939
- return super.generateFetchResults(result.result, {
92940
- ...batchInput,
92941
- ids: chunk,
92942
- }, undefined);
92943
- });
92944
- return fetchResults;
92945
- }
92946
- }
92947
-
92948
92761
  class SoqlRecordConverter {
92949
92762
  /**
92950
92763
  *
@@ -93437,16 +93250,7 @@ class LdsPrimingRecordRefresher {
93437
93250
  function primingSessionFactory(config) {
93438
93251
  const { store, objectInfoService, getLuvio } = config;
93439
93252
  const networkAdapter = new NimbusPrimingNetworkAdapter();
93440
- let recordLoader;
93441
- if (lmrPrimingUseSoql.isOpen({ fallback: false })) {
93442
- recordLoader = new RecordLoaderSOQLComposite(networkAdapter);
93443
- }
93444
- else if (ldsPrimingGraphqlBatch.isOpen({ fallback: false })) {
93445
- recordLoader = new RecordLoaderGraphQLBatch(networkAdapter);
93446
- }
93447
- else {
93448
- recordLoader = new RecordLoaderGraphQL(networkAdapter);
93449
- }
93253
+ const recordLoader = new RecordLoaderSOQLComposite(networkAdapter);
93450
93254
  const primingStore = new SqlitePrimingStore(getLuvio, store);
93451
93255
  const recordIngestor = new RecordIngestor(primingStore, getLuvio);
93452
93256
  const session = new PrimingSession({
@@ -94099,6 +93903,38 @@ function buildCachedSnapshotCachePolicy$e(context, storeLookup) {
94099
93903
  }
94100
93904
 
94101
93905
  const SIDE_EFFECT_SEGMENT = 'RECORD_SIDE_EFFECTS';
93906
+ // Base-record effects rebuild (or remove) the entire record for a key, so they must be
93907
+ // applied before any field-level effect on that same key — otherwise a field effect that
93908
+ // runs first is discarded when the base record is (re)built. keyToEffectMap ordering is not
93909
+ // stable: it is re-materialized from the durable store on reload / onChanged replay, where
93910
+ // entry order is not guaranteed. So two field-set effects on the same key from different
93911
+ // actions (e.g. two queued edits of one field) can come back in either order; applied out of
93912
+ // order the OLDER value wins and the newer edit is lost. Effects must therefore be applied in
93913
+ // action chronology (by timestamp), with base-record ops ordered ahead of field ops only as
93914
+ // the tie-breaker within one action — create/field effects on a draft-created record share a
93915
+ // key AND a timestamp, so timestamp alone can't order those.
93916
+ const BASE_RECORD_EFFECT_TYPES = new Set(['record-create', 'record-update', 'record-delete']);
93917
+ function orderEffectsForApply(effects) {
93918
+ // Nothing to reorder for a single effect; return the original array to avoid allocating on
93919
+ // the hot read-out path.
93920
+ if (effects.length <= 1) {
93921
+ return effects;
93922
+ }
93923
+ // Primary key: timestamp (action chronology). Secondary key: base-record ops before field
93924
+ // ops, which only matters within one action's shared timestamp. Array.prototype.sort is
93925
+ // stable (ES2019), so effects that tie on both keys keep their existing relative order.
93926
+ return [...effects].sort((a, b) => {
93927
+ if (a.timestamp !== b.timestamp) {
93928
+ return a.timestamp - b.timestamp;
93929
+ }
93930
+ const aIsBase = BASE_RECORD_EFFECT_TYPES.has(a.type);
93931
+ const bIsBase = BASE_RECORD_EFFECT_TYPES.has(b.type);
93932
+ if (aIsBase === bIsBase) {
93933
+ return 0;
93934
+ }
93935
+ return aIsBase ? -1 : 1;
93936
+ });
93937
+ }
94102
93938
  class SideEffectStore {
94103
93939
  constructor(durableStore) {
94104
93940
  this.durableStore = durableStore;
@@ -94177,7 +94013,7 @@ class SideEffectStore {
94177
94013
  if (effects === undefined) {
94178
94014
  return [];
94179
94015
  }
94180
- return effects;
94016
+ return orderEffectsForApply(effects);
94181
94017
  }
94182
94018
  getEffectsByTag(tag) {
94183
94019
  // This scan is still O(N) over every effect regardless of the incremental-map
@@ -94674,6 +94510,58 @@ class RecordFieldIncrementHandler {
94674
94510
  return record;
94675
94511
  }
94676
94512
  }
94513
+ class RecordFieldSetHandler {
94514
+ constructor() {
94515
+ this.logger = typeof __nimbus !== 'undefined' &&
94516
+ __nimbus.plugins !== undefined &&
94517
+ __nimbus.plugins.JSLoggerPlugin !== undefined
94518
+ ? __nimbus.plugins.JSLoggerPlugin
94519
+ : undefined;
94520
+ }
94521
+ apply(record, effect) {
94522
+ if (record === undefined) {
94523
+ if (this.logger) {
94524
+ this.logger.logWarning('RecordFieldSetHandler - cannot apply set side effect to non existing record');
94525
+ }
94526
+ return undefined;
94527
+ }
94528
+ // fieldName is an unconstrained string that has round-tripped through the durable
94529
+ // store, so a value like `__proto__` would hit the prototype setter on the plain
94530
+ // `fields`/`serverValues` objects below rather than creating an own field (prototype
94531
+ // pollution). Reject dangerous keys before any dynamic read/write and skip the effect.
94532
+ if (isUnsafeFieldName(effect.fieldName)) {
94533
+ if (this.logger) {
94534
+ this.logger.logError(`RecordFieldSetHandler - unsafe field name ${effect.fieldName}, skip applying side effect`);
94535
+ }
94536
+ return record;
94537
+ }
94538
+ applyDraftNode(record, effect);
94539
+ record.drafts.edited = true;
94540
+ // capture the server value so it can be restored when the draft syncs.
94541
+ // unlike the increment handler, the field may not yet exist on the record
94542
+ // (e.g. IsProduct2Serialized is not part of the draft-created record), in
94543
+ // which case we record `null` to indicate we never had the field.
94544
+ if (record.drafts.created === false) {
94545
+ const serverValue = record.drafts.serverValues[effect.fieldName];
94546
+ if (serverValue === undefined) {
94547
+ const existingField = record.fields[effect.fieldName];
94548
+ record.drafts.serverValues[effect.fieldName] =
94549
+ existingField === undefined ? null : existingField;
94550
+ }
94551
+ }
94552
+ record.fields[effect.fieldName] = {
94553
+ value: effect.value,
94554
+ displayValue: null,
94555
+ };
94556
+ return record;
94557
+ }
94558
+ }
94559
+ // Field names that would target the prototype chain rather than an own property when used
94560
+ // to index a plain object, enabling prototype pollution on the record's field containers.
94561
+ const UNSAFE_FIELD_NAMES = new Set(['__proto__', 'constructor', 'prototype']);
94562
+ function isUnsafeFieldName(fieldName) {
94563
+ return UNSAFE_FIELD_NAMES.has(fieldName);
94564
+ }
94677
94565
  function applyDraftNode(record, effect) {
94678
94566
  if (record.drafts === undefined) {
94679
94567
  record.drafts = {
@@ -94708,6 +94596,7 @@ const handlerMap = {
94708
94596
  'record-update': new UpdateRecordEffectHandler(),
94709
94597
  'record-delete': new DeleteRecordEffectHandler(),
94710
94598
  'record-field-increment': new RecordFieldIncrementHandler(),
94599
+ 'record-field-set': new RecordFieldSetHandler(),
94711
94600
  };
94712
94601
  /**
94713
94602
  * Removes any existing effects on a record and re-applies the given ordered list and returns the record
@@ -94872,14 +94761,181 @@ function isStoreRecordError(storeRecord) {
94872
94761
  }
94873
94762
 
94874
94763
  const instrumentation$4 = getInstrumentation();
94764
+ const ProductItem$2 = {
94765
+ Fields: {
94766
+ PRODUCT2_ID: 'Product2Id',
94767
+ IS_PRODUCT2_SERIALIZED: 'IsProduct2Serialized',
94768
+ },
94769
+ };
94770
+ const Product2 = {
94771
+ Fields: {
94772
+ IS_SERIALIZED: 'IsSerialized',
94773
+ },
94774
+ };
94775
+ var CounterResult;
94776
+ (function (CounterResult) {
94777
+ // A side effect was created.
94778
+ CounterResult["Created"] = "Created";
94779
+ // The hook exited early.
94780
+ CounterResult["Exited"] = "Exited";
94781
+ // An unexpected error occurred.
94782
+ CounterResult["Error"] = "Error";
94783
+ })(CounterResult || (CounterResult = {}));
94784
+ function incrementCounter(name, result) {
94785
+ instrumentation$4.incrementCounter('SFSSideEffect', 1, result === CounterResult.Error, {
94786
+ Name: name,
94787
+ Result: result,
94788
+ });
94789
+ }
94790
+ /**
94791
+ * Reads a string-valued field off a draft-create `fields` map (the raw create body, where
94792
+ * values are the primitives themselves rather than `{ value }` wrappers). Returns `undefined`
94793
+ * when the field is absent or not a string.
94794
+ */
94795
+ function getStringDraftField(fields, fieldName) {
94796
+ const field = fields[fieldName];
94797
+ if (field === undefined || typeof field !== 'string') {
94798
+ return undefined;
94799
+ }
94800
+ return field;
94801
+ }
94802
+ /**
94803
+ * Reads a string-valued field off a durable record's `fields` map (where each value is a
94804
+ * `{ value }` wrapper). Returns `undefined` when the field is absent, null, or not a string.
94805
+ */
94806
+ function getStringRecordField(fields, fieldName) {
94807
+ const field = fields[fieldName];
94808
+ if (field === undefined || field.value === undefined || typeof field.value !== 'string') {
94809
+ return undefined;
94810
+ }
94811
+ return field.value;
94812
+ }
94813
+ /**
94814
+ * Resolves the reference id that points at a related record for a draft edit.
94815
+ *
94816
+ * On a `patch` the body only carries the fields being changed, so the link field may be
94817
+ * absent, present with a new id, or present but explicitly `null` (the link is being cleared).
94818
+ * These three cases are distinguished via `hasOwnProperty` because they demand different
94819
+ * behavior — falling back to the original durable-store id on an explicit `null` would resolve
94820
+ * `IsProduct2Serialized` from a relationship the record no longer has:
94821
+ *
94822
+ * - present + string -> `resolved` with the reassigned id.
94823
+ * - present + null -> `cleared`.
94824
+ * - absent -> fall back to the original record; `resolved` when it carries a usable
94825
+ * string, otherwise `unresolved`.
94826
+ */
94827
+ function resolveReferenceIdForUpdate(patchFields, originalFields, fieldName) {
94828
+ if (Object.prototype.hasOwnProperty.call(patchFields, fieldName)) {
94829
+ const patchValue = patchFields[fieldName];
94830
+ if (patchValue === null) {
94831
+ return { kind: 'cleared' };
94832
+ }
94833
+ const patchId = getStringDraftField(patchFields, fieldName);
94834
+ if (patchId !== undefined) {
94835
+ return { kind: 'resolved', id: patchId, source: 'patch' };
94836
+ }
94837
+ // Present but neither a string nor null (unexpected shape): treat as an absent link
94838
+ // and fall through to the original record rather than guessing.
94839
+ }
94840
+ const originalId = getStringRecordField(originalFields, fieldName);
94841
+ if (originalId !== undefined) {
94842
+ return { kind: 'resolved', id: originalId, source: 'original' };
94843
+ }
94844
+ return { kind: 'unresolved' };
94845
+ }
94846
+ /**
94847
+ * Resolves the server-derived `IsProduct2Serialized` boolean for a draft-created record by
94848
+ * following the related `ProductItem`:
94849
+ *
94850
+ * 1. If the cached `ProductItem` already carries `IsProduct2Serialized`, use it directly
94851
+ * (it mirrors the linked product's serialization, and avoids a second durable read).
94852
+ * 2. Otherwise fall back to the linked `Product2`:
94853
+ * `ProductItem.Product2Id` -> `Product2.IsSerialized`.
94854
+ *
94855
+ * All lookups go through the given `RecordReader` (the durable store, or a per-action memoizer
94856
+ * that dedups reads). Returns `undefined` when a link in the chain is missing so the caller can
94857
+ * exit without an effect and leave the field synthesized (null). Callers pass their own
94858
+ * `counterName` so instrumentation is attributed to the owning hook.
94859
+ */
94860
+ async function resolveIsProduct2SerializedFromProductItem(recordReader, productItemId, counterName) {
94861
+ const productItemRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(productItemId));
94862
+ if (productItemRecord === undefined) {
94863
+ incrementCounter(counterName, CounterResult.Exited);
94864
+ return undefined;
94865
+ }
94866
+ // Prefer IsProduct2Serialized straight off the ProductItem when it is cached there;
94867
+ // it saves a second durable read. Fall back to the linked Product2 otherwise.
94868
+ const fromProductItem = getIsProduct2SerializedFromRecordFields(productItemRecord.fields);
94869
+ if (fromProductItem !== undefined) {
94870
+ incrementCounter(counterName, CounterResult.Created);
94871
+ return fromProductItem;
94872
+ }
94873
+ const product2Id = getProduct2IdFromRecordFields(productItemRecord.fields);
94874
+ if (product2Id === undefined) {
94875
+ incrementCounter(counterName, CounterResult.Exited);
94876
+ return undefined;
94877
+ }
94878
+ return resolveIsSerializedFromProduct2Id(recordReader, product2Id, counterName);
94879
+ }
94880
+ /**
94881
+ * Resolves the `Product2.IsSerialized` boolean for a given `Product2Id` by reading the linked
94882
+ * `Product2` record through the given `RecordReader`. Used directly by the `ProductItem` hooks
94883
+ * (which carry `Product2Id` themselves) and as the fallback tail of
94884
+ * `resolveIsProduct2SerializedFromProductItem`.
94885
+ *
94886
+ * Returns `undefined` when the `Product2` record is missing so the caller can exit without an
94887
+ * effect and leave the field synthesized (null). Callers pass their own `counterName` so
94888
+ * instrumentation is attributed to the owning hook.
94889
+ */
94890
+ async function resolveIsSerializedFromProduct2Id(recordReader, product2Id, counterName) {
94891
+ const product2Record = await recordReader.getRecord(buildRecordRepKeyFromId$1(product2Id));
94892
+ if (product2Record === undefined) {
94893
+ incrementCounter(counterName, CounterResult.Exited);
94894
+ return undefined;
94895
+ }
94896
+ const isSerialized = getIsSerializedFromRecordFields(product2Record.fields);
94897
+ if (isSerialized === undefined) {
94898
+ incrementCounter(counterName, CounterResult.Error);
94899
+ // Keep the message a stable literal and append the variable parts as key=value
94900
+ // pairs, so the log line stays low-cardinality and greppable in Splunk (the raw
94901
+ // record id is a discrete field rather than baked into the message text).
94902
+ instrumentation$4.error(`SFS - Product2 IsSerialized not found: hook=${counterName}, product2Id=${product2Id}`);
94903
+ return undefined;
94904
+ }
94905
+ incrementCounter(counterName, CounterResult.Created);
94906
+ return isSerialized;
94907
+ }
94908
+ function getProduct2IdFromRecordFields(fields) {
94909
+ const field = fields[ProductItem$2.Fields.PRODUCT2_ID];
94910
+ if (field === undefined || field.value === undefined || typeof field.value !== 'string') {
94911
+ return undefined;
94912
+ }
94913
+ return field.value;
94914
+ }
94915
+ function getIsProduct2SerializedFromRecordFields(fields) {
94916
+ const field = fields[ProductItem$2.Fields.IS_PRODUCT2_SERIALIZED];
94917
+ if (field === undefined || field.value === undefined || typeof field.value !== 'boolean') {
94918
+ return undefined;
94919
+ }
94920
+ return field.value;
94921
+ }
94922
+ function getIsSerializedFromRecordFields(fields) {
94923
+ const field = fields[Product2.Fields.IS_SERIALIZED];
94924
+ if (field === undefined || field.value === undefined || typeof field.value !== 'boolean') {
94925
+ return undefined;
94926
+ }
94927
+ return field.value;
94928
+ }
94929
+
94875
94930
  const ProductConsumed = {
94876
94931
  API_NAME: 'ProductConsumed',
94877
94932
  Fields: {
94878
94933
  PRODUCT_ITEM_ID: 'ProductItemId',
94879
94934
  QUANTITY_CONSUMED: 'QuantityConsumed',
94935
+ IS_PRODUCT2_SERIALIZED: 'IsProduct2Serialized',
94880
94936
  },
94881
94937
  };
94882
- const ProductItem = {
94938
+ const ProductItem$1 = {
94883
94939
  Fields: {
94884
94940
  QUANTITY_ON_HAND: 'QuantityOnHand',
94885
94941
  },
@@ -94890,7 +94946,7 @@ class ProductConsumedCreatedHook {
94890
94946
  this.apiName = ProductConsumed.API_NAME;
94891
94947
  }
94892
94948
  async then(draftActionId, draftActionTimestamp, _draftRecordId, fields) {
94893
- const productItemId = getProductItemId(fields);
94949
+ const productItemId = getStringDraftField(fields, ProductConsumed.Fields.PRODUCT_ITEM_ID);
94894
94950
  const quantityConsumed = getQuantityConsumed(fields);
94895
94951
  if (productItemId === undefined) {
94896
94952
  incrementCounter(ProductConsumedCreatedHook.COUNTER_NAME, CounterResult.Exited);
@@ -94903,7 +94959,7 @@ class ProductConsumedCreatedHook {
94903
94959
  incrementCounter(ProductConsumedCreatedHook.COUNTER_NAME, CounterResult.Created);
94904
94960
  const effect = {
94905
94961
  type: 'record-field-increment',
94906
- fieldName: ProductItem.Fields.QUANTITY_ON_HAND,
94962
+ fieldName: ProductItem$1.Fields.QUANTITY_ON_HAND,
94907
94963
  value: -quantityConsumed,
94908
94964
  key: buildRecordRepKeyFromId$1(productItemId),
94909
94965
  tag: draftActionId,
@@ -94920,16 +94976,17 @@ class ProductConsumedUpdatedHook {
94920
94976
  this.on = 'patch';
94921
94977
  this.apiName = ProductConsumed.API_NAME;
94922
94978
  }
94923
- async then(draftActionId, draftActionTimestamp, draftRecordId, fields) {
94979
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
94924
94980
  const quantityConsumed = getQuantityConsumed(fields);
94925
94981
  if (quantityConsumed === undefined) {
94926
94982
  incrementCounter(ProductConsumedUpdatedHook.COUNTER_NAME, CounterResult.Exited);
94927
94983
  return [];
94928
94984
  }
94929
- const originalRecord = await this.durableRecordStore.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
94985
+ const recordReader = reader ?? this.durableRecordStore;
94986
+ const originalRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
94930
94987
  if (originalRecord === undefined) {
94931
94988
  incrementCounter(ProductConsumedUpdatedHook.COUNTER_NAME, CounterResult.Error);
94932
- instrumentation$4.error(`SFS - ProductConsumedUpdatedHook - Original record ${draftRecordId} not found`);
94989
+ instrumentation$4.error(`SFS - ProductConsumedUpdatedHook original record not found: draftRecordId=${draftRecordId}`);
94933
94990
  return [];
94934
94991
  }
94935
94992
  const originalProductItemId = getProductItemIdFromRecordFields(originalRecord.fields);
@@ -94940,13 +94997,13 @@ class ProductConsumedUpdatedHook {
94940
94997
  }
94941
94998
  if (originalQuantityConsumed === undefined) {
94942
94999
  incrementCounter(ProductConsumedUpdatedHook.COUNTER_NAME, CounterResult.Error);
94943
- instrumentation$4.error(`SFS - ProductConsumedUpdatedHook - Original record ${draftRecordId} QuantityConsumed not found`);
95000
+ instrumentation$4.error(`SFS - ProductConsumedUpdatedHook original record QuantityConsumed not found: draftRecordId=${draftRecordId}`);
94944
95001
  return [];
94945
95002
  }
94946
95003
  incrementCounter(ProductConsumedUpdatedHook.COUNTER_NAME, CounterResult.Created);
94947
95004
  const effect = {
94948
95005
  type: 'record-field-increment',
94949
- fieldName: ProductItem.Fields.QUANTITY_ON_HAND,
95006
+ fieldName: ProductItem$1.Fields.QUANTITY_ON_HAND,
94950
95007
  value: originalQuantityConsumed - quantityConsumed,
94951
95008
  key: buildRecordRepKeyFromId$1(originalProductItemId),
94952
95009
  tag: draftActionId,
@@ -94963,11 +95020,12 @@ class ProductConsumedDeletedHook {
94963
95020
  this.on = 'delete';
94964
95021
  this.apiName = ProductConsumed.API_NAME;
94965
95022
  }
94966
- async then(draftActionId, draftActionTimestamp, draftRecordId) {
94967
- const originalRecord = await this.durableRecordStore.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
95023
+ async then(draftActionId, draftActionTimestamp, draftRecordId, _fields, reader) {
95024
+ const recordReader = reader ?? this.durableRecordStore;
95025
+ const originalRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
94968
95026
  if (originalRecord === undefined) {
94969
95027
  incrementCounter(ProductConsumedDeletedHook.COUNTER_NAME, CounterResult.Error);
94970
- instrumentation$4.error(`SFS - ProductConsumedDeletedHook - Original record ${draftRecordId} not found`);
95028
+ instrumentation$4.error(`SFS - ProductConsumedDeletedHook original record not found: draftRecordId=${draftRecordId}`);
94971
95029
  return [];
94972
95030
  }
94973
95031
  const productItemId = getProductItemIdFromRecordFields(originalRecord.fields);
@@ -94978,13 +95036,13 @@ class ProductConsumedDeletedHook {
94978
95036
  }
94979
95037
  if (quantityConsumed === undefined) {
94980
95038
  incrementCounter(ProductConsumedDeletedHook.COUNTER_NAME, CounterResult.Error);
94981
- instrumentation$4.error(`SFS - ProductConsumedDeletedHook - Original record ${draftRecordId} QuantityConsumed not found`);
95039
+ instrumentation$4.error(`SFS - ProductConsumedDeletedHook original record QuantityConsumed not found: draftRecordId=${draftRecordId}`);
94982
95040
  return [];
94983
95041
  }
94984
95042
  incrementCounter(ProductConsumedDeletedHook.COUNTER_NAME, CounterResult.Created);
94985
95043
  const effect = {
94986
95044
  type: 'record-field-increment',
94987
- fieldName: ProductItem.Fields.QUANTITY_ON_HAND,
95045
+ fieldName: ProductItem$1.Fields.QUANTITY_ON_HAND,
94988
95046
  value: quantityConsumed,
94989
95047
  key: buildRecordRepKeyFromId$1(productItemId),
94990
95048
  tag: draftActionId,
@@ -94995,13 +95053,115 @@ class ProductConsumedDeletedHook {
94995
95053
  }
94996
95054
  }
94997
95055
  ProductConsumedDeletedHook.COUNTER_NAME = 'ProductConsumedDeletedHook';
94998
- function getProductItemId(fields) {
94999
- const field = fields[ProductConsumed.Fields.PRODUCT_ITEM_ID];
95000
- if (field === undefined || typeof field !== 'string') {
95001
- return undefined;
95056
+ /**
95057
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-created
95058
+ * `ProductConsumed` record.
95059
+ *
95060
+ * `Product2Id` is not createable on `ProductConsumed`, so the draft only carries the
95061
+ * `ProductItemId`. The value is resolved through the related `ProductItem` record (see
95062
+ * `resolveIsProduct2SerializedFromProductItem`): the ProductItem's own
95063
+ * `IsProduct2Serialized` when cached, otherwise `ProductItem.Product2Id` ->
95064
+ * `Product2.IsSerialized`. When the chain cannot be resolved the hook exits without an
95065
+ * effect and the field is left as synthesized (null).
95066
+ */
95067
+ class ProductConsumedIsSerializedCreatedHook {
95068
+ constructor(durableRecordStore) {
95069
+ this.durableRecordStore = durableRecordStore;
95070
+ this.on = 'post';
95071
+ this.apiName = ProductConsumed.API_NAME;
95072
+ }
95073
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95074
+ const productItemId = getStringDraftField(fields, ProductConsumed.Fields.PRODUCT_ITEM_ID);
95075
+ if (productItemId === undefined) {
95076
+ incrementCounter(ProductConsumedIsSerializedCreatedHook.COUNTER_NAME, CounterResult.Exited);
95077
+ return [];
95078
+ }
95079
+ const isSerialized = await resolveIsProduct2SerializedFromProductItem(reader ?? this.durableRecordStore, productItemId, ProductConsumedIsSerializedCreatedHook.COUNTER_NAME);
95080
+ if (isSerialized === undefined) {
95081
+ return [];
95082
+ }
95083
+ const effect = {
95084
+ type: 'record-field-set',
95085
+ fieldName: ProductConsumed.Fields.IS_PRODUCT2_SERIALIZED,
95086
+ value: isSerialized,
95087
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95088
+ tag: draftActionId,
95089
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95090
+ timestamp: draftActionTimestamp,
95091
+ };
95092
+ return [effect];
95093
+ }
95094
+ }
95095
+ ProductConsumedIsSerializedCreatedHook.COUNTER_NAME = 'ProductConsumedIsSerializedCreatedHook';
95096
+ /**
95097
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-edited
95098
+ * `ProductConsumed` record.
95099
+ *
95100
+ * A `patch` body only carries changed fields, so the related `ProductItemId` may be absent.
95101
+ * The id is resolved by preferring the patch body (the link is being reassigned) and falling
95102
+ * back to the original record in the durable store, then the value is resolved through that
95103
+ * `ProductItem` exactly as on create (see `resolveIsProduct2SerializedFromProductItem`).
95104
+ *
95105
+ * When the patch explicitly clears `ProductItemId` (sets it to `null`) the record no longer
95106
+ * derives from any `ProductItem`, so `IsProduct2Serialized` is cleared (set to `null`) rather
95107
+ * than resolved from the now-stale original link. Likewise, when the patch reassigns
95108
+ * `ProductItemId` to a new id whose chain isn't cached, the stored value reflects the old link
95109
+ * and is stale, so it is cleared. When the link is unchanged and its chain cannot be resolved,
95110
+ * the stored value is still correct so the hook exits without an effect and leaves it unchanged.
95111
+ */
95112
+ class ProductConsumedIsSerializedUpdatedHook {
95113
+ constructor(durableRecordStore) {
95114
+ this.durableRecordStore = durableRecordStore;
95115
+ this.on = 'patch';
95116
+ this.apiName = ProductConsumed.API_NAME;
95117
+ }
95118
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95119
+ const recordReader = reader ?? this.durableRecordStore;
95120
+ const originalRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
95121
+ if (originalRecord === undefined) {
95122
+ incrementCounter(ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Error);
95123
+ instrumentation$4.error(`SFS - ProductConsumedIsSerializedUpdatedHook original record not found: draftRecordId=${draftRecordId}`);
95124
+ return [];
95125
+ }
95126
+ const reference = resolveReferenceIdForUpdate(fields, originalRecord.fields, ProductConsumed.Fields.PRODUCT_ITEM_ID);
95127
+ if (reference.kind === 'unresolved') {
95128
+ incrementCounter(ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Exited);
95129
+ return [];
95130
+ }
95131
+ let isSerialized;
95132
+ if (reference.kind === 'cleared') {
95133
+ // The link was explicitly cleared: the record derives from no ProductItem, so clear
95134
+ // IsProduct2Serialized rather than resolving it from the stale original link.
95135
+ incrementCounter(ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95136
+ isSerialized = null;
95137
+ }
95138
+ else {
95139
+ isSerialized = await resolveIsProduct2SerializedFromProductItem(recordReader, reference.id, ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME);
95140
+ if (isSerialized === undefined && reference.source === 'patch') {
95141
+ // The link was reassigned to a new ProductItem whose chain isn't cached yet, so
95142
+ // the stored IsProduct2Serialized still reflects the OLD link and is now stale.
95143
+ // Clear it rather than leaving the stale value alongside the new relationship;
95144
+ // the server recomputes the real value on sync.
95145
+ incrementCounter(ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95146
+ isSerialized = null;
95147
+ }
95148
+ }
95149
+ if (isSerialized === undefined) {
95150
+ return [];
95151
+ }
95152
+ const effect = {
95153
+ type: 'record-field-set',
95154
+ fieldName: ProductConsumed.Fields.IS_PRODUCT2_SERIALIZED,
95155
+ value: isSerialized,
95156
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95157
+ tag: draftActionId,
95158
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95159
+ timestamp: draftActionTimestamp,
95160
+ };
95161
+ return [effect];
95002
95162
  }
95003
- return field;
95004
95163
  }
95164
+ ProductConsumedIsSerializedUpdatedHook.COUNTER_NAME = 'ProductConsumedIsSerializedUpdatedHook';
95005
95165
  function getProductItemIdFromRecordFields(fields) {
95006
95166
  const field = fields[ProductConsumed.Fields.PRODUCT_ITEM_ID];
95007
95167
  if (field === undefined || field.value === undefined || typeof field.value !== 'string') {
@@ -95023,29 +95183,265 @@ function getQuantityConsumedFromRecordFields(fields) {
95023
95183
  }
95024
95184
  return field.value;
95025
95185
  }
95026
- var CounterResult;
95027
- (function (CounterResult) {
95028
- // A side effect was created.
95029
- CounterResult["Created"] = "Created";
95030
- // The hook exited early.
95031
- CounterResult["Exited"] = "Exited";
95032
- // An unexpected error occurred.
95033
- CounterResult["Error"] = "Error";
95034
- })(CounterResult || (CounterResult = {}));
95035
- function incrementCounter(name, result) {
95036
- instrumentation$4.incrementCounter('SFSSideEffect', 1, result === CounterResult.Error, {
95037
- Name: name,
95038
- Result: result,
95039
- });
95186
+
95187
+ const ProductTransfer = {
95188
+ API_NAME: 'ProductTransfer',
95189
+ Fields: {
95190
+ SOURCE_PRODUCT_ITEM_ID: 'SourceProductItemId',
95191
+ IS_PRODUCT2_SERIALIZED: 'IsProduct2Serialized',
95192
+ },
95193
+ };
95194
+ /**
95195
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-created
95196
+ * `ProductTransfer` record.
95197
+ *
95198
+ * The value is resolved through the related `SourceProductItem` record (a `ProductItem`),
95199
+ * mirroring the `ProductConsumed` hook: the ProductItem's own `IsProduct2Serialized` when
95200
+ * cached, otherwise `ProductItem.Product2Id` -> `Product2.IsSerialized` (see
95201
+ * `resolveIsProduct2SerializedFromProductItem`). When the chain cannot be resolved the hook
95202
+ * exits without an effect and the field is left as synthesized (null).
95203
+ */
95204
+ class ProductTransferIsSerializedCreatedHook {
95205
+ constructor(durableRecordStore) {
95206
+ this.durableRecordStore = durableRecordStore;
95207
+ this.on = 'post';
95208
+ this.apiName = ProductTransfer.API_NAME;
95209
+ }
95210
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95211
+ const sourceProductItemId = getStringDraftField(fields, ProductTransfer.Fields.SOURCE_PRODUCT_ITEM_ID);
95212
+ if (sourceProductItemId === undefined) {
95213
+ incrementCounter(ProductTransferIsSerializedCreatedHook.COUNTER_NAME, CounterResult.Exited);
95214
+ return [];
95215
+ }
95216
+ const isSerialized = await resolveIsProduct2SerializedFromProductItem(reader ?? this.durableRecordStore, sourceProductItemId, ProductTransferIsSerializedCreatedHook.COUNTER_NAME);
95217
+ if (isSerialized === undefined) {
95218
+ return [];
95219
+ }
95220
+ const effect = {
95221
+ type: 'record-field-set',
95222
+ fieldName: ProductTransfer.Fields.IS_PRODUCT2_SERIALIZED,
95223
+ value: isSerialized,
95224
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95225
+ tag: draftActionId,
95226
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95227
+ timestamp: draftActionTimestamp,
95228
+ };
95229
+ return [effect];
95230
+ }
95231
+ }
95232
+ ProductTransferIsSerializedCreatedHook.COUNTER_NAME = 'ProductTransferIsSerializedCreatedHook';
95233
+ /**
95234
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-edited
95235
+ * `ProductTransfer` record.
95236
+ *
95237
+ * ProductTransfer can only be edited (not created) on mobile, so this is the hook that fires
95238
+ * in practice. A `patch` body only carries changed fields, so the related `SourceProductItemId`
95239
+ * may be absent; the id is resolved by preferring the patch body (the link is being reassigned)
95240
+ * and falling back to the original record in the durable store, then the value is resolved
95241
+ * through that `ProductItem` exactly as on create (see
95242
+ * `resolveIsProduct2SerializedFromProductItem`).
95243
+ *
95244
+ * When the patch explicitly clears `SourceProductItemId` (sets it to `null`) the record no
95245
+ * longer derives from any `ProductItem`, so `IsProduct2Serialized` is cleared (set to `null`)
95246
+ * rather than resolved from the now-stale original link. Likewise, when the patch reassigns
95247
+ * `SourceProductItemId` to a new id whose chain isn't cached, the stored value reflects the old
95248
+ * link and is stale, so it is cleared. When the link is unchanged and its chain cannot be
95249
+ * resolved, the stored value is still correct so the hook exits without an effect and leaves it
95250
+ * unchanged.
95251
+ */
95252
+ class ProductTransferIsSerializedUpdatedHook {
95253
+ constructor(durableRecordStore) {
95254
+ this.durableRecordStore = durableRecordStore;
95255
+ this.on = 'patch';
95256
+ this.apiName = ProductTransfer.API_NAME;
95257
+ }
95258
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95259
+ const recordReader = reader ?? this.durableRecordStore;
95260
+ const originalRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
95261
+ if (originalRecord === undefined) {
95262
+ incrementCounter(ProductTransferIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Error);
95263
+ instrumentation$4.error(`SFS - ProductTransferIsSerializedUpdatedHook original record not found: draftRecordId=${draftRecordId}`);
95264
+ return [];
95265
+ }
95266
+ const reference = resolveReferenceIdForUpdate(fields, originalRecord.fields, ProductTransfer.Fields.SOURCE_PRODUCT_ITEM_ID);
95267
+ if (reference.kind === 'unresolved') {
95268
+ incrementCounter(ProductTransferIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Exited);
95269
+ return [];
95270
+ }
95271
+ let isSerialized;
95272
+ if (reference.kind === 'cleared') {
95273
+ // The link was explicitly cleared: the record derives from no ProductItem, so clear
95274
+ // IsProduct2Serialized rather than resolving it from the stale original link.
95275
+ incrementCounter(ProductTransferIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95276
+ isSerialized = null;
95277
+ }
95278
+ else {
95279
+ isSerialized = await resolveIsProduct2SerializedFromProductItem(recordReader, reference.id, ProductTransferIsSerializedUpdatedHook.COUNTER_NAME);
95280
+ if (isSerialized === undefined && reference.source === 'patch') {
95281
+ // The link was reassigned to a new ProductItem whose chain isn't cached yet, so
95282
+ // the stored IsProduct2Serialized still reflects the OLD link and is now stale.
95283
+ // Clear it rather than leaving the stale value alongside the new relationship;
95284
+ // the server recomputes the real value on sync.
95285
+ incrementCounter(ProductTransferIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95286
+ isSerialized = null;
95287
+ }
95288
+ }
95289
+ if (isSerialized === undefined) {
95290
+ return [];
95291
+ }
95292
+ const effect = {
95293
+ type: 'record-field-set',
95294
+ fieldName: ProductTransfer.Fields.IS_PRODUCT2_SERIALIZED,
95295
+ value: isSerialized,
95296
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95297
+ tag: draftActionId,
95298
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95299
+ timestamp: draftActionTimestamp,
95300
+ };
95301
+ return [effect];
95302
+ }
95040
95303
  }
95304
+ ProductTransferIsSerializedUpdatedHook.COUNTER_NAME = 'ProductTransferIsSerializedUpdatedHook';
95305
+
95306
+ const ProductItem = {
95307
+ API_NAME: 'ProductItem',
95308
+ Fields: {
95309
+ PRODUCT2_ID: 'Product2Id',
95310
+ IS_PRODUCT2_SERIALIZED: 'IsProduct2Serialized',
95311
+ },
95312
+ };
95313
+ /**
95314
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-created
95315
+ * `ProductItem` record.
95316
+ *
95317
+ * Unlike `ProductConsumed`/`ProductTransfer` — which reach the product through a related
95318
+ * `ProductItem` — a `ProductItem` carries `Product2Id` directly, so the value is resolved as
95319
+ * `Product2Id` -> `Product2.IsSerialized` (see `resolveIsSerializedFromProduct2Id`). When the
95320
+ * chain cannot be resolved the hook exits without an effect and the field is left as
95321
+ * synthesized (null).
95322
+ */
95323
+ class ProductItemIsSerializedCreatedHook {
95324
+ constructor(durableRecordStore) {
95325
+ this.durableRecordStore = durableRecordStore;
95326
+ this.on = 'post';
95327
+ this.apiName = ProductItem.API_NAME;
95328
+ }
95329
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95330
+ const product2Id = getStringDraftField(fields, ProductItem.Fields.PRODUCT2_ID);
95331
+ if (product2Id === undefined) {
95332
+ incrementCounter(ProductItemIsSerializedCreatedHook.COUNTER_NAME, CounterResult.Exited);
95333
+ return [];
95334
+ }
95335
+ const isSerialized = await resolveIsSerializedFromProduct2Id(reader ?? this.durableRecordStore, product2Id, ProductItemIsSerializedCreatedHook.COUNTER_NAME);
95336
+ if (isSerialized === undefined) {
95337
+ return [];
95338
+ }
95339
+ const effect = {
95340
+ type: 'record-field-set',
95341
+ fieldName: ProductItem.Fields.IS_PRODUCT2_SERIALIZED,
95342
+ value: isSerialized,
95343
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95344
+ tag: draftActionId,
95345
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95346
+ timestamp: draftActionTimestamp,
95347
+ };
95348
+ return [effect];
95349
+ }
95350
+ }
95351
+ ProductItemIsSerializedCreatedHook.COUNTER_NAME = 'ProductItemIsSerializedCreatedHook';
95352
+ /**
95353
+ * Replicates the server-derived `IsProduct2Serialized` boolean on a draft-edited
95354
+ * `ProductItem` record.
95355
+ *
95356
+ * A `patch` body only carries changed fields, so `Product2Id` may be absent; it is resolved by
95357
+ * preferring the patch body (the product is being reassigned) and falling back to the original
95358
+ * record in the durable store, then resolved as `Product2Id` -> `Product2.IsSerialized` (see
95359
+ * `resolveIsSerializedFromProduct2Id`).
95360
+ *
95361
+ * When the patch explicitly clears `Product2Id` (sets it to `null`) the record no longer
95362
+ * derives from any `Product2`, so `IsProduct2Serialized` is cleared (set to `null`) rather than
95363
+ * resolved from the now-stale original link. Likewise, when the patch reassigns `Product2Id` to
95364
+ * a new id whose `Product2` isn't cached, the stored value reflects the old link and is stale,
95365
+ * so it is cleared. When the link is unchanged and its `Product2` cannot be resolved, the stored
95366
+ * value is still correct so the hook exits without an effect and leaves it unchanged.
95367
+ */
95368
+ class ProductItemIsSerializedUpdatedHook {
95369
+ constructor(durableRecordStore) {
95370
+ this.durableRecordStore = durableRecordStore;
95371
+ this.on = 'patch';
95372
+ this.apiName = ProductItem.API_NAME;
95373
+ }
95374
+ async then(draftActionId, draftActionTimestamp, draftRecordId, fields, reader) {
95375
+ const recordReader = reader ?? this.durableRecordStore;
95376
+ const originalRecord = await recordReader.getRecord(buildRecordRepKeyFromId$1(draftRecordId));
95377
+ if (originalRecord === undefined) {
95378
+ incrementCounter(ProductItemIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Error);
95379
+ instrumentation$4.error(`SFS - ProductItemIsSerializedUpdatedHook original record not found: draftRecordId=${draftRecordId}`);
95380
+ return [];
95381
+ }
95382
+ const reference = resolveReferenceIdForUpdate(fields, originalRecord.fields, ProductItem.Fields.PRODUCT2_ID);
95383
+ if (reference.kind === 'unresolved') {
95384
+ incrementCounter(ProductItemIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Exited);
95385
+ return [];
95386
+ }
95387
+ let isSerialized;
95388
+ if (reference.kind === 'cleared') {
95389
+ // The link was explicitly cleared: the record derives from no Product2, so clear
95390
+ // IsProduct2Serialized rather than resolving it from the stale original link.
95391
+ incrementCounter(ProductItemIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95392
+ isSerialized = null;
95393
+ }
95394
+ else {
95395
+ isSerialized = await resolveIsSerializedFromProduct2Id(recordReader, reference.id, ProductItemIsSerializedUpdatedHook.COUNTER_NAME);
95396
+ if (isSerialized === undefined && reference.source === 'patch') {
95397
+ // The link was reassigned to a new Product2 that isn't cached yet, so the stored
95398
+ // IsProduct2Serialized still reflects the OLD link and is now stale. Clear it
95399
+ // rather than leaving the stale value alongside the new relationship; the server
95400
+ // recomputes the real value on sync.
95401
+ incrementCounter(ProductItemIsSerializedUpdatedHook.COUNTER_NAME, CounterResult.Created);
95402
+ isSerialized = null;
95403
+ }
95404
+ }
95405
+ if (isSerialized === undefined) {
95406
+ return [];
95407
+ }
95408
+ const effect = {
95409
+ type: 'record-field-set',
95410
+ fieldName: ProductItem.Fields.IS_PRODUCT2_SERIALIZED,
95411
+ value: isSerialized,
95412
+ key: buildRecordRepKeyFromId$1(draftRecordId),
95413
+ tag: draftActionId,
95414
+ uniqueId: `${draftActionId}-IsProduct2Serialized`,
95415
+ timestamp: draftActionTimestamp,
95416
+ };
95417
+ return [effect];
95418
+ }
95419
+ }
95420
+ ProductItemIsSerializedUpdatedHook.COUNTER_NAME = 'ProductItemIsSerializedUpdatedHook';
95041
95421
 
95042
95422
  function createSfsSideEffectHooks(durableRecordStore) {
95043
95423
  if (productConsumedSideEffects.isOpen({ fallback: false })) {
95044
- return [
95424
+ const hooks = [
95045
95425
  new ProductConsumedCreatedHook(),
95046
95426
  new ProductConsumedUpdatedHook(durableRecordStore),
95047
95427
  new ProductConsumedDeletedHook(durableRecordStore),
95048
95428
  ];
95429
+ // The IsProduct2Serialized side effects (ProductConsumed, ProductTransfer and
95430
+ // ProductItem) ship on by default and can be disabled independently of the van stock
95431
+ // hooks above via a shared kill switch: the gate is closed (fallback false) by default,
95432
+ // which keeps the effects enabled, and opening the gate turns them off.
95433
+ //
95434
+ // Each object gets both a create (post) and edit (patch) hook so the field is
95435
+ // synthesized on either draft action, keeping the behavior uniform across the objects.
95436
+ if (!isProduct2SerializedSideEffectKillSwitch.isOpen({ fallback: false })) {
95437
+ hooks.push(new ProductConsumedIsSerializedCreatedHook(durableRecordStore));
95438
+ hooks.push(new ProductConsumedIsSerializedUpdatedHook(durableRecordStore));
95439
+ hooks.push(new ProductTransferIsSerializedCreatedHook(durableRecordStore));
95440
+ hooks.push(new ProductTransferIsSerializedUpdatedHook(durableRecordStore));
95441
+ hooks.push(new ProductItemIsSerializedCreatedHook(durableRecordStore));
95442
+ hooks.push(new ProductItemIsSerializedUpdatedHook(durableRecordStore));
95443
+ }
95444
+ return hooks;
95049
95445
  }
95050
95446
  return [];
95051
95447
  }
@@ -95369,6 +95765,15 @@ function buildServiceDescriptor$m() {
95369
95765
  };
95370
95766
  }
95371
95767
 
95768
+ function applyOperationRequestOptions$1(init, operationType) {
95769
+ if (operationType === void 0) {
95770
+ return init;
95771
+ }
95772
+ return {
95773
+ ...init,
95774
+ onestore: { ...init?.onestore, retrySafe: operationType === "query" }
95775
+ };
95776
+ }
95372
95777
  class AuraNetworkCommand extends NetworkCommand$1 {
95373
95778
  constructor(services) {
95374
95779
  super(services);
@@ -95387,6 +95792,18 @@ class AuraNetworkCommand extends NetworkCommand$1 {
95387
95792
  "Fetch parameters must be specified when using HTTP transport on an Aura adapter"
95388
95793
  );
95389
95794
  }
95795
+ /**
95796
+ * The command's operation type. Generated command subclasses override this
95797
+ * getter with their concrete operation (see the generator's
95798
+ * `generateOperationType`); the base returns `undefined` for commands that
95799
+ * don't declare one. On the HTTP fallback path (`fetch`) it derives the
95800
+ * request's `onestore.retrySafe` — reads (`query`) are retry-safe, writes
95801
+ * (`mutation`) are not. Mirrors `FetchNetworkCommand.operationType`, which
95802
+ * this class does not inherit (it extends `NetworkCommand` directly).
95803
+ */
95804
+ get operationType() {
95805
+ return void 0;
95806
+ }
95390
95807
  coerceAuraErrors(auraErrors) {
95391
95808
  return new UserVisibleError(getFetchResponseFromAuraError(auraErrors[0]));
95392
95809
  }
@@ -95488,9 +95905,11 @@ class AuraNetworkCommand extends NetworkCommand$1 {
95488
95905
  this.coerceAuraErrors
95489
95906
  );
95490
95907
  } else if (this.shouldUseFetch()) {
95491
- const params = this.fetchParams;
95908
+ const [input, init] = this.fetchParams;
95909
+ const initWithOptions = applyOperationRequestOptions$1(init, this.operationType);
95492
95910
  try {
95493
- return this.convertFetchResponseToData(this.services.fetch(...params));
95911
+ const fetchCall = initWithOptions === void 0 ? this.services.fetch(input) : this.services.fetch(input, initWithOptions);
95912
+ return this.convertFetchResponseToData(fetchCall);
95494
95913
  } catch (reason) {
95495
95914
  return resolvedPromiseLike$2(err$1(toError(reason)));
95496
95915
  }
@@ -96237,6 +96656,15 @@ class NetworkCommand extends BaseCommand {
96237
96656
  async afterRequestHooks(_options) {
96238
96657
  }
96239
96658
  }
96659
+ function applyOperationRequestOptions(init, operationType) {
96660
+ if (operationType === void 0) {
96661
+ return init;
96662
+ }
96663
+ return {
96664
+ ...init,
96665
+ onestore: { ...init?.onestore, retrySafe: operationType === "query" }
96666
+ };
96667
+ }
96240
96668
  function isAbortableCommand(command) {
96241
96669
  return !!command && typeof command === "object" && "isAbortableCommand" in command && command.isAbortableCommand === true;
96242
96670
  }
@@ -96309,7 +96737,8 @@ const _FetchNetworkCommand = class _FetchNetworkCommand extends NetworkCommand {
96309
96737
  * `generateOperationType`); the base returns `undefined` for commands that
96310
96738
  * don't declare one (e.g. hand-written commands). Transport-layer
96311
96739
  * interceptors use it to tell a read (`query`) apart from a write — see
96312
- * `fetch`, which folds it into the context seed.
96740
+ * `fetch`, which folds it into the context seed and derives
96741
+ * `onestore.retrySafe` from it.
96313
96742
  */
96314
96743
  get operationType() {
96315
96744
  return void 0;
@@ -96318,8 +96747,9 @@ const _FetchNetworkCommand = class _FetchNetworkCommand extends NetworkCommand {
96318
96747
  try {
96319
96748
  const [input, init] = this.fetchParams;
96320
96749
  const operationType = this.operationType;
96750
+ const initWithOptions = applyOperationRequestOptions(init, operationType);
96321
96751
  const seed = operationType === void 0 ? contextSeed : { operationType, ...contextSeed };
96322
- const initWithSeed = seed === void 0 ? init : { ...init, __contextSeed: seed };
96752
+ const initWithSeed = seed === void 0 ? initWithOptions : { ...initWithOptions, __contextSeed: seed };
96323
96753
  const fetchCall = initWithSeed === void 0 ? this.services.fetch(input) : this.services.fetch(input, initWithSeed);
96324
96754
  return this.convertFetchResponseToData(fetchCall);
96325
96755
  } catch (reason) {
@@ -97413,7 +97843,7 @@ function buildServiceDescriptor$b(luvio) {
97413
97843
  },
97414
97844
  };
97415
97845
  }
97416
- // version: 1.461.0-37b1c9d1cb
97846
+ // version: 1.463.0-ea9ecd17fc
97417
97847
 
97418
97848
  /**
97419
97849
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -97439,7 +97869,7 @@ function buildServiceDescriptor$a(notifyRecordUpdateAvailable, getNormalizedLuvi
97439
97869
  },
97440
97870
  };
97441
97871
  }
97442
- // version: 1.461.0-37b1c9d1cb
97872
+ // version: 1.463.0-ea9ecd17fc
97443
97873
 
97444
97874
  function findExecutableOperation$1(input) {
97445
97875
  const operations = input.query.definitions.filter(
@@ -98020,9 +98450,18 @@ function validateDiscriminatedOneOf(data, discriminator, path, document, visited
98020
98450
  if (data === null || typeof data !== "object" || Array.isArray(data)) {
98021
98451
  return invalidSchemaResponseWithError(incorrectTypeError("object", typeof data, path));
98022
98452
  }
98023
- const { propertyName, mapping } = discriminator;
98453
+ const { propertyName, mapping, defaultMapping } = discriminator;
98024
98454
  const discriminatorValue = data[propertyName];
98025
98455
  if (discriminatorValue === void 0) {
98456
+ if (defaultMapping !== void 0) {
98457
+ return validateJsonSchemaInternal(
98458
+ data,
98459
+ { $ref: defaultMapping },
98460
+ path,
98461
+ document,
98462
+ visited
98463
+ );
98464
+ }
98026
98465
  return invalidSchemaResponseWithError(
98027
98466
  new MissingRequiredPropertyError(
98028
98467
  `Object at path '${path}' is missing required discriminator property '${propertyName}'.`
@@ -98034,7 +98473,7 @@ function validateDiscriminatedOneOf(data, discriminator, path, document, visited
98034
98473
  incorrectTypeError("string", typeof discriminatorValue, `${path}.${propertyName}`)
98035
98474
  );
98036
98475
  }
98037
- const targetRef = mapping[discriminatorValue];
98476
+ const targetRef = mapping[discriminatorValue] ?? defaultMapping;
98038
98477
  if (targetRef === void 0) {
98039
98478
  return invalidSchemaResponseWithError(
98040
98479
  new JsonSchemaViolationError(
@@ -99828,9 +100267,6 @@ class NimbusSqliteOneStoreCache {
99828
100267
 
99829
100268
  const PURGE_CACHE_DAYS = 30;
99830
100269
  function initializeOneStore(sqliteStore, luvio) {
99831
- if (!useOneStore.isOpen({ fallback: true })) {
99832
- return;
99833
- }
99834
100270
  // Initialize performance.now() fallback if not available
99835
100271
  if (typeof globalThis.performance === 'undefined') {
99836
100272
  globalThis.performance = { now: () => Date.now() };
@@ -100017,12 +100453,6 @@ function getRuntime({ primeUser = false } = {}) {
100017
100453
  lazyInternalLuvio = internalLuvio;
100018
100454
  lazyObjectInfoService = new ObjectInfoService(getObjectInfo, getObjectInfos, getObjectInfoDirectory, lazyDurableStore);
100019
100455
  const baseEnv = new Environment(store, lazyNetworkAdapter);
100020
- const shouldFilterFieldsOnRevive = (key) => {
100021
- if (reviveOnlyRequestedFields.isOpen({ fallback: true })) {
100022
- return isStoreKeyRecordId(key);
100023
- }
100024
- return false;
100025
- };
100026
100456
  const gqlEnv = makeEnvironmentGraphqlAware(baseEnv);
100027
100457
  const durableEnv = makeDurable(gqlEnv, {
100028
100458
  durableStore: lazyDurableStore,
@@ -100030,7 +100460,12 @@ function getRuntime({ primeUser = false } = {}) {
100030
100460
  // disable luvio deep freeze in headless environments
100031
100461
  disableDeepFreeze: typeof window === 'undefined',
100032
100462
  shouldFlush,
100033
- shouldFilterFieldsOnRevive,
100463
+ // Disabling field filtering on revive for now (previously gated behind
100464
+ // the lmr.reviveOnlyRequestedFields gate). The code path is retained
100465
+ // below should we need to re-enable this behavior in the future:
100466
+ shouldFilterFieldsOnRevive: (_key) => {
100467
+ return false;
100468
+ },
100034
100469
  });
100035
100470
  // draft queue
100036
100471
  lazyDraftQueue = buildLdsDraftQueue(lazyDurableStore);
@@ -100148,7 +100583,7 @@ register$1({
100148
100583
  id: '@salesforce/lds-network-adapter',
100149
100584
  instrument: instrument$2,
100150
100585
  });
100151
- // version: 1.461.0-37b1c9d1cb
100586
+ // version: 1.463.0-ea9ecd17fc
100152
100587
 
100153
100588
  const { create: create$2, keys: keys$2 } = Object;
100154
100589
  const { stringify, parse } = JSON;
@@ -107700,7 +108135,7 @@ function registerCallback(cb) {
107700
108135
  cb(graphql_v1_import, graphql_imperative$1, graphql_imperative_legacy_v1_import, graphql_state_manager, useOneStoreGraphQL);
107701
108136
  }
107702
108137
  }
107703
- // version: 1.461.0-55ae54e753
108138
+ // version: 1.463.0-8515018faf
107704
108139
 
107705
108140
  function createFragmentMap(documentNode) {
107706
108141
  const fragments = {};
@@ -136943,7 +137378,7 @@ register$1({
136943
137378
  configuration: { ...configurationForGraphQLAdapters$1 },
136944
137379
  instrument: instrument$1,
136945
137380
  });
136946
- // version: 1.461.0-55ae54e753
137381
+ // version: 1.463.0-8515018faf
136947
137382
 
136948
137383
  // On core the unstable adapters are re-exported with different names,
136949
137384
  // we want to match them here.
@@ -137095,7 +137530,7 @@ withDefaultLuvio((luvio) => {
137095
137530
  unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
137096
137531
  graphQLImperative = ldsAdapter;
137097
137532
  });
137098
- // version: 1.461.0-55ae54e753
137533
+ // version: 1.463.0-8515018faf
137099
137534
 
137100
137535
  var gqlApi = /*#__PURE__*/Object.freeze({
137101
137536
  __proto__: null,
@@ -137418,19 +137853,7 @@ function invokeDmlAdapter(adapter, configObject, onResponse, nativeAdapterReques
137418
137853
  function invokeAdapterWithDraftToReplace(adapterId, config, draftIdToReplace, onResponse, nativeAdapterRequestContext) {
137419
137854
  invokeAdapterWithDraftToMerge(adapterId, config, draftIdToReplace, onResponse, nativeAdapterRequestContext);
137420
137855
  }
137421
- /**
137422
- * Executes the specified adapter with the given @param adapterId and @param config. Then
137423
- * it merges the draft with the given id with the draft generated by
137424
- * the mutating adapter. Will call onResult callback once with data or error.
137425
- *
137426
- * This function returns an error if the given adapterId cannot be found, or if the
137427
- * adapterId is not a mutating adapter if a draft isn't created, or
137428
- * if it fails to parse the given config string.
137429
- *
137430
- * If the @param adapterId is deleteRecod, the invocation of it will generate a delele draft.
137431
- * The newly generated delete draft will replace the draft specified by @param targetDraftId
137432
- */
137433
- function invokeAdapterWithDraftToMerge(adapterId, config, targetDraftId, onResponse, nativeAdapterRequestContext) {
137856
+ function invokeAdapterWithDraftToMergeAndMetadata(adapterId, config, targetDraftId, metadata, onResponse, nativeAdapterRequestContext) {
137434
137857
  const adapter = getDMLAdapterFromName(adapterId);
137435
137858
  if (adapter === undefined) {
137436
137859
  // Check if the adapter is non-mutating adapter and create proper error message.
@@ -137446,10 +137869,12 @@ function invokeAdapterWithDraftToMerge(adapterId, config, targetDraftId, onRespo
137446
137869
  // deleteRecord adapter call will generate a delete draft and
137447
137870
  // the newly generate draft will replace the target draft
137448
137871
  if (adapterId === 'deleteRecord') {
137449
- invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, targetDraftId, onResponse, nativeAdapterRequestContext);
137872
+ invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, targetDraftId, metadata, onResponse, nativeAdapterRequestContext);
137450
137873
  return;
137451
137874
  }
137452
- draftManager.getQueue().then((draftInfo) => {
137875
+ draftManager
137876
+ .getQueue()
137877
+ .then((draftInfo) => {
137453
137878
  const draftIds = draftInfo.items.map((draft) => draft.id);
137454
137879
  if (draftIds.includes(targetDraftId) === false) {
137455
137880
  onResponse({
@@ -137466,14 +137891,32 @@ function invokeAdapterWithDraftToMerge(adapterId, config, targetDraftId, onRespo
137466
137891
  const draftId = draftIds[draftIds.length - 1];
137467
137892
  draftManager
137468
137893
  .mergeActions(targetDraftId, draftId)
137469
- .then(() => {
137894
+ .then(async () => {
137470
137895
  if (responseValue.data === undefined ||
137471
137896
  responseValue.data.drafts === undefined) {
137472
137897
  onResponse(responseValue);
137473
137898
  return;
137474
137899
  }
137475
137900
  const newResponse = removeDraftNodeActionId(draftId, responseValue);
137476
- onResponse(newResponse);
137901
+ if (metadata !== undefined) {
137902
+ const mergedState = await draftManager.getQueue();
137903
+ const mergedItem = mergedState.items.find((item) => item.id === targetDraftId);
137904
+ const mergedMetadata = mergedItem?.metadata ?? {};
137905
+ draftManager
137906
+ .setMetadata(targetDraftId, {
137907
+ ...mergedMetadata,
137908
+ ...metadata,
137909
+ })
137910
+ .then(() => {
137911
+ onResponse(newResponse);
137912
+ })
137913
+ .catch((error) => {
137914
+ onResponse(convertErrorIntoNativeFetchError(error, 'Unable to set metadata on draft'));
137915
+ });
137916
+ }
137917
+ else {
137918
+ onResponse(newResponse);
137919
+ }
137477
137920
  })
137478
137921
  .catch((error) => {
137479
137922
  onResponse(convertErrorIntoNativeFetchError(error, `Unknown error merging draft`));
@@ -137485,8 +137928,26 @@ function invokeAdapterWithDraftToMerge(adapterId, config, targetDraftId, onRespo
137485
137928
  onResponse(response);
137486
137929
  }
137487
137930
  }, nativeAdapterRequestContext);
137931
+ })
137932
+ .catch((error) => {
137933
+ onResponse(convertErrorIntoNativeFetchError(error, 'Unable to get draft queue'));
137488
137934
  });
137489
137935
  }
137936
+ /**
137937
+ * Executes the specified adapter with the given @param adapterId and @param config. Then
137938
+ * it merges the draft with the given id with the draft generated by
137939
+ * the mutating adapter. Will call onResult callback once with data or error.
137940
+ *
137941
+ * This function returns an error if the given adapterId cannot be found, or if the
137942
+ * adapterId is not a mutating adapter if a draft isn't created, or
137943
+ * if it fails to parse the given config string.
137944
+ *
137945
+ * If the @param adapterId is deleteRecod, the invocation of it will generate a delele draft.
137946
+ * The newly generated delete draft will replace the draft specified by @param targetDraftId
137947
+ */
137948
+ function invokeAdapterWithDraftToMerge(adapterId, config, targetDraftId, onResponse, nativeAdapterRequestContext) {
137949
+ invokeAdapterWithDraftToMergeAndMetadata(adapterId, config, targetDraftId, undefined, onResponse, nativeAdapterRequestContext);
137950
+ }
137490
137951
  /**
137491
137952
  * Executes the specified adapter with the given adapterId and config. Then
137492
137953
  * it sets the given metadata on the draft created by the mutating adapter. Will call
@@ -137622,18 +138083,20 @@ This is a special case version of the invokeAdapterWithDraftToMerge function
137622
138083
  which should only be used for the deleteRecord wire adapter, since it does not
137623
138084
  contain record data in the result and has to do special querying of the draft queue
137624
138085
  */
137625
- function invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, draftIdToReplace, onResponse, nativeAdapterRequestContext) {
138086
+ function invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, draftIdToReplace, metadata, onResponse, nativeAdapterRequestContext) {
137626
138087
  const targetedRecordId = parse(config);
137627
138088
  let priorDraftIds;
137628
- draftManager.getQueue().then((draftState) => {
137629
- priorDraftIds = draftState.items.map((item) => {
137630
- return item.id;
137631
- });
138089
+ draftManager
138090
+ .getQueue()
138091
+ .then((draftState) => {
138092
+ priorDraftIds = draftState.items.map((item) => item.id);
137632
138093
  invokeDmlAdapter(adapter, parse(config), (responseValue) => {
137633
138094
  if (responseValue.error === undefined &&
137634
138095
  responseValue.errors === undefined &&
137635
138096
  responseValue.data === undefined) {
137636
- draftManager.getQueue().then((newState) => {
138097
+ draftManager
138098
+ .getQueue()
138099
+ .then((newState) => {
137637
138100
  const draftIdsToFilter = priorDraftIds ? priorDraftIds : [];
137638
138101
  const newDrafts = newState.items;
137639
138102
  const addedDrafts = newDrafts.filter((item) => {
@@ -137650,13 +138113,35 @@ function invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, draftIdToRep
137650
138113
  else {
137651
138114
  draftManager
137652
138115
  .replaceAction(draftIdToReplace, addedDrafts[0].id)
137653
- .then(() => {
137654
- onResponse(responseValue);
138116
+ .then((replacedDraft) => {
138117
+ if (metadata !== undefined) {
138118
+ // Spread the metadata returned by replaceAction
138119
+ // (not the pre-replace snapshot) so that keys the
138120
+ // replacement clears — notably retryCount — stay
138121
+ // cleared instead of being reintroduced here.
138122
+ draftManager
138123
+ .setMetadata(draftIdToReplace, {
138124
+ ...replacedDraft.metadata,
138125
+ ...metadata,
138126
+ })
138127
+ .then(() => {
138128
+ onResponse(responseValue);
138129
+ })
138130
+ .catch((error) => {
138131
+ onResponse(convertErrorIntoNativeFetchError(error, 'Unable to set metadata on draft'));
138132
+ });
138133
+ }
138134
+ else {
138135
+ onResponse(responseValue);
138136
+ }
137655
138137
  })
137656
138138
  .catch((error) => {
137657
138139
  onResponse(convertErrorIntoNativeFetchError(error, 'Unknown error replacing action'));
137658
138140
  });
137659
138141
  }
138142
+ })
138143
+ .catch((error) => {
138144
+ onResponse(convertErrorIntoNativeFetchError(error, 'Unable to get draft queue'));
137660
138145
  });
137661
138146
  }
137662
138147
  else {
@@ -137665,6 +138150,9 @@ function invokeAdapterWithDraftToMergeDeleteRecord(adapter, config, draftIdToRep
137665
138150
  onResponse(response);
137666
138151
  }
137667
138152
  }, nativeAdapterRequestContext);
138153
+ })
138154
+ .catch((error) => {
138155
+ onResponse(convertErrorIntoNativeFetchError(error, 'Unable to get draft queue'));
137668
138156
  });
137669
138157
  }
137670
138158
  function removeDraftNodeActionId(targetDraftId, responseValue) {
@@ -137894,7 +138382,7 @@ const callbacks$1 = [];
137894
138382
  function register(r) {
137895
138383
  callbacks$1.forEach((callback) => callback(r));
137896
138384
  }
137897
- // version: 1.461.0-37b1c9d1cb
138385
+ // version: 1.463.0-ea9ecd17fc
137898
138386
 
137899
138387
  /**
137900
138388
  * Copyright (c) 2022, Salesforce, Inc.,
@@ -139182,5 +139670,5 @@ class OneStoreAdapterModule {
139182
139670
  const { luvio } = getRuntime();
139183
139671
  setDefaultLuvio({ luvio });
139184
139672
 
139185
- export { createPrimingSession, draftManager, draftQueue, evictCacheRecordsByIds, evictExpiredCacheEntries, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, importLuvioAdapterModule, importOneStoreAdapterModule, invokeAdapter, invokeAdapterWithDraftToMerge, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, registerReportObserver, setMetadataTTL, setUiApiRecordTTL, stopEviction, subscribeToAdapter };
139186
- // version: 1.461.0-37b1c9d1cb
139673
+ export { createPrimingSession, draftManager, draftQueue, evictCacheRecordsByIds, evictExpiredCacheEntries, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, importLuvioAdapterModule, importOneStoreAdapterModule, invokeAdapter, invokeAdapterWithDraftToMerge, invokeAdapterWithDraftToMergeAndMetadata, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, registerReportObserver, setMetadataTTL, setUiApiRecordTTL, stopEviction, subscribeToAdapter };
139674
+ // version: 1.463.0-ea9ecd17fc