@salesforce/lds-worker-api 1.208.1 → 1.209.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.
|
@@ -770,4 +770,4 @@ if (process.env.NODE_ENV !== 'production') {
|
|
|
770
770
|
}
|
|
771
771
|
|
|
772
772
|
export { createPrimingSession, draftManager, draftQueue, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, invokeAdapter, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, setMetadataTTL, setUiApiRecordTTL, subscribeToAdapter };
|
|
773
|
-
// version: 1.
|
|
773
|
+
// version: 1.209.0-6b633d53a
|
|
@@ -3862,7 +3862,7 @@ function withDefaultLuvio(callback) {
|
|
|
3862
3862
|
}
|
|
3863
3863
|
callbacks.push(callback);
|
|
3864
3864
|
}
|
|
3865
|
-
// version: 1.
|
|
3865
|
+
// version: 1.209.0-6b633d53a
|
|
3866
3866
|
|
|
3867
3867
|
// TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
|
|
3868
3868
|
function instrumentAdapter$1(createFunction, _metadata) {
|
|
@@ -15295,7 +15295,7 @@ function parseAndVisit(source) {
|
|
|
15295
15295
|
updateReferenceMapWithKnownKey(ast, luvioDocumentNode);
|
|
15296
15296
|
return luvioDocumentNode;
|
|
15297
15297
|
}
|
|
15298
|
-
// version: 1.
|
|
15298
|
+
// version: 1.209.0-6b633d53a
|
|
15299
15299
|
|
|
15300
15300
|
function unwrap(data) {
|
|
15301
15301
|
// The lwc-luvio bindings import a function from lwc called "unwrap".
|
|
@@ -16218,7 +16218,7 @@ function createGraphQLWireAdapterConstructor(luvio, adapter, metadata, astResolv
|
|
|
16218
16218
|
const { apiFamily, name } = metadata;
|
|
16219
16219
|
return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
|
|
16220
16220
|
}
|
|
16221
|
-
// version: 1.
|
|
16221
|
+
// version: 1.209.0-6b633d53a
|
|
16222
16222
|
|
|
16223
16223
|
/**
|
|
16224
16224
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -43556,7 +43556,7 @@ withDefaultLuvio((luvio) => {
|
|
|
43556
43556
|
});
|
|
43557
43557
|
throttle(60, 60000, createLDSAdapter(luvio, 'notifyListViewSummaryUpdateAvailable', notifyUpdateAvailableFactory));
|
|
43558
43558
|
});
|
|
43559
|
-
// version: 1.
|
|
43559
|
+
// version: 1.209.0-b5ca8738d
|
|
43560
43560
|
|
|
43561
43561
|
var caseSensitiveUserId = '005B0000000GR4OIAW';
|
|
43562
43562
|
|
|
@@ -58066,6 +58066,8 @@ function reportDraftAwareContentVersionSynthesizeCalls(mimeType) {
|
|
|
58066
58066
|
}
|
|
58067
58067
|
function reportPrimingError(errorType, recordCount) {
|
|
58068
58068
|
}
|
|
58069
|
+
function reportPrimingConflict(resolutionType, recordCount) {
|
|
58070
|
+
}
|
|
58069
58071
|
|
|
58070
58072
|
/**
|
|
58071
58073
|
* HOF (high-order-function) that instruments any async operation. If the operation
|
|
@@ -59041,6 +59043,245 @@ function generateTypedBatches(work, batchSize) {
|
|
|
59041
59043
|
return batches;
|
|
59042
59044
|
}
|
|
59043
59045
|
|
|
59046
|
+
function getMissingElementsFromSuperset(superset, subset) {
|
|
59047
|
+
return subset.filter((val) => !superset.includes(val));
|
|
59048
|
+
}
|
|
59049
|
+
function findReferenceFieldForSpanningField(fieldName, objectInfo) {
|
|
59050
|
+
const fieldNames = Object.keys(objectInfo.fields);
|
|
59051
|
+
for (const objectInfoFieldName of fieldNames) {
|
|
59052
|
+
const field = objectInfo.fields[objectInfoFieldName];
|
|
59053
|
+
if (field.reference === true && field.relationshipName === fieldName) {
|
|
59054
|
+
return objectInfoFieldName;
|
|
59055
|
+
}
|
|
59056
|
+
}
|
|
59057
|
+
}
|
|
59058
|
+
function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
|
|
59059
|
+
const allFields = Array.from(new Set([...Object.keys(existingRecord.fields), ...Object.keys(incomingRecord.fields)]));
|
|
59060
|
+
const fieldUnion = [];
|
|
59061
|
+
allFields.forEach((fieldName) => {
|
|
59062
|
+
const objectInfoField = objectInfo.fields[fieldName];
|
|
59063
|
+
if (objectInfoField === undefined) {
|
|
59064
|
+
// find the reference field for the spanning field
|
|
59065
|
+
const referenceField = findReferenceFieldForSpanningField(fieldName, objectInfo);
|
|
59066
|
+
if (referenceField !== undefined) {
|
|
59067
|
+
fieldUnion.push(`${fieldName}.Id`);
|
|
59068
|
+
}
|
|
59069
|
+
}
|
|
59070
|
+
else {
|
|
59071
|
+
fieldUnion.push(fieldName);
|
|
59072
|
+
}
|
|
59073
|
+
});
|
|
59074
|
+
return fieldUnion;
|
|
59075
|
+
}
|
|
59076
|
+
/**
|
|
59077
|
+
* Merges (if possible) an incoming record from a priming session with an existing record in the durable store.
|
|
59078
|
+
*
|
|
59079
|
+
* IMPORTANT NOTE: this is not a suitable function to use for general merging of two DurableRecordRepresentation since it
|
|
59080
|
+
* makes the assumption that the incoming record ONLY contains scalar field values and no spanning records. The same is not
|
|
59081
|
+
* necessarily true for the existing record as it may have been populated in the cache outside of a priming session.
|
|
59082
|
+
* This function should not be moved out of the priming module!
|
|
59083
|
+
*
|
|
59084
|
+
* @param existingRecord Existing record in the durable store
|
|
59085
|
+
* @param incomingRecord Incoming record from the priming session
|
|
59086
|
+
* @param objectInfo Object info for the incoming record type
|
|
59087
|
+
* @returns Merge result describing the success or failure of the merge operation
|
|
59088
|
+
*/
|
|
59089
|
+
function mergeRecord(existingRecord, incomingRecord, objectInfo) {
|
|
59090
|
+
// cache already contains everything incoming has
|
|
59091
|
+
if (existingRecord.weakEtag >= incomingRecord.weakEtag &&
|
|
59092
|
+
getMissingElementsFromSuperset(Object.keys(existingRecord.fields), Object.keys(incomingRecord.fields)).length === 0) {
|
|
59093
|
+
return {
|
|
59094
|
+
ok: true,
|
|
59095
|
+
code: 'success',
|
|
59096
|
+
needsWrite: false,
|
|
59097
|
+
record: existingRecord,
|
|
59098
|
+
};
|
|
59099
|
+
}
|
|
59100
|
+
// don't touch records that contain drafts
|
|
59101
|
+
if (existingRecord.drafts !== undefined) {
|
|
59102
|
+
return {
|
|
59103
|
+
ok: false,
|
|
59104
|
+
code: 'conflict-drafts',
|
|
59105
|
+
hasDraft: true,
|
|
59106
|
+
fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
|
|
59107
|
+
};
|
|
59108
|
+
}
|
|
59109
|
+
// Check if incoming record's Etag is equal to the existing one
|
|
59110
|
+
if (existingRecord.weakEtag === incomingRecord.weakEtag) {
|
|
59111
|
+
// If so, merge the fields and return the updated record
|
|
59112
|
+
return {
|
|
59113
|
+
ok: true,
|
|
59114
|
+
needsWrite: true,
|
|
59115
|
+
code: 'success',
|
|
59116
|
+
record: {
|
|
59117
|
+
...existingRecord,
|
|
59118
|
+
fields: {
|
|
59119
|
+
...existingRecord.fields,
|
|
59120
|
+
...incomingRecord.fields,
|
|
59121
|
+
},
|
|
59122
|
+
links: {
|
|
59123
|
+
...existingRecord.links,
|
|
59124
|
+
...incomingRecord.links,
|
|
59125
|
+
},
|
|
59126
|
+
},
|
|
59127
|
+
};
|
|
59128
|
+
}
|
|
59129
|
+
else if (incomingRecord.weakEtag > existingRecord.weakEtag &&
|
|
59130
|
+
getMissingElementsFromSuperset(Object.keys(incomingRecord.fields), Object.keys(existingRecord.fields)).length === 0) {
|
|
59131
|
+
// If incoming record's Etag is higher and contains all the fields, overwrite the record
|
|
59132
|
+
// NOTE: if existing record contains spanning records, this condition will never hit since incoming won't have those fields
|
|
59133
|
+
return { ok: true, code: 'success', needsWrite: true, record: incomingRecord };
|
|
59134
|
+
}
|
|
59135
|
+
else {
|
|
59136
|
+
const missingFields = getMissingElementsFromSuperset(Object.keys(incomingRecord.fields), Object.keys(existingRecord.fields));
|
|
59137
|
+
// if the only missing fields are spanning fields and their corresponding lookup fields match, we can merge
|
|
59138
|
+
// since none of the changed fields are part of the incoming record
|
|
59139
|
+
if (missingFields.every((field) => {
|
|
59140
|
+
const referenceFieldName = findReferenceFieldForSpanningField(field, objectInfo);
|
|
59141
|
+
if (referenceFieldName !== undefined) {
|
|
59142
|
+
return (incomingRecord.fields[referenceFieldName].value ===
|
|
59143
|
+
existingRecord.fields[referenceFieldName].value);
|
|
59144
|
+
}
|
|
59145
|
+
else {
|
|
59146
|
+
return false;
|
|
59147
|
+
}
|
|
59148
|
+
})) {
|
|
59149
|
+
return {
|
|
59150
|
+
ok: true,
|
|
59151
|
+
needsWrite: true,
|
|
59152
|
+
code: 'success',
|
|
59153
|
+
record: {
|
|
59154
|
+
// we span the existing record to maintain spanning references
|
|
59155
|
+
...incomingRecord,
|
|
59156
|
+
fields: {
|
|
59157
|
+
...existingRecord.fields,
|
|
59158
|
+
...incomingRecord.fields,
|
|
59159
|
+
},
|
|
59160
|
+
links: {
|
|
59161
|
+
...existingRecord.links,
|
|
59162
|
+
...incomingRecord.links,
|
|
59163
|
+
},
|
|
59164
|
+
},
|
|
59165
|
+
};
|
|
59166
|
+
}
|
|
59167
|
+
// If Etags do not match and the incoming record does not contain all fields, re-request the record
|
|
59168
|
+
return {
|
|
59169
|
+
ok: false,
|
|
59170
|
+
code: 'conflict-missing-fields',
|
|
59171
|
+
fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
|
|
59172
|
+
hasDraft: false,
|
|
59173
|
+
};
|
|
59174
|
+
}
|
|
59175
|
+
}
|
|
59176
|
+
|
|
59177
|
+
const CONFLICT_POOL_SIZE = 5;
|
|
59178
|
+
/**
|
|
59179
|
+
* A pool of workers that resolve conflicts between incoming records and records in the store.
|
|
59180
|
+
*/
|
|
59181
|
+
class ConflictPool {
|
|
59182
|
+
constructor(store, objectInfoLoader) {
|
|
59183
|
+
this.store = store;
|
|
59184
|
+
this.objectInfoLoader = objectInfoLoader;
|
|
59185
|
+
this.pool = new AsyncWorkerPool(CONFLICT_POOL_SIZE);
|
|
59186
|
+
}
|
|
59187
|
+
enqueueConflictedRecords(records, abortController) {
|
|
59188
|
+
return this.pool.push({
|
|
59189
|
+
workFn: () => this.resolveConflicts(records, abortController),
|
|
59190
|
+
});
|
|
59191
|
+
}
|
|
59192
|
+
async resolveConflicts(incomingRecords, abortController) {
|
|
59193
|
+
const result = {
|
|
59194
|
+
additionalWork: { type: 'record-fields', records: {} },
|
|
59195
|
+
recordsToWrite: [],
|
|
59196
|
+
resolvedRecords: [],
|
|
59197
|
+
recordsNeedingRefetch: new Map(),
|
|
59198
|
+
errors: [],
|
|
59199
|
+
};
|
|
59200
|
+
const ids = [];
|
|
59201
|
+
const trackedFieldsByType = new Map();
|
|
59202
|
+
const apiNames = new Set();
|
|
59203
|
+
incomingRecords.forEach((record) => {
|
|
59204
|
+
ids.push(record.id);
|
|
59205
|
+
apiNames.add(record.apiName);
|
|
59206
|
+
});
|
|
59207
|
+
const existingRecords = await this.store.readRecords(ids);
|
|
59208
|
+
if (abortController.aborted) {
|
|
59209
|
+
return result;
|
|
59210
|
+
}
|
|
59211
|
+
const objectInfos = await this.objectInfoLoader.getObjectInfos(Array.from(apiNames));
|
|
59212
|
+
if (abortController.aborted) {
|
|
59213
|
+
return result;
|
|
59214
|
+
}
|
|
59215
|
+
const existingRecordsById = new Map(existingRecords.map((record) => [record.record.id, record]));
|
|
59216
|
+
for (const incomingRecord of incomingRecords) {
|
|
59217
|
+
const existingDurableRecordRepresentation = existingRecordsById.get(incomingRecord.id);
|
|
59218
|
+
const objectInfo = objectInfos[incomingRecord.apiName];
|
|
59219
|
+
if (existingDurableRecordRepresentation === undefined) {
|
|
59220
|
+
// this shouldn't happen but if it does, we should write the incoming record since there's nothing to merge
|
|
59221
|
+
result.recordsToWrite.push(incomingRecord);
|
|
59222
|
+
continue;
|
|
59223
|
+
}
|
|
59224
|
+
if (objectInfo === undefined) {
|
|
59225
|
+
// object infos are a prerequisite for priming so if we don't have one, we can't do anything
|
|
59226
|
+
result.errors.push({ id: incomingRecord.id, reason: 'object-info-missing' });
|
|
59227
|
+
continue;
|
|
59228
|
+
}
|
|
59229
|
+
const existingRecord = existingDurableRecordRepresentation.record;
|
|
59230
|
+
const mergedRecordResult = mergeRecord(existingRecord, incomingRecord, objectInfo);
|
|
59231
|
+
if (mergedRecordResult.ok) {
|
|
59232
|
+
if (mergedRecordResult.needsWrite) {
|
|
59233
|
+
result.recordsToWrite.push(mergedRecordResult.record);
|
|
59234
|
+
}
|
|
59235
|
+
else {
|
|
59236
|
+
result.resolvedRecords.push(mergedRecordResult.record.id);
|
|
59237
|
+
}
|
|
59238
|
+
continue;
|
|
59239
|
+
}
|
|
59240
|
+
else {
|
|
59241
|
+
const { code } = mergedRecordResult;
|
|
59242
|
+
const isConflict = code === 'conflict-drafts' ||
|
|
59243
|
+
code === 'conflict-spanning-record' ||
|
|
59244
|
+
code === 'conflict-missing-fields';
|
|
59245
|
+
if (isConflict) {
|
|
59246
|
+
let trackedFields = trackedFieldsByType.get(incomingRecord.apiName);
|
|
59247
|
+
if (trackedFields === undefined) {
|
|
59248
|
+
trackedFields = new Set();
|
|
59249
|
+
trackedFieldsByType.set(incomingRecord.apiName, trackedFields);
|
|
59250
|
+
}
|
|
59251
|
+
mergedRecordResult.fieldUnion.forEach((field) => trackedFields.add(field));
|
|
59252
|
+
if (code === 'conflict-missing-fields') {
|
|
59253
|
+
const additionalWorkForType = result.additionalWork.records[incomingRecord.apiName];
|
|
59254
|
+
if (additionalWorkForType === undefined) {
|
|
59255
|
+
result.additionalWork.records[incomingRecord.apiName] = {
|
|
59256
|
+
ids: [incomingRecord.id],
|
|
59257
|
+
fields: Array.from(trackedFields),
|
|
59258
|
+
};
|
|
59259
|
+
}
|
|
59260
|
+
else {
|
|
59261
|
+
additionalWorkForType.ids.push(incomingRecord.id);
|
|
59262
|
+
additionalWorkForType.fields = Array.from(trackedFields);
|
|
59263
|
+
}
|
|
59264
|
+
}
|
|
59265
|
+
else if (code === 'conflict-drafts' || code === 'conflict-spanning-record') {
|
|
59266
|
+
const recordByType = result.recordsNeedingRefetch.get(incomingRecord.apiName);
|
|
59267
|
+
if (recordByType === undefined) {
|
|
59268
|
+
result.recordsNeedingRefetch.set(incomingRecord.apiName, {
|
|
59269
|
+
ids: [incomingRecord.id],
|
|
59270
|
+
fields: Array.from(trackedFields),
|
|
59271
|
+
});
|
|
59272
|
+
}
|
|
59273
|
+
else {
|
|
59274
|
+
recordByType.ids.push(incomingRecord.id);
|
|
59275
|
+
recordByType.fields = Array.from(trackedFields);
|
|
59276
|
+
}
|
|
59277
|
+
}
|
|
59278
|
+
}
|
|
59279
|
+
}
|
|
59280
|
+
}
|
|
59281
|
+
return result;
|
|
59282
|
+
}
|
|
59283
|
+
}
|
|
59284
|
+
|
|
59044
59285
|
const DEFAULT_BATCH_SIZE = 500;
|
|
59045
59286
|
const DEFAULT_CONCURRENCY = 6;
|
|
59046
59287
|
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
@@ -59054,8 +59295,10 @@ class PrimingSession extends EventEmitter {
|
|
|
59054
59295
|
this.recordLoader = config.recordLoader;
|
|
59055
59296
|
this.recordIngestor = config.recordIngestor;
|
|
59056
59297
|
this.objectInfoLoader = config.objectInfoLoader;
|
|
59298
|
+
this.ldsRecordRefresher = config.ldsRecordRefresher;
|
|
59057
59299
|
this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
|
|
59058
59300
|
this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
|
|
59301
|
+
this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
|
|
59059
59302
|
}
|
|
59060
59303
|
// function that enqueues priming work
|
|
59061
59304
|
async enqueue(work) {
|
|
@@ -59180,7 +59423,9 @@ class PrimingSession extends EventEmitter {
|
|
|
59180
59423
|
const { records } = result;
|
|
59181
59424
|
const beforeWrite = Date.now();
|
|
59182
59425
|
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
59183
|
-
this.recordIngestor
|
|
59426
|
+
this.recordIngestor
|
|
59427
|
+
.insertRecords(records, false)
|
|
59428
|
+
.then(({ written, conflicted, errors }) => {
|
|
59184
59429
|
this.emit('batch-written', {
|
|
59185
59430
|
written,
|
|
59186
59431
|
conflicted,
|
|
@@ -59203,16 +59448,69 @@ class PrimingSession extends EventEmitter {
|
|
|
59203
59448
|
if (written.length > 0) {
|
|
59204
59449
|
this.emit('primed', Array.from(written));
|
|
59205
59450
|
}
|
|
59206
|
-
//
|
|
59451
|
+
// if any records could not be written to the store because there were conflicts, handle the conflicts
|
|
59452
|
+
if (conflicted.length > 0) {
|
|
59453
|
+
this.handleWriteConflicts(records, conflicted, abortController);
|
|
59454
|
+
}
|
|
59455
|
+
});
|
|
59456
|
+
}
|
|
59457
|
+
async handleWriteConflicts(records, conflicted, abortController) {
|
|
59458
|
+
const result = await this.conflictPool.enqueueConflictedRecords(records.filter((x) => conflicted.includes(x.id)), abortController);
|
|
59459
|
+
if (abortController.aborted) {
|
|
59460
|
+
return;
|
|
59461
|
+
}
|
|
59462
|
+
if (Object.keys(result.additionalWork.records).length > 0) {
|
|
59463
|
+
this.emit('conflict', {
|
|
59464
|
+
ids: Object.values(result.additionalWork.records).flatMap((record) => record.ids),
|
|
59465
|
+
resolution: 'priming-refresh',
|
|
59466
|
+
});
|
|
59467
|
+
this.enqueue(result.additionalWork);
|
|
59468
|
+
}
|
|
59469
|
+
if (result.resolvedRecords.length > 0) {
|
|
59470
|
+
this.emit('conflict', {
|
|
59471
|
+
ids: result.resolvedRecords,
|
|
59472
|
+
resolution: 'priming-merge',
|
|
59473
|
+
});
|
|
59474
|
+
this.emit('primed', result.resolvedRecords);
|
|
59475
|
+
}
|
|
59476
|
+
if (result.recordsToWrite.length > 0) {
|
|
59477
|
+
const { written, errors, conflicted } = await this.recordIngestor.insertRecords(result.recordsToWrite, true);
|
|
59478
|
+
if (written.length > 0) {
|
|
59479
|
+
const ids = Array.from(written);
|
|
59480
|
+
this.emit('conflict', { ids, resolution: 'priming-merge' });
|
|
59481
|
+
this.emit('primed', ids);
|
|
59482
|
+
}
|
|
59483
|
+
if (errors.length > 0) {
|
|
59484
|
+
errors.forEach(({ ids, message }) => {
|
|
59485
|
+
this.emit('error', {
|
|
59486
|
+
ids,
|
|
59487
|
+
code: 'unknown',
|
|
59488
|
+
message: message,
|
|
59489
|
+
});
|
|
59490
|
+
});
|
|
59491
|
+
}
|
|
59207
59492
|
if (conflicted.length > 0) {
|
|
59208
|
-
// for now emit conlicts as errors
|
|
59209
59493
|
this.emit('error', {
|
|
59210
|
-
ids:
|
|
59494
|
+
ids: conflicted,
|
|
59211
59495
|
code: 'unknown',
|
|
59212
|
-
message: '
|
|
59496
|
+
message: 'unexpected write conflict',
|
|
59213
59497
|
});
|
|
59214
59498
|
}
|
|
59215
|
-
}
|
|
59499
|
+
}
|
|
59500
|
+
if (result.recordsNeedingRefetch.size > 0) {
|
|
59501
|
+
const { loaded, errored } = await this.ldsRecordRefresher.loadRecords(result.recordsNeedingRefetch);
|
|
59502
|
+
if (loaded.length > 0) {
|
|
59503
|
+
this.emit('conflict', { resolution: 'lds-refresh', ids: loaded });
|
|
59504
|
+
this.emit('primed', loaded);
|
|
59505
|
+
}
|
|
59506
|
+
if (errored.length > 0) {
|
|
59507
|
+
this.emit('error', {
|
|
59508
|
+
ids: errored,
|
|
59509
|
+
code: 'unknown',
|
|
59510
|
+
message: `could not resolve conflicts`,
|
|
59511
|
+
});
|
|
59512
|
+
}
|
|
59513
|
+
}
|
|
59216
59514
|
}
|
|
59217
59515
|
async fetchMetadata(batches) {
|
|
59218
59516
|
const apiNames = Array.from(batches.reduce((acc, x) => {
|
|
@@ -59451,6 +59749,9 @@ function instrumentPrimingSession(session) {
|
|
|
59451
59749
|
});
|
|
59452
59750
|
session.on('primed', ({ length }) => {
|
|
59453
59751
|
});
|
|
59752
|
+
session.on('conflict', ({ ids, resolution }) => {
|
|
59753
|
+
reportPrimingConflict(resolution, ids.length);
|
|
59754
|
+
});
|
|
59454
59755
|
return session;
|
|
59455
59756
|
}
|
|
59456
59757
|
|
|
@@ -59632,19 +59933,72 @@ function batchArray(arr, batchSize = BATCH_SIZE) {
|
|
|
59632
59933
|
return batches;
|
|
59633
59934
|
}
|
|
59634
59935
|
|
|
59936
|
+
/**
|
|
59937
|
+
* A polyfill for Promise.allSettled
|
|
59938
|
+
* @param promises An array of promises
|
|
59939
|
+
* @returns the result of all the promises when they either fulfill or reject
|
|
59940
|
+
*/
|
|
59941
|
+
function allSettled(promises) {
|
|
59942
|
+
let wrappedPromises = promises.map((p) => Promise.resolve(p).then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason })));
|
|
59943
|
+
return Promise.all(wrappedPromises);
|
|
59944
|
+
}
|
|
59945
|
+
|
|
59946
|
+
class LdsPrimingRecordRefresher {
|
|
59947
|
+
constructor(getRecordsAdapter) {
|
|
59948
|
+
this.getRecordsAdapter = getRecordsAdapter;
|
|
59949
|
+
}
|
|
59950
|
+
async loadRecords(records) {
|
|
59951
|
+
const requestedRecords = new Set();
|
|
59952
|
+
const promises = Array.from(records).flatMap(([_apiName, value]) => {
|
|
59953
|
+
value.ids.forEach((id) => requestedRecords.add(id));
|
|
59954
|
+
return Promise.resolve(this.getRecordsAdapter({
|
|
59955
|
+
records: [
|
|
59956
|
+
{
|
|
59957
|
+
recordIds: value.ids,
|
|
59958
|
+
optionalFields: value.fields.map((f) => `${_apiName}.${f}`),
|
|
59959
|
+
},
|
|
59960
|
+
],
|
|
59961
|
+
}));
|
|
59962
|
+
});
|
|
59963
|
+
const promiseResults = await allSettled(promises);
|
|
59964
|
+
const loaded = [];
|
|
59965
|
+
promiseResults.forEach((promiseResult) => {
|
|
59966
|
+
if (promiseResult.status === 'fulfilled') {
|
|
59967
|
+
const batchResultRepresenatation = promiseResult.value;
|
|
59968
|
+
if (batchResultRepresenatation &&
|
|
59969
|
+
batchResultRepresenatation.state === 'Fulfilled') {
|
|
59970
|
+
batchResultRepresenatation.data.results.forEach((result) => {
|
|
59971
|
+
if (result.statusCode === 200) {
|
|
59972
|
+
const id = result.result.id;
|
|
59973
|
+
loaded.push(id);
|
|
59974
|
+
requestedRecords.delete(id);
|
|
59975
|
+
}
|
|
59976
|
+
});
|
|
59977
|
+
}
|
|
59978
|
+
}
|
|
59979
|
+
});
|
|
59980
|
+
// errored contains all the requestedRecords that weren't loaded
|
|
59981
|
+
const errored = Array.from(requestedRecords);
|
|
59982
|
+
return { loaded, errored };
|
|
59983
|
+
}
|
|
59984
|
+
}
|
|
59985
|
+
|
|
59635
59986
|
function primingSessionFactory(config) {
|
|
59636
59987
|
const { store, objectInfoService, getLuvio } = config;
|
|
59637
59988
|
const networkAdapter = new NimbusPrimingNetworkAdapter();
|
|
59638
59989
|
const recordLoader = new RecordLoaderGraphQL(networkAdapter);
|
|
59639
|
-
const
|
|
59990
|
+
const primingStore = new SqlitePrimingStore(getLuvio, store);
|
|
59991
|
+
const recordIngestor = new RecordIngestor(primingStore, getLuvio);
|
|
59640
59992
|
const session = new PrimingSession({
|
|
59641
59993
|
recordLoader,
|
|
59642
59994
|
recordIngestor,
|
|
59995
|
+
store: primingStore,
|
|
59643
59996
|
objectInfoLoader: {
|
|
59644
59997
|
getObjectInfos: objectInfoService.getObjectInfos.bind(objectInfoService),
|
|
59645
59998
|
},
|
|
59646
59999
|
concurrency: config.concurrency,
|
|
59647
60000
|
batchSize: config.batchSize,
|
|
60001
|
+
ldsRecordRefresher: new LdsPrimingRecordRefresher(config.getRecords),
|
|
59648
60002
|
});
|
|
59649
60003
|
return instrumentPrimingSession(session);
|
|
59650
60004
|
}
|
|
@@ -59658,6 +60012,7 @@ let lazyEnvironment;
|
|
|
59658
60012
|
let lazyBaseDurableStore;
|
|
59659
60013
|
let lazyNetworkAdapter;
|
|
59660
60014
|
let lazyObjectInfoService;
|
|
60015
|
+
let lazyGetRecords;
|
|
59661
60016
|
/**
|
|
59662
60017
|
* This returns the LDS on Mobile Runtime singleton object.
|
|
59663
60018
|
*/
|
|
@@ -59728,6 +60083,7 @@ function getRuntime() {
|
|
|
59728
60083
|
lazyLuvio = new Luvio(lazyEnvironment, {
|
|
59729
60084
|
instrument: instrumentLuvio,
|
|
59730
60085
|
});
|
|
60086
|
+
lazyGetRecords = getRecordsAdapterFactory(lazyLuvio);
|
|
59731
60087
|
// Currently instruments store runtime perf
|
|
59732
60088
|
setupMobileInstrumentation();
|
|
59733
60089
|
// If the inspection nimbus plugin is configured, inspection is enabled otherwise this is a no-op
|
|
@@ -59781,6 +60137,7 @@ function getRuntime() {
|
|
|
59781
60137
|
getLuvio: () => lazyLuvio,
|
|
59782
60138
|
concurrency: config.concurrency,
|
|
59783
60139
|
batchSize: config.batchSize,
|
|
60140
|
+
getRecords: lazyGetRecords,
|
|
59784
60141
|
});
|
|
59785
60142
|
},
|
|
59786
60143
|
};
|
|
@@ -59797,7 +60154,7 @@ register({
|
|
|
59797
60154
|
id: '@salesforce/lds-network-adapter',
|
|
59798
60155
|
instrument: instrument$1,
|
|
59799
60156
|
});
|
|
59800
|
-
// version: 1.
|
|
60157
|
+
// version: 1.209.0-6b633d53a
|
|
59801
60158
|
|
|
59802
60159
|
const { create: create$2, keys: keys$2 } = Object;
|
|
59803
60160
|
const { stringify: stringify$1, parse: parse$1 } = JSON;
|
|
@@ -77140,7 +77497,7 @@ register({
|
|
|
77140
77497
|
configuration: { ...configurationForGraphQLAdapters },
|
|
77141
77498
|
instrument,
|
|
77142
77499
|
});
|
|
77143
|
-
// version: 1.
|
|
77500
|
+
// version: 1.209.0-b5ca8738d
|
|
77144
77501
|
|
|
77145
77502
|
// On core the unstable adapters are re-exported with different names,
|
|
77146
77503
|
|
|
@@ -79387,7 +79744,7 @@ withDefaultLuvio((luvio) => {
|
|
|
79387
79744
|
unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
|
|
79388
79745
|
graphQLImperative = ldsAdapter;
|
|
79389
79746
|
});
|
|
79390
|
-
// version: 1.
|
|
79747
|
+
// version: 1.209.0-b5ca8738d
|
|
79391
79748
|
|
|
79392
79749
|
var gqlApi = /*#__PURE__*/Object.freeze({
|
|
79393
79750
|
__proto__: null,
|
|
@@ -80076,4 +80433,4 @@ const { luvio } = getRuntime();
|
|
|
80076
80433
|
setDefaultLuvio({ luvio });
|
|
80077
80434
|
|
|
80078
80435
|
export { createPrimingSession, draftManager, draftQueue, executeAdapter, executeMutatingAdapter, getImperativeAdapterNames, invokeAdapter, invokeAdapterWithDraftToReplace, invokeAdapterWithMetadata, nimbusDraftQueue, registerReportObserver, setMetadataTTL, setUiApiRecordTTL, subscribeToAdapter };
|
|
80079
|
-
// version: 1.
|
|
80436
|
+
// version: 1.209.0-6b633d53a
|
|
@@ -3868,7 +3868,7 @@
|
|
|
3868
3868
|
}
|
|
3869
3869
|
callbacks.push(callback);
|
|
3870
3870
|
}
|
|
3871
|
-
// version: 1.
|
|
3871
|
+
// version: 1.209.0-6b633d53a
|
|
3872
3872
|
|
|
3873
3873
|
// TODO [TD-0081508]: once that TD is fulfilled we can probably change this file
|
|
3874
3874
|
function instrumentAdapter$1(createFunction, _metadata) {
|
|
@@ -15301,7 +15301,7 @@
|
|
|
15301
15301
|
updateReferenceMapWithKnownKey(ast, luvioDocumentNode);
|
|
15302
15302
|
return luvioDocumentNode;
|
|
15303
15303
|
}
|
|
15304
|
-
// version: 1.
|
|
15304
|
+
// version: 1.209.0-6b633d53a
|
|
15305
15305
|
|
|
15306
15306
|
function unwrap(data) {
|
|
15307
15307
|
// The lwc-luvio bindings import a function from lwc called "unwrap".
|
|
@@ -16224,7 +16224,7 @@
|
|
|
16224
16224
|
const { apiFamily, name } = metadata;
|
|
16225
16225
|
return createGraphQLWireAdapterConstructor$1(adapter, `${apiFamily}.${name}`, luvio, astResolver);
|
|
16226
16226
|
}
|
|
16227
|
-
// version: 1.
|
|
16227
|
+
// version: 1.209.0-6b633d53a
|
|
16228
16228
|
|
|
16229
16229
|
/**
|
|
16230
16230
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
@@ -43562,7 +43562,7 @@
|
|
|
43562
43562
|
});
|
|
43563
43563
|
throttle(60, 60000, createLDSAdapter(luvio, 'notifyListViewSummaryUpdateAvailable', notifyUpdateAvailableFactory));
|
|
43564
43564
|
});
|
|
43565
|
-
// version: 1.
|
|
43565
|
+
// version: 1.209.0-b5ca8738d
|
|
43566
43566
|
|
|
43567
43567
|
var caseSensitiveUserId = '005B0000000GR4OIAW';
|
|
43568
43568
|
|
|
@@ -58072,6 +58072,8 @@
|
|
|
58072
58072
|
}
|
|
58073
58073
|
function reportPrimingError(errorType, recordCount) {
|
|
58074
58074
|
}
|
|
58075
|
+
function reportPrimingConflict(resolutionType, recordCount) {
|
|
58076
|
+
}
|
|
58075
58077
|
|
|
58076
58078
|
/**
|
|
58077
58079
|
* HOF (high-order-function) that instruments any async operation. If the operation
|
|
@@ -59047,6 +59049,245 @@
|
|
|
59047
59049
|
return batches;
|
|
59048
59050
|
}
|
|
59049
59051
|
|
|
59052
|
+
function getMissingElementsFromSuperset(superset, subset) {
|
|
59053
|
+
return subset.filter((val) => !superset.includes(val));
|
|
59054
|
+
}
|
|
59055
|
+
function findReferenceFieldForSpanningField(fieldName, objectInfo) {
|
|
59056
|
+
const fieldNames = Object.keys(objectInfo.fields);
|
|
59057
|
+
for (const objectInfoFieldName of fieldNames) {
|
|
59058
|
+
const field = objectInfo.fields[objectInfoFieldName];
|
|
59059
|
+
if (field.reference === true && field.relationshipName === fieldName) {
|
|
59060
|
+
return objectInfoFieldName;
|
|
59061
|
+
}
|
|
59062
|
+
}
|
|
59063
|
+
}
|
|
59064
|
+
function buildFieldUnionArray(existingRecord, incomingRecord, objectInfo) {
|
|
59065
|
+
const allFields = Array.from(new Set([...Object.keys(existingRecord.fields), ...Object.keys(incomingRecord.fields)]));
|
|
59066
|
+
const fieldUnion = [];
|
|
59067
|
+
allFields.forEach((fieldName) => {
|
|
59068
|
+
const objectInfoField = objectInfo.fields[fieldName];
|
|
59069
|
+
if (objectInfoField === undefined) {
|
|
59070
|
+
// find the reference field for the spanning field
|
|
59071
|
+
const referenceField = findReferenceFieldForSpanningField(fieldName, objectInfo);
|
|
59072
|
+
if (referenceField !== undefined) {
|
|
59073
|
+
fieldUnion.push(`${fieldName}.Id`);
|
|
59074
|
+
}
|
|
59075
|
+
}
|
|
59076
|
+
else {
|
|
59077
|
+
fieldUnion.push(fieldName);
|
|
59078
|
+
}
|
|
59079
|
+
});
|
|
59080
|
+
return fieldUnion;
|
|
59081
|
+
}
|
|
59082
|
+
/**
|
|
59083
|
+
* Merges (if possible) an incoming record from a priming session with an existing record in the durable store.
|
|
59084
|
+
*
|
|
59085
|
+
* IMPORTANT NOTE: this is not a suitable function to use for general merging of two DurableRecordRepresentation since it
|
|
59086
|
+
* makes the assumption that the incoming record ONLY contains scalar field values and no spanning records. The same is not
|
|
59087
|
+
* necessarily true for the existing record as it may have been populated in the cache outside of a priming session.
|
|
59088
|
+
* This function should not be moved out of the priming module!
|
|
59089
|
+
*
|
|
59090
|
+
* @param existingRecord Existing record in the durable store
|
|
59091
|
+
* @param incomingRecord Incoming record from the priming session
|
|
59092
|
+
* @param objectInfo Object info for the incoming record type
|
|
59093
|
+
* @returns Merge result describing the success or failure of the merge operation
|
|
59094
|
+
*/
|
|
59095
|
+
function mergeRecord(existingRecord, incomingRecord, objectInfo) {
|
|
59096
|
+
// cache already contains everything incoming has
|
|
59097
|
+
if (existingRecord.weakEtag >= incomingRecord.weakEtag &&
|
|
59098
|
+
getMissingElementsFromSuperset(Object.keys(existingRecord.fields), Object.keys(incomingRecord.fields)).length === 0) {
|
|
59099
|
+
return {
|
|
59100
|
+
ok: true,
|
|
59101
|
+
code: 'success',
|
|
59102
|
+
needsWrite: false,
|
|
59103
|
+
record: existingRecord,
|
|
59104
|
+
};
|
|
59105
|
+
}
|
|
59106
|
+
// don't touch records that contain drafts
|
|
59107
|
+
if (existingRecord.drafts !== undefined) {
|
|
59108
|
+
return {
|
|
59109
|
+
ok: false,
|
|
59110
|
+
code: 'conflict-drafts',
|
|
59111
|
+
hasDraft: true,
|
|
59112
|
+
fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
|
|
59113
|
+
};
|
|
59114
|
+
}
|
|
59115
|
+
// Check if incoming record's Etag is equal to the existing one
|
|
59116
|
+
if (existingRecord.weakEtag === incomingRecord.weakEtag) {
|
|
59117
|
+
// If so, merge the fields and return the updated record
|
|
59118
|
+
return {
|
|
59119
|
+
ok: true,
|
|
59120
|
+
needsWrite: true,
|
|
59121
|
+
code: 'success',
|
|
59122
|
+
record: {
|
|
59123
|
+
...existingRecord,
|
|
59124
|
+
fields: {
|
|
59125
|
+
...existingRecord.fields,
|
|
59126
|
+
...incomingRecord.fields,
|
|
59127
|
+
},
|
|
59128
|
+
links: {
|
|
59129
|
+
...existingRecord.links,
|
|
59130
|
+
...incomingRecord.links,
|
|
59131
|
+
},
|
|
59132
|
+
},
|
|
59133
|
+
};
|
|
59134
|
+
}
|
|
59135
|
+
else if (incomingRecord.weakEtag > existingRecord.weakEtag &&
|
|
59136
|
+
getMissingElementsFromSuperset(Object.keys(incomingRecord.fields), Object.keys(existingRecord.fields)).length === 0) {
|
|
59137
|
+
// If incoming record's Etag is higher and contains all the fields, overwrite the record
|
|
59138
|
+
// NOTE: if existing record contains spanning records, this condition will never hit since incoming won't have those fields
|
|
59139
|
+
return { ok: true, code: 'success', needsWrite: true, record: incomingRecord };
|
|
59140
|
+
}
|
|
59141
|
+
else {
|
|
59142
|
+
const missingFields = getMissingElementsFromSuperset(Object.keys(incomingRecord.fields), Object.keys(existingRecord.fields));
|
|
59143
|
+
// if the only missing fields are spanning fields and their corresponding lookup fields match, we can merge
|
|
59144
|
+
// since none of the changed fields are part of the incoming record
|
|
59145
|
+
if (missingFields.every((field) => {
|
|
59146
|
+
const referenceFieldName = findReferenceFieldForSpanningField(field, objectInfo);
|
|
59147
|
+
if (referenceFieldName !== undefined) {
|
|
59148
|
+
return (incomingRecord.fields[referenceFieldName].value ===
|
|
59149
|
+
existingRecord.fields[referenceFieldName].value);
|
|
59150
|
+
}
|
|
59151
|
+
else {
|
|
59152
|
+
return false;
|
|
59153
|
+
}
|
|
59154
|
+
})) {
|
|
59155
|
+
return {
|
|
59156
|
+
ok: true,
|
|
59157
|
+
needsWrite: true,
|
|
59158
|
+
code: 'success',
|
|
59159
|
+
record: {
|
|
59160
|
+
// we span the existing record to maintain spanning references
|
|
59161
|
+
...incomingRecord,
|
|
59162
|
+
fields: {
|
|
59163
|
+
...existingRecord.fields,
|
|
59164
|
+
...incomingRecord.fields,
|
|
59165
|
+
},
|
|
59166
|
+
links: {
|
|
59167
|
+
...existingRecord.links,
|
|
59168
|
+
...incomingRecord.links,
|
|
59169
|
+
},
|
|
59170
|
+
},
|
|
59171
|
+
};
|
|
59172
|
+
}
|
|
59173
|
+
// If Etags do not match and the incoming record does not contain all fields, re-request the record
|
|
59174
|
+
return {
|
|
59175
|
+
ok: false,
|
|
59176
|
+
code: 'conflict-missing-fields',
|
|
59177
|
+
fieldUnion: buildFieldUnionArray(existingRecord, incomingRecord, objectInfo),
|
|
59178
|
+
hasDraft: false,
|
|
59179
|
+
};
|
|
59180
|
+
}
|
|
59181
|
+
}
|
|
59182
|
+
|
|
59183
|
+
const CONFLICT_POOL_SIZE = 5;
|
|
59184
|
+
/**
|
|
59185
|
+
* A pool of workers that resolve conflicts between incoming records and records in the store.
|
|
59186
|
+
*/
|
|
59187
|
+
class ConflictPool {
|
|
59188
|
+
constructor(store, objectInfoLoader) {
|
|
59189
|
+
this.store = store;
|
|
59190
|
+
this.objectInfoLoader = objectInfoLoader;
|
|
59191
|
+
this.pool = new AsyncWorkerPool(CONFLICT_POOL_SIZE);
|
|
59192
|
+
}
|
|
59193
|
+
enqueueConflictedRecords(records, abortController) {
|
|
59194
|
+
return this.pool.push({
|
|
59195
|
+
workFn: () => this.resolveConflicts(records, abortController),
|
|
59196
|
+
});
|
|
59197
|
+
}
|
|
59198
|
+
async resolveConflicts(incomingRecords, abortController) {
|
|
59199
|
+
const result = {
|
|
59200
|
+
additionalWork: { type: 'record-fields', records: {} },
|
|
59201
|
+
recordsToWrite: [],
|
|
59202
|
+
resolvedRecords: [],
|
|
59203
|
+
recordsNeedingRefetch: new Map(),
|
|
59204
|
+
errors: [],
|
|
59205
|
+
};
|
|
59206
|
+
const ids = [];
|
|
59207
|
+
const trackedFieldsByType = new Map();
|
|
59208
|
+
const apiNames = new Set();
|
|
59209
|
+
incomingRecords.forEach((record) => {
|
|
59210
|
+
ids.push(record.id);
|
|
59211
|
+
apiNames.add(record.apiName);
|
|
59212
|
+
});
|
|
59213
|
+
const existingRecords = await this.store.readRecords(ids);
|
|
59214
|
+
if (abortController.aborted) {
|
|
59215
|
+
return result;
|
|
59216
|
+
}
|
|
59217
|
+
const objectInfos = await this.objectInfoLoader.getObjectInfos(Array.from(apiNames));
|
|
59218
|
+
if (abortController.aborted) {
|
|
59219
|
+
return result;
|
|
59220
|
+
}
|
|
59221
|
+
const existingRecordsById = new Map(existingRecords.map((record) => [record.record.id, record]));
|
|
59222
|
+
for (const incomingRecord of incomingRecords) {
|
|
59223
|
+
const existingDurableRecordRepresentation = existingRecordsById.get(incomingRecord.id);
|
|
59224
|
+
const objectInfo = objectInfos[incomingRecord.apiName];
|
|
59225
|
+
if (existingDurableRecordRepresentation === undefined) {
|
|
59226
|
+
// this shouldn't happen but if it does, we should write the incoming record since there's nothing to merge
|
|
59227
|
+
result.recordsToWrite.push(incomingRecord);
|
|
59228
|
+
continue;
|
|
59229
|
+
}
|
|
59230
|
+
if (objectInfo === undefined) {
|
|
59231
|
+
// object infos are a prerequisite for priming so if we don't have one, we can't do anything
|
|
59232
|
+
result.errors.push({ id: incomingRecord.id, reason: 'object-info-missing' });
|
|
59233
|
+
continue;
|
|
59234
|
+
}
|
|
59235
|
+
const existingRecord = existingDurableRecordRepresentation.record;
|
|
59236
|
+
const mergedRecordResult = mergeRecord(existingRecord, incomingRecord, objectInfo);
|
|
59237
|
+
if (mergedRecordResult.ok) {
|
|
59238
|
+
if (mergedRecordResult.needsWrite) {
|
|
59239
|
+
result.recordsToWrite.push(mergedRecordResult.record);
|
|
59240
|
+
}
|
|
59241
|
+
else {
|
|
59242
|
+
result.resolvedRecords.push(mergedRecordResult.record.id);
|
|
59243
|
+
}
|
|
59244
|
+
continue;
|
|
59245
|
+
}
|
|
59246
|
+
else {
|
|
59247
|
+
const { code } = mergedRecordResult;
|
|
59248
|
+
const isConflict = code === 'conflict-drafts' ||
|
|
59249
|
+
code === 'conflict-spanning-record' ||
|
|
59250
|
+
code === 'conflict-missing-fields';
|
|
59251
|
+
if (isConflict) {
|
|
59252
|
+
let trackedFields = trackedFieldsByType.get(incomingRecord.apiName);
|
|
59253
|
+
if (trackedFields === undefined) {
|
|
59254
|
+
trackedFields = new Set();
|
|
59255
|
+
trackedFieldsByType.set(incomingRecord.apiName, trackedFields);
|
|
59256
|
+
}
|
|
59257
|
+
mergedRecordResult.fieldUnion.forEach((field) => trackedFields.add(field));
|
|
59258
|
+
if (code === 'conflict-missing-fields') {
|
|
59259
|
+
const additionalWorkForType = result.additionalWork.records[incomingRecord.apiName];
|
|
59260
|
+
if (additionalWorkForType === undefined) {
|
|
59261
|
+
result.additionalWork.records[incomingRecord.apiName] = {
|
|
59262
|
+
ids: [incomingRecord.id],
|
|
59263
|
+
fields: Array.from(trackedFields),
|
|
59264
|
+
};
|
|
59265
|
+
}
|
|
59266
|
+
else {
|
|
59267
|
+
additionalWorkForType.ids.push(incomingRecord.id);
|
|
59268
|
+
additionalWorkForType.fields = Array.from(trackedFields);
|
|
59269
|
+
}
|
|
59270
|
+
}
|
|
59271
|
+
else if (code === 'conflict-drafts' || code === 'conflict-spanning-record') {
|
|
59272
|
+
const recordByType = result.recordsNeedingRefetch.get(incomingRecord.apiName);
|
|
59273
|
+
if (recordByType === undefined) {
|
|
59274
|
+
result.recordsNeedingRefetch.set(incomingRecord.apiName, {
|
|
59275
|
+
ids: [incomingRecord.id],
|
|
59276
|
+
fields: Array.from(trackedFields),
|
|
59277
|
+
});
|
|
59278
|
+
}
|
|
59279
|
+
else {
|
|
59280
|
+
recordByType.ids.push(incomingRecord.id);
|
|
59281
|
+
recordByType.fields = Array.from(trackedFields);
|
|
59282
|
+
}
|
|
59283
|
+
}
|
|
59284
|
+
}
|
|
59285
|
+
}
|
|
59286
|
+
}
|
|
59287
|
+
return result;
|
|
59288
|
+
}
|
|
59289
|
+
}
|
|
59290
|
+
|
|
59050
59291
|
const DEFAULT_BATCH_SIZE = 500;
|
|
59051
59292
|
const DEFAULT_CONCURRENCY = 6;
|
|
59052
59293
|
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
@@ -59060,8 +59301,10 @@
|
|
|
59060
59301
|
this.recordLoader = config.recordLoader;
|
|
59061
59302
|
this.recordIngestor = config.recordIngestor;
|
|
59062
59303
|
this.objectInfoLoader = config.objectInfoLoader;
|
|
59304
|
+
this.ldsRecordRefresher = config.ldsRecordRefresher;
|
|
59063
59305
|
this.networkWorkerPool = new AsyncWorkerPool(this.concurrency);
|
|
59064
59306
|
this.useBatchGQL = ldsPrimingGraphqlBatch.isOpen({ fallback: false });
|
|
59307
|
+
this.conflictPool = new ConflictPool(config.store, this.objectInfoLoader);
|
|
59065
59308
|
}
|
|
59066
59309
|
// function that enqueues priming work
|
|
59067
59310
|
async enqueue(work) {
|
|
@@ -59186,7 +59429,9 @@
|
|
|
59186
59429
|
const { records } = result;
|
|
59187
59430
|
const beforeWrite = Date.now();
|
|
59188
59431
|
// dispatch the write but DO NOT wait on it to unblock the network pool
|
|
59189
|
-
this.recordIngestor
|
|
59432
|
+
this.recordIngestor
|
|
59433
|
+
.insertRecords(records, false)
|
|
59434
|
+
.then(({ written, conflicted, errors }) => {
|
|
59190
59435
|
this.emit('batch-written', {
|
|
59191
59436
|
written,
|
|
59192
59437
|
conflicted,
|
|
@@ -59209,16 +59454,69 @@
|
|
|
59209
59454
|
if (written.length > 0) {
|
|
59210
59455
|
this.emit('primed', Array.from(written));
|
|
59211
59456
|
}
|
|
59212
|
-
//
|
|
59457
|
+
// if any records could not be written to the store because there were conflicts, handle the conflicts
|
|
59458
|
+
if (conflicted.length > 0) {
|
|
59459
|
+
this.handleWriteConflicts(records, conflicted, abortController);
|
|
59460
|
+
}
|
|
59461
|
+
});
|
|
59462
|
+
}
|
|
59463
|
+
async handleWriteConflicts(records, conflicted, abortController) {
|
|
59464
|
+
const result = await this.conflictPool.enqueueConflictedRecords(records.filter((x) => conflicted.includes(x.id)), abortController);
|
|
59465
|
+
if (abortController.aborted) {
|
|
59466
|
+
return;
|
|
59467
|
+
}
|
|
59468
|
+
if (Object.keys(result.additionalWork.records).length > 0) {
|
|
59469
|
+
this.emit('conflict', {
|
|
59470
|
+
ids: Object.values(result.additionalWork.records).flatMap((record) => record.ids),
|
|
59471
|
+
resolution: 'priming-refresh',
|
|
59472
|
+
});
|
|
59473
|
+
this.enqueue(result.additionalWork);
|
|
59474
|
+
}
|
|
59475
|
+
if (result.resolvedRecords.length > 0) {
|
|
59476
|
+
this.emit('conflict', {
|
|
59477
|
+
ids: result.resolvedRecords,
|
|
59478
|
+
resolution: 'priming-merge',
|
|
59479
|
+
});
|
|
59480
|
+
this.emit('primed', result.resolvedRecords);
|
|
59481
|
+
}
|
|
59482
|
+
if (result.recordsToWrite.length > 0) {
|
|
59483
|
+
const { written, errors, conflicted } = await this.recordIngestor.insertRecords(result.recordsToWrite, true);
|
|
59484
|
+
if (written.length > 0) {
|
|
59485
|
+
const ids = Array.from(written);
|
|
59486
|
+
this.emit('conflict', { ids, resolution: 'priming-merge' });
|
|
59487
|
+
this.emit('primed', ids);
|
|
59488
|
+
}
|
|
59489
|
+
if (errors.length > 0) {
|
|
59490
|
+
errors.forEach(({ ids, message }) => {
|
|
59491
|
+
this.emit('error', {
|
|
59492
|
+
ids,
|
|
59493
|
+
code: 'unknown',
|
|
59494
|
+
message: message,
|
|
59495
|
+
});
|
|
59496
|
+
});
|
|
59497
|
+
}
|
|
59213
59498
|
if (conflicted.length > 0) {
|
|
59214
|
-
// for now emit conlicts as errors
|
|
59215
59499
|
this.emit('error', {
|
|
59216
|
-
ids:
|
|
59500
|
+
ids: conflicted,
|
|
59217
59501
|
code: 'unknown',
|
|
59218
|
-
message: '
|
|
59502
|
+
message: 'unexpected write conflict',
|
|
59219
59503
|
});
|
|
59220
59504
|
}
|
|
59221
|
-
}
|
|
59505
|
+
}
|
|
59506
|
+
if (result.recordsNeedingRefetch.size > 0) {
|
|
59507
|
+
const { loaded, errored } = await this.ldsRecordRefresher.loadRecords(result.recordsNeedingRefetch);
|
|
59508
|
+
if (loaded.length > 0) {
|
|
59509
|
+
this.emit('conflict', { resolution: 'lds-refresh', ids: loaded });
|
|
59510
|
+
this.emit('primed', loaded);
|
|
59511
|
+
}
|
|
59512
|
+
if (errored.length > 0) {
|
|
59513
|
+
this.emit('error', {
|
|
59514
|
+
ids: errored,
|
|
59515
|
+
code: 'unknown',
|
|
59516
|
+
message: `could not resolve conflicts`,
|
|
59517
|
+
});
|
|
59518
|
+
}
|
|
59519
|
+
}
|
|
59222
59520
|
}
|
|
59223
59521
|
async fetchMetadata(batches) {
|
|
59224
59522
|
const apiNames = Array.from(batches.reduce((acc, x) => {
|
|
@@ -59457,6 +59755,9 @@
|
|
|
59457
59755
|
});
|
|
59458
59756
|
session.on('primed', ({ length }) => {
|
|
59459
59757
|
});
|
|
59758
|
+
session.on('conflict', ({ ids, resolution }) => {
|
|
59759
|
+
reportPrimingConflict(resolution, ids.length);
|
|
59760
|
+
});
|
|
59460
59761
|
return session;
|
|
59461
59762
|
}
|
|
59462
59763
|
|
|
@@ -59638,19 +59939,72 @@
|
|
|
59638
59939
|
return batches;
|
|
59639
59940
|
}
|
|
59640
59941
|
|
|
59942
|
+
/**
|
|
59943
|
+
* A polyfill for Promise.allSettled
|
|
59944
|
+
* @param promises An array of promises
|
|
59945
|
+
* @returns the result of all the promises when they either fulfill or reject
|
|
59946
|
+
*/
|
|
59947
|
+
function allSettled(promises) {
|
|
59948
|
+
let wrappedPromises = promises.map((p) => Promise.resolve(p).then((value) => ({ status: 'fulfilled', value }), (reason) => ({ status: 'rejected', reason })));
|
|
59949
|
+
return Promise.all(wrappedPromises);
|
|
59950
|
+
}
|
|
59951
|
+
|
|
59952
|
+
class LdsPrimingRecordRefresher {
|
|
59953
|
+
constructor(getRecordsAdapter) {
|
|
59954
|
+
this.getRecordsAdapter = getRecordsAdapter;
|
|
59955
|
+
}
|
|
59956
|
+
async loadRecords(records) {
|
|
59957
|
+
const requestedRecords = new Set();
|
|
59958
|
+
const promises = Array.from(records).flatMap(([_apiName, value]) => {
|
|
59959
|
+
value.ids.forEach((id) => requestedRecords.add(id));
|
|
59960
|
+
return Promise.resolve(this.getRecordsAdapter({
|
|
59961
|
+
records: [
|
|
59962
|
+
{
|
|
59963
|
+
recordIds: value.ids,
|
|
59964
|
+
optionalFields: value.fields.map((f) => `${_apiName}.${f}`),
|
|
59965
|
+
},
|
|
59966
|
+
],
|
|
59967
|
+
}));
|
|
59968
|
+
});
|
|
59969
|
+
const promiseResults = await allSettled(promises);
|
|
59970
|
+
const loaded = [];
|
|
59971
|
+
promiseResults.forEach((promiseResult) => {
|
|
59972
|
+
if (promiseResult.status === 'fulfilled') {
|
|
59973
|
+
const batchResultRepresenatation = promiseResult.value;
|
|
59974
|
+
if (batchResultRepresenatation &&
|
|
59975
|
+
batchResultRepresenatation.state === 'Fulfilled') {
|
|
59976
|
+
batchResultRepresenatation.data.results.forEach((result) => {
|
|
59977
|
+
if (result.statusCode === 200) {
|
|
59978
|
+
const id = result.result.id;
|
|
59979
|
+
loaded.push(id);
|
|
59980
|
+
requestedRecords.delete(id);
|
|
59981
|
+
}
|
|
59982
|
+
});
|
|
59983
|
+
}
|
|
59984
|
+
}
|
|
59985
|
+
});
|
|
59986
|
+
// errored contains all the requestedRecords that weren't loaded
|
|
59987
|
+
const errored = Array.from(requestedRecords);
|
|
59988
|
+
return { loaded, errored };
|
|
59989
|
+
}
|
|
59990
|
+
}
|
|
59991
|
+
|
|
59641
59992
|
function primingSessionFactory(config) {
|
|
59642
59993
|
const { store, objectInfoService, getLuvio } = config;
|
|
59643
59994
|
const networkAdapter = new NimbusPrimingNetworkAdapter();
|
|
59644
59995
|
const recordLoader = new RecordLoaderGraphQL(networkAdapter);
|
|
59645
|
-
const
|
|
59996
|
+
const primingStore = new SqlitePrimingStore(getLuvio, store);
|
|
59997
|
+
const recordIngestor = new RecordIngestor(primingStore, getLuvio);
|
|
59646
59998
|
const session = new PrimingSession({
|
|
59647
59999
|
recordLoader,
|
|
59648
60000
|
recordIngestor,
|
|
60001
|
+
store: primingStore,
|
|
59649
60002
|
objectInfoLoader: {
|
|
59650
60003
|
getObjectInfos: objectInfoService.getObjectInfos.bind(objectInfoService),
|
|
59651
60004
|
},
|
|
59652
60005
|
concurrency: config.concurrency,
|
|
59653
60006
|
batchSize: config.batchSize,
|
|
60007
|
+
ldsRecordRefresher: new LdsPrimingRecordRefresher(config.getRecords),
|
|
59654
60008
|
});
|
|
59655
60009
|
return instrumentPrimingSession(session);
|
|
59656
60010
|
}
|
|
@@ -59664,6 +60018,7 @@
|
|
|
59664
60018
|
let lazyBaseDurableStore;
|
|
59665
60019
|
let lazyNetworkAdapter;
|
|
59666
60020
|
let lazyObjectInfoService;
|
|
60021
|
+
let lazyGetRecords;
|
|
59667
60022
|
/**
|
|
59668
60023
|
* This returns the LDS on Mobile Runtime singleton object.
|
|
59669
60024
|
*/
|
|
@@ -59734,6 +60089,7 @@
|
|
|
59734
60089
|
lazyLuvio = new Luvio(lazyEnvironment, {
|
|
59735
60090
|
instrument: instrumentLuvio,
|
|
59736
60091
|
});
|
|
60092
|
+
lazyGetRecords = getRecordsAdapterFactory(lazyLuvio);
|
|
59737
60093
|
// Currently instruments store runtime perf
|
|
59738
60094
|
setupMobileInstrumentation();
|
|
59739
60095
|
// If the inspection nimbus plugin is configured, inspection is enabled otherwise this is a no-op
|
|
@@ -59787,6 +60143,7 @@
|
|
|
59787
60143
|
getLuvio: () => lazyLuvio,
|
|
59788
60144
|
concurrency: config.concurrency,
|
|
59789
60145
|
batchSize: config.batchSize,
|
|
60146
|
+
getRecords: lazyGetRecords,
|
|
59790
60147
|
});
|
|
59791
60148
|
},
|
|
59792
60149
|
};
|
|
@@ -59803,7 +60160,7 @@
|
|
|
59803
60160
|
id: '@salesforce/lds-network-adapter',
|
|
59804
60161
|
instrument: instrument$1,
|
|
59805
60162
|
});
|
|
59806
|
-
// version: 1.
|
|
60163
|
+
// version: 1.209.0-6b633d53a
|
|
59807
60164
|
|
|
59808
60165
|
const { create: create$2, keys: keys$2 } = Object;
|
|
59809
60166
|
const { stringify: stringify$1, parse: parse$1 } = JSON;
|
|
@@ -77146,7 +77503,7 @@
|
|
|
77146
77503
|
configuration: { ...configurationForGraphQLAdapters },
|
|
77147
77504
|
instrument,
|
|
77148
77505
|
});
|
|
77149
|
-
// version: 1.
|
|
77506
|
+
// version: 1.209.0-b5ca8738d
|
|
77150
77507
|
|
|
77151
77508
|
// On core the unstable adapters are re-exported with different names,
|
|
77152
77509
|
|
|
@@ -79393,7 +79750,7 @@
|
|
|
79393
79750
|
unstable_graphQL_imperative = createImperativeAdapter(luvio, createInstrumentedAdapter(ldsAdapter, adapterMetadata), adapterMetadata);
|
|
79394
79751
|
graphQLImperative = ldsAdapter;
|
|
79395
79752
|
});
|
|
79396
|
-
// version: 1.
|
|
79753
|
+
// version: 1.209.0-b5ca8738d
|
|
79397
79754
|
|
|
79398
79755
|
var gqlApi = /*#__PURE__*/Object.freeze({
|
|
79399
79756
|
__proto__: null,
|
|
@@ -80099,4 +80456,4 @@
|
|
|
80099
80456
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
80100
80457
|
|
|
80101
80458
|
}));
|
|
80102
|
-
// version: 1.
|
|
80459
|
+
// version: 1.209.0-6b633d53a
|