@salesforce/lds-runtime-mobile 1.248.0 → 1.250.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/main.js +178 -126
- package/dist/types/priming/SqlitePrimingStore.d.ts +0 -5
- package/dist/types/utils/ObjectInfoService.d.ts +5 -0
- package/package.json +1 -1
- package/sfdc/main.js +178 -126
- package/sfdc/types/priming/SqlitePrimingStore.d.ts +0 -5
- package/sfdc/types/utils/ObjectInfoService.d.ts +5 -0
package/dist/main.js
CHANGED
|
@@ -16,7 +16,8 @@ import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrum
|
|
|
16
16
|
import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from '@luvio/engine';
|
|
17
17
|
import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
|
|
18
18
|
import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from '@luvio/graphql-parser';
|
|
19
|
-
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion,
|
|
19
|
+
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
|
|
20
|
+
import ldsIdempotencyWriteDisabled from '@salesforce/gate/lds.idempotencyWriteDisabled';
|
|
20
21
|
import caseSensitiveUserId from '@salesforce/user/Id';
|
|
21
22
|
import { idleDetector, getInstrumentation } from 'o11y/client';
|
|
22
23
|
import ldsUseShortUrlGate from '@salesforce/gate/lds.useShortUrl';
|
|
@@ -1237,12 +1238,12 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
1237
1238
|
}
|
|
1238
1239
|
return environment.getNode(key, stagingStore);
|
|
1239
1240
|
};
|
|
1240
|
-
const wrapNormalizedGraphNode = function (normalized) {
|
|
1241
|
+
const wrapNormalizedGraphNode = function (normalized, key) {
|
|
1241
1242
|
validateNotDisposed();
|
|
1242
1243
|
if (stagingStore === null) {
|
|
1243
1244
|
stagingStore = buildIngestStagingStore(environment);
|
|
1244
1245
|
}
|
|
1245
|
-
return environment.wrapNormalizedGraphNode(normalized, stagingStore);
|
|
1246
|
+
return environment.wrapNormalizedGraphNode(normalized, key, stagingStore);
|
|
1246
1247
|
};
|
|
1247
1248
|
const rebuildSnapshot = function (snapshot, onRebuild) {
|
|
1248
1249
|
validateNotDisposed();
|
|
@@ -4377,7 +4378,7 @@ function rootQuery(recordNodes, input) {
|
|
|
4377
4378
|
if (fails.length > 0) {
|
|
4378
4379
|
return failure(fails);
|
|
4379
4380
|
}
|
|
4380
|
-
return success({ type: 'root', connections });
|
|
4381
|
+
return success({ type: 'root', connections, queryKeys: input.queryKeys });
|
|
4381
4382
|
}
|
|
4382
4383
|
/**
|
|
4383
4384
|
* Given a connection array of LuvioSelectionCustomFieldNode
|
|
@@ -4597,11 +4598,11 @@ class StoreEvalPreconditioner {
|
|
|
4597
4598
|
// require at least this top level record present to resolve relationship lookups
|
|
4598
4599
|
const recordSelections = findRecordSelections(ast);
|
|
4599
4600
|
let metadata = {};
|
|
4601
|
+
const queryKeys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
|
|
4600
4602
|
if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
|
|
4601
|
-
|
|
4602
|
-
let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${keys
|
|
4603
|
+
let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${queryKeys
|
|
4603
4604
|
.map(() => '?')
|
|
4604
|
-
.join(',')})`,
|
|
4605
|
+
.join(',')})`, queryKeys);
|
|
4605
4606
|
metadata = sqlResult.rows.reduce((metadata, row) => {
|
|
4606
4607
|
metadata[row[0]] = JSON.parse(row[1]);
|
|
4607
4608
|
return metadata;
|
|
@@ -4653,6 +4654,7 @@ class StoreEvalPreconditioner {
|
|
|
4653
4654
|
draftFunctions,
|
|
4654
4655
|
connectionKeyBuilder,
|
|
4655
4656
|
metadata,
|
|
4657
|
+
queryKeys,
|
|
4656
4658
|
});
|
|
4657
4659
|
if (astTransformResult.isSuccess === false) {
|
|
4658
4660
|
for (const error of astTransformResult.error) {
|
|
@@ -4724,6 +4726,11 @@ async function evaluateSqlite(query, eventEmitter, store) {
|
|
|
4724
4726
|
eventEmitter({ type: 'graphql-db-read', sql, bindings, duration: Date.now() - start });
|
|
4725
4727
|
const data = JSON.parse(rawValue);
|
|
4726
4728
|
const seenRecords = createSeenRecords$1(data);
|
|
4729
|
+
if (query.queryKeys) {
|
|
4730
|
+
for (const queryKey of query.queryKeys) {
|
|
4731
|
+
seenRecords.add(queryKey);
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4727
4734
|
return { data, seenRecords };
|
|
4728
4735
|
}
|
|
4729
4736
|
const wrapStartEndEvents = (storeEval) => {
|
|
@@ -4950,6 +4957,21 @@ class AsyncWorkerPool {
|
|
|
4950
4957
|
}
|
|
4951
4958
|
}
|
|
4952
4959
|
|
|
4960
|
+
/**
|
|
4961
|
+
Use Math.random to generate v4 RFC4122 compliant uuid
|
|
4962
|
+
*/
|
|
4963
|
+
function uuidv4() {
|
|
4964
|
+
const uuid = [];
|
|
4965
|
+
for (let i = 0; i < 32; i++) {
|
|
4966
|
+
const random = (Math.random() * 16) | 0;
|
|
4967
|
+
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
|
4968
|
+
uuid.push('-');
|
|
4969
|
+
}
|
|
4970
|
+
uuid.push((i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16));
|
|
4971
|
+
}
|
|
4972
|
+
return uuid.join('');
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4953
4975
|
/**
|
|
4954
4976
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
4955
4977
|
* All rights reserved.
|
|
@@ -5163,20 +5185,6 @@ function generateUniqueDraftActionId(existingIds) {
|
|
|
5163
5185
|
}
|
|
5164
5186
|
return newId.toString();
|
|
5165
5187
|
}
|
|
5166
|
-
/**
|
|
5167
|
-
Use Math.random to generate v4 RFC4122 compliant uuid
|
|
5168
|
-
*/
|
|
5169
|
-
function uuidv4() {
|
|
5170
|
-
const uuid = [];
|
|
5171
|
-
for (let i = 0; i < 32; i++) {
|
|
5172
|
-
const random = (Math.random() * 16) | 0;
|
|
5173
|
-
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
|
5174
|
-
uuid.push('-');
|
|
5175
|
-
}
|
|
5176
|
-
uuid.push((i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16));
|
|
5177
|
-
}
|
|
5178
|
-
return uuid.join('');
|
|
5179
|
-
}
|
|
5180
5188
|
|
|
5181
5189
|
const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
|
|
5182
5190
|
const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
|
|
@@ -6053,7 +6061,12 @@ class AbstractResourceRequestActionHandler {
|
|
|
6053
6061
|
// the luvio store redirect table, during which a new draft might be enqueued
|
|
6054
6062
|
// which would not see a necessary mapping.
|
|
6055
6063
|
this.ephemeralRedirects = {};
|
|
6064
|
+
// determined by Server setup.
|
|
6056
6065
|
this.isIdempotencySupported = true;
|
|
6066
|
+
// idempotency write flag set by lds
|
|
6067
|
+
this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
|
|
6068
|
+
fallback: false,
|
|
6069
|
+
});
|
|
6057
6070
|
}
|
|
6058
6071
|
enqueue(data) {
|
|
6059
6072
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -6410,7 +6423,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
6410
6423
|
return [action.targetId];
|
|
6411
6424
|
}
|
|
6412
6425
|
hasIdempotencySupport() {
|
|
6413
|
-
return this.isIdempotencySupported;
|
|
6426
|
+
return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
|
|
6414
6427
|
}
|
|
6415
6428
|
async ingestResponses(responses, action) {
|
|
6416
6429
|
const luvio = this.getLuvio();
|
|
@@ -6941,6 +6954,20 @@ function buildQueryTypeStringKey(args) {
|
|
|
6941
6954
|
return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
|
|
6942
6955
|
}
|
|
6943
6956
|
|
|
6957
|
+
/**
|
|
6958
|
+
* @description Spec compliant way to retrieve the correct Operation from the Document that Luvio should operate on. https://spec.graphql.org/June2018/#sec-Named-Operation-Definitions
|
|
6959
|
+
* @param document
|
|
6960
|
+
* @param operationName
|
|
6961
|
+
* @returns The Operation in the GraphQL document we should use for the current call.
|
|
6962
|
+
*/
|
|
6963
|
+
function getOperationFromDocument(document, operationName) {
|
|
6964
|
+
const operations = document.definitions.filter((def) => def.kind === 'OperationDefinition');
|
|
6965
|
+
if (operationName) {
|
|
6966
|
+
return operations.find((def) => def.name !== undefined && def.name.value === operationName);
|
|
6967
|
+
}
|
|
6968
|
+
return operations[0]; // If a named operation is not provided, we return the first one
|
|
6969
|
+
}
|
|
6970
|
+
|
|
6944
6971
|
/**
|
|
6945
6972
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
6946
6973
|
* All rights reserved.
|
|
@@ -6948,14 +6975,20 @@ function buildQueryTypeStringKey(args) {
|
|
|
6948
6975
|
*/
|
|
6949
6976
|
|
|
6950
6977
|
|
|
6978
|
+
const MAX_BATCH_SIZE = 2000;
|
|
6951
6979
|
class DataLoader {
|
|
6952
|
-
constructor(batchLoadFn) {
|
|
6980
|
+
constructor(batchLoadFn, options) {
|
|
6953
6981
|
this._batchLoadFn = batchLoadFn;
|
|
6954
6982
|
this._batch = null;
|
|
6955
6983
|
this._batchScheduleFn = function (fn) {
|
|
6956
6984
|
setTimeout(fn, 0);
|
|
6957
6985
|
};
|
|
6958
6986
|
this._cacheMap = new Map();
|
|
6987
|
+
this._maxBatchSize = MAX_BATCH_SIZE;
|
|
6988
|
+
if (options !== undefined) {
|
|
6989
|
+
const { maxBatchSize } = options;
|
|
6990
|
+
this._maxBatchSize = maxBatchSize || MAX_BATCH_SIZE;
|
|
6991
|
+
}
|
|
6959
6992
|
}
|
|
6960
6993
|
load(key) {
|
|
6961
6994
|
if (key === null || key === undefined) {
|
|
@@ -6985,7 +7018,9 @@ class DataLoader {
|
|
|
6985
7018
|
// If there is an existing batch which has not yet dispatched and is within
|
|
6986
7019
|
// the limit of the batch size, then return it.
|
|
6987
7020
|
const existingBatch = this._batch;
|
|
6988
|
-
if (existingBatch !== null &&
|
|
7021
|
+
if (existingBatch !== null &&
|
|
7022
|
+
!existingBatch.hasDispatched &&
|
|
7023
|
+
existingBatch.keys.length < this._maxBatchSize) {
|
|
6989
7024
|
return existingBatch;
|
|
6990
7025
|
}
|
|
6991
7026
|
// Otherwise, create a new batch for this loader.
|
|
@@ -9378,7 +9413,9 @@ function extendSchemaWithObjectInfos(cache, objectInfoMap) {
|
|
|
9378
9413
|
];
|
|
9379
9414
|
// extend the schema and add resolvers
|
|
9380
9415
|
const schema = addResolversToSchema(extendSchema(cache.getSchema(), extensions), polymorphicFieldTypeNames);
|
|
9416
|
+
const polymorphicFieldTypeNamesSet = new Set(polymorphicFieldTypeNames);
|
|
9381
9417
|
cache.setSchema(schema);
|
|
9418
|
+
cache.setPolymorphicFieldTypeNames([...polymorphicFieldTypeNamesSet]);
|
|
9382
9419
|
return cache;
|
|
9383
9420
|
}
|
|
9384
9421
|
/**
|
|
@@ -9534,8 +9571,11 @@ function extendExistingRecordType(schema, type, objectInfo, objectInfoMap) {
|
|
|
9534
9571
|
let typedScalars = new Set();
|
|
9535
9572
|
let parentRelationshipFields = new Set();
|
|
9536
9573
|
const existingFields = keys$4(type.getFields());
|
|
9537
|
-
const missingFields = values$2(objectInfo.fields).filter((field) =>
|
|
9538
|
-
|
|
9574
|
+
const missingFields = values$2(objectInfo.fields).filter((field) => {
|
|
9575
|
+
return (existingFields.includes(field.apiName) === false ||
|
|
9576
|
+
(field.relationshipName !== null && field.referenceToInfos.length > 0));
|
|
9577
|
+
});
|
|
9578
|
+
const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached', existingFields);
|
|
9539
9579
|
const { apiName, childRelationships } = objectInfo;
|
|
9540
9580
|
// handles child relationship
|
|
9541
9581
|
const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields, existingFields);
|
|
@@ -9610,7 +9650,7 @@ function makeSpanningRecordConnections(schema, childRelationships, objectInfoMap
|
|
|
9610
9650
|
* @param recordTypeInSchema
|
|
9611
9651
|
* @returns
|
|
9612
9652
|
*/
|
|
9613
|
-
function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema) {
|
|
9653
|
+
function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema, existingFields = []) {
|
|
9614
9654
|
const polymorphicFieldTypeNames = new Set();
|
|
9615
9655
|
let fields = ``;
|
|
9616
9656
|
for (const field of values$2(fieldRepresentations)) {
|
|
@@ -9627,15 +9667,18 @@ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRela
|
|
|
9627
9667
|
// Only add the relationship if there is relevant objectinfos for it,
|
|
9628
9668
|
// otherwise we'd be defining types we cannot satisfy and aren't referenced in
|
|
9629
9669
|
// the query.
|
|
9630
|
-
if (objectInfoMap[relation.apiName] !== undefined
|
|
9670
|
+
if (objectInfoMap[relation.apiName] !== undefined &&
|
|
9671
|
+
existingFields.includes(field.relationshipName) === false) {
|
|
9631
9672
|
existingParentRelationships.add(field.relationshipName);
|
|
9632
9673
|
fields += `${field.relationshipName}: ${relation.apiName}\n`;
|
|
9633
9674
|
}
|
|
9634
9675
|
// For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
|
|
9635
9676
|
}
|
|
9636
9677
|
else if (field.referenceToInfos.length > 1) {
|
|
9637
|
-
|
|
9638
|
-
|
|
9678
|
+
if (recordTypeInSchema === 'Missing') {
|
|
9679
|
+
existingParentRelationships.add(field.relationshipName);
|
|
9680
|
+
fields += `${field.relationshipName}: Record\n`;
|
|
9681
|
+
}
|
|
9639
9682
|
for (const relation of field.referenceToInfos) {
|
|
9640
9683
|
if (objectInfoMap[relation.apiName] !== undefined) {
|
|
9641
9684
|
polymorphicFieldTypeNames.add(relation.apiName);
|
|
@@ -9698,6 +9741,8 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9698
9741
|
// this is only wrapped in a try to execute the event after the result was returned
|
|
9699
9742
|
try {
|
|
9700
9743
|
eventEmitter({ type: 'graphql-eval-start' });
|
|
9744
|
+
const operationNode = getOperationFromDocument(config.query);
|
|
9745
|
+
let topLevelQueries = [];
|
|
9701
9746
|
// assume that 'config.query' has required injected fields.
|
|
9702
9747
|
const modifiedAST = visit(config.query, {
|
|
9703
9748
|
Field: {
|
|
@@ -9727,6 +9772,25 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9727
9772
|
};
|
|
9728
9773
|
},
|
|
9729
9774
|
},
|
|
9775
|
+
Directive: {
|
|
9776
|
+
enter(node, _key, _parent, _path, ancester) {
|
|
9777
|
+
if (node.name.value === 'category' && node.arguments !== undefined) {
|
|
9778
|
+
for (let i = 0, len = node.arguments.length; i < len; i++) {
|
|
9779
|
+
const argument = node.arguments[i];
|
|
9780
|
+
if (isStringValueNode(argument.value) &&
|
|
9781
|
+
argument.value.value === 'recordQuery') {
|
|
9782
|
+
const parentNode = ancester[ancester.length - 1];
|
|
9783
|
+
if (isFieldNode(parentNode)) {
|
|
9784
|
+
topLevelQueries.push({
|
|
9785
|
+
argumentNodes: parentNode.arguments || [],
|
|
9786
|
+
recordName: parentNode.name.value,
|
|
9787
|
+
});
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
}
|
|
9791
|
+
}
|
|
9792
|
+
},
|
|
9793
|
+
},
|
|
9730
9794
|
});
|
|
9731
9795
|
eventEmitter({ type: 'graphql-preconditions-met' });
|
|
9732
9796
|
// create the resolver request context, runtime values and functions for
|
|
@@ -9745,7 +9809,18 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9745
9809
|
rootValue: {},
|
|
9746
9810
|
}));
|
|
9747
9811
|
eventEmitter({ type: 'graphql-evaluated' });
|
|
9748
|
-
|
|
9812
|
+
// add record key to seen ids
|
|
9813
|
+
const seenRecordIds = [...contextValue.seenRecordIds].map((id) => `UiApi::RecordRepresentation:${id}`);
|
|
9814
|
+
// if we have all the data to build the top level query
|
|
9815
|
+
// add it to the seen ids
|
|
9816
|
+
if (operationNode !== undefined && topLevelQueries.length > 0) {
|
|
9817
|
+
topLevelQueries.forEach((query) => {
|
|
9818
|
+
const { recordName, argumentNodes } = query;
|
|
9819
|
+
const queryString = buildKeyStringForRecordQuery(operationNode, config.variables || {}, argumentNodes, recordName);
|
|
9820
|
+
seenRecordIds.push(queryString);
|
|
9821
|
+
});
|
|
9822
|
+
}
|
|
9823
|
+
return { result, seenRecordIds };
|
|
9749
9824
|
}
|
|
9750
9825
|
finally {
|
|
9751
9826
|
eventEmitter({ type: 'graphql-eval-end' });
|
|
@@ -12084,34 +12159,42 @@ function applyReferenceLinksToDraft(record, draftMetadata) {
|
|
|
12084
12159
|
}
|
|
12085
12160
|
const { dataType, relationshipName, referenceToInfos } = fieldInfo;
|
|
12086
12161
|
const draftFieldValue = record.fields[draftField].value;
|
|
12087
|
-
if (dataType === 'Reference' && relationshipName !== null
|
|
12088
|
-
if (
|
|
12089
|
-
|
|
12162
|
+
if (dataType === 'Reference' && relationshipName !== null) {
|
|
12163
|
+
if (draftFieldValue === null) {
|
|
12164
|
+
recordFields[relationshipName] = {
|
|
12165
|
+
displayValue: null,
|
|
12166
|
+
value: null,
|
|
12167
|
+
};
|
|
12090
12168
|
}
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
displayValue: null,
|
|
12095
|
-
value: createLink(key),
|
|
12096
|
-
};
|
|
12097
|
-
// for custom objects, we select the 'Name' field
|
|
12098
|
-
// otherwise we check the object info for name fields.
|
|
12099
|
-
//if there are multiple we select 'Name' if it exists, otherwise the first one
|
|
12100
|
-
if (referencedRecord !== undefined && referenceToInfos.length > 0) {
|
|
12101
|
-
let nameField;
|
|
12102
|
-
const referenceToInfo = referenceToInfos[0];
|
|
12103
|
-
const nameFields = referenceToInfo.nameFields;
|
|
12104
|
-
if (nameFields.length !== 0) {
|
|
12105
|
-
nameField = nameFields.find((x) => x === 'Name');
|
|
12106
|
-
if (nameField === undefined) {
|
|
12107
|
-
nameField = nameFields[0];
|
|
12108
|
-
}
|
|
12169
|
+
else {
|
|
12170
|
+
if (typeof draftFieldValue !== 'string') {
|
|
12171
|
+
throw Error('reference field value is not a string');
|
|
12109
12172
|
}
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12173
|
+
const key = getRecordKeyForId(luvio, draftFieldValue);
|
|
12174
|
+
const referencedRecord = referencedRecords.get(key);
|
|
12175
|
+
recordFields[relationshipName] = {
|
|
12176
|
+
displayValue: null,
|
|
12177
|
+
value: createLink(key),
|
|
12178
|
+
};
|
|
12179
|
+
// for custom objects, we select the 'Name' field
|
|
12180
|
+
// otherwise we check the object info for name fields.
|
|
12181
|
+
//if there are multiple we select 'Name' if it exists, otherwise the first one
|
|
12182
|
+
if (referencedRecord !== undefined && referenceToInfos.length > 0) {
|
|
12183
|
+
let nameField;
|
|
12184
|
+
const referenceToInfo = referenceToInfos[0];
|
|
12185
|
+
const nameFields = referenceToInfo.nameFields;
|
|
12186
|
+
if (nameFields.length !== 0) {
|
|
12187
|
+
nameField = nameFields.find((x) => x === 'Name');
|
|
12188
|
+
if (nameField === undefined) {
|
|
12189
|
+
nameField = nameFields[0];
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
if (nameField !== undefined) {
|
|
12193
|
+
const nameFieldRef = referencedRecord.fields[nameField];
|
|
12194
|
+
if (nameFieldRef) {
|
|
12195
|
+
recordFields[relationshipName].displayValue =
|
|
12196
|
+
(_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
|
|
12197
|
+
}
|
|
12115
12198
|
}
|
|
12116
12199
|
}
|
|
12117
12200
|
}
|
|
@@ -12404,17 +12487,8 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12404
12487
|
};
|
|
12405
12488
|
for (const fieldName of keys$3(recordWithSpanningRefLinks.fields)) {
|
|
12406
12489
|
const fieldKey = buildRecordFieldStoreKey(key, fieldName);
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12410
|
-
publishData(fieldKey, fieldData);
|
|
12411
|
-
}
|
|
12412
|
-
else if (recordWithSpanningRefLinks.fields[fieldName] &&
|
|
12413
|
-
recordWithSpanningRefLinks.fields[fieldName].value &&
|
|
12414
|
-
recordWithSpanningRefLinks.fields[fieldName].value.__ref !== undefined) {
|
|
12415
|
-
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12416
|
-
publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
|
|
12417
|
-
}
|
|
12490
|
+
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12491
|
+
publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
|
|
12418
12492
|
}
|
|
12419
12493
|
// publish the normalized record
|
|
12420
12494
|
publishData(key, normalizedRecord);
|
|
@@ -13126,14 +13200,12 @@ function isLocalEvalSnapshot(snapshot) {
|
|
|
13126
13200
|
return 'rebuildWithLocalEval' in snapshot;
|
|
13127
13201
|
}
|
|
13128
13202
|
function createSeenRecords(ids, currentSnapshot) {
|
|
13129
|
-
let seenRecords = ids
|
|
13130
|
-
.map((id) => `UiApi::RecordRepresentation:${id}`)
|
|
13131
|
-
.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
|
|
13203
|
+
let seenRecords = ids.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
|
|
13132
13204
|
if (currentSnapshot.state !== 'Error') {
|
|
13133
13205
|
currentSnapshot.seenRecords.forEach((record) => {
|
|
13134
13206
|
let keyString = typeof record !== 'string' ? serializeStructuredKey(record) : record;
|
|
13135
|
-
if (keyString !== 'UiApi::
|
|
13136
|
-
keyString !== 'UiApi::
|
|
13207
|
+
if (keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi' &&
|
|
13208
|
+
keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi__query' &&
|
|
13137
13209
|
seenRecords.has(record) === false) {
|
|
13138
13210
|
seenRecords.add(record);
|
|
13139
13211
|
}
|
|
@@ -15375,6 +15447,14 @@ class ObjectInfoService {
|
|
|
15375
15447
|
return this.updateObjectInfoMapping(keyPrefix, apiName);
|
|
15376
15448
|
}
|
|
15377
15449
|
};
|
|
15450
|
+
this.getCachedObjectInfoStatus = async () => {
|
|
15451
|
+
const infos = await this.readObjectInfoDataFromDurableStore();
|
|
15452
|
+
const map = new Map();
|
|
15453
|
+
infos.forEach(({ apiName, expirationTimestamp }) => {
|
|
15454
|
+
map.set(apiName, { expiration: expirationTimestamp });
|
|
15455
|
+
});
|
|
15456
|
+
return map;
|
|
15457
|
+
};
|
|
15378
15458
|
this.isObjectInfoInDurableStore = async (apiName) => {
|
|
15379
15459
|
if (this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined) {
|
|
15380
15460
|
return Promise.resolve(true);
|
|
@@ -15383,12 +15463,10 @@ class ObjectInfoService {
|
|
|
15383
15463
|
return this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined;
|
|
15384
15464
|
};
|
|
15385
15465
|
this.loadObjectInfoMaps = async () => {
|
|
15386
|
-
const
|
|
15387
|
-
|
|
15388
|
-
const apiName = row[0];
|
|
15389
|
-
const keyPrefix = row[1];
|
|
15466
|
+
const infos = await this.readObjectInfoDataFromDurableStore();
|
|
15467
|
+
infos.forEach(({ keyPrefix, apiName }) => {
|
|
15390
15468
|
this.updateObjectInfoMapping(keyPrefix, apiName);
|
|
15391
|
-
}
|
|
15469
|
+
});
|
|
15392
15470
|
};
|
|
15393
15471
|
this.updateObjectInfoMapping = (keyPrefix, apiName) => {
|
|
15394
15472
|
this.apiNameToKeyPrefixMemoryCache[apiName] = keyPrefix;
|
|
@@ -15432,6 +15510,24 @@ class ObjectInfoService {
|
|
|
15432
15510
|
}
|
|
15433
15511
|
return snapshot.data;
|
|
15434
15512
|
}
|
|
15513
|
+
async readObjectInfoDataFromDurableStore() {
|
|
15514
|
+
const rows = (await this.durableStore.query(`
|
|
15515
|
+
SELECT
|
|
15516
|
+
json_extract(data, '$.apiName') as ApiName,
|
|
15517
|
+
json_extract(data, '$.keyPrefix') as keyPrefix,
|
|
15518
|
+
JSON_EXTRACT(metadata, '$.expirationTimestamp') AS expirationTimestamp
|
|
15519
|
+
from
|
|
15520
|
+
lds_data
|
|
15521
|
+
where
|
|
15522
|
+
key like '%ObjectInfoRepresentation%'`, [])).rows;
|
|
15523
|
+
return rows.map((row) => {
|
|
15524
|
+
return {
|
|
15525
|
+
apiName: row[0],
|
|
15526
|
+
keyPrefix: row[1],
|
|
15527
|
+
expirationTimestamp: row[2],
|
|
15528
|
+
};
|
|
15529
|
+
});
|
|
15530
|
+
}
|
|
15435
15531
|
}
|
|
15436
15532
|
|
|
15437
15533
|
function instrumentGraphQLEval(adapter) {
|
|
@@ -16530,7 +16626,7 @@ class ConflictPool {
|
|
|
16530
16626
|
}
|
|
16531
16627
|
}
|
|
16532
16628
|
|
|
16533
|
-
const DEFAULT_BATCH_SIZE = 500;
|
|
16629
|
+
const DEFAULT_BATCH_SIZE$1 = 500;
|
|
16534
16630
|
const DEFAULT_CONCURRENCY = 6;
|
|
16535
16631
|
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
16536
16632
|
class PrimingSession extends EventEmitter {
|
|
@@ -16538,7 +16634,7 @@ class PrimingSession extends EventEmitter {
|
|
|
16538
16634
|
var _a, _b;
|
|
16539
16635
|
super();
|
|
16540
16636
|
this.useBatchGQL = false;
|
|
16541
|
-
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
|
|
16637
|
+
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE$1;
|
|
16542
16638
|
this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
|
|
16543
16639
|
this.recordLoader = config.recordLoader;
|
|
16544
16640
|
this.recordIngestor = config.recordIngestor;
|
|
@@ -17159,10 +17255,6 @@ class NimbusPrimingNetworkAdapter {
|
|
|
17159
17255
|
// ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
|
|
17160
17256
|
const SQLITE_MAX_VARIABLE_NUMBER = 999;
|
|
17161
17257
|
const PARAMS_PER_RECORD = 3;
|
|
17162
|
-
/**
|
|
17163
|
-
* No key builder (or adapter) exists for the object info directory, we need to build the key manually
|
|
17164
|
-
*/
|
|
17165
|
-
const ObjectInfoDirectoryKey = `${UiApiNamespace}::${ObjectInfoDirectoryEntryRepresentationType}:`;
|
|
17166
17258
|
// We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
|
|
17167
17259
|
const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
|
|
17168
17260
|
class SqlitePrimingStore {
|
|
@@ -17227,44 +17319,6 @@ class SqlitePrimingStore {
|
|
|
17227
17319
|
};
|
|
17228
17320
|
}
|
|
17229
17321
|
}
|
|
17230
|
-
async readObjectInfoDirectory() {
|
|
17231
|
-
const sql = 'SELECT data FROM lds_data WHERE key = ?';
|
|
17232
|
-
const params = [ObjectInfoDirectoryKey];
|
|
17233
|
-
const result = await this.store.query(sql, params);
|
|
17234
|
-
if (result.rows.length === 1) {
|
|
17235
|
-
return JSON.parse(result.rows[0][0]);
|
|
17236
|
-
}
|
|
17237
|
-
return undefined;
|
|
17238
|
-
}
|
|
17239
|
-
async readObjectApiNames() {
|
|
17240
|
-
const sql = 'SELECT key FROM lds_data WHERE key like ?';
|
|
17241
|
-
const params = [`%${ObjectInfoRepresentationType}%`];
|
|
17242
|
-
const result = await this.store.query(sql, params);
|
|
17243
|
-
const apiNames = new Set();
|
|
17244
|
-
result.rows.forEach((row) => {
|
|
17245
|
-
const key = row[0];
|
|
17246
|
-
const parts = key.split(':');
|
|
17247
|
-
apiNames.add(parts[parts.length - 1]);
|
|
17248
|
-
});
|
|
17249
|
-
return apiNames;
|
|
17250
|
-
}
|
|
17251
|
-
writeObjectInfoDirectory(directory) {
|
|
17252
|
-
const sql = 'INSERT or IGNORE into lds_data (key, data) values (?, ?)';
|
|
17253
|
-
const params = [ObjectInfoDirectoryKey, JSON.stringify(directory)];
|
|
17254
|
-
return this.store.query(sql, params).then(() => { });
|
|
17255
|
-
}
|
|
17256
|
-
writeObjectInfos(objectInfos) {
|
|
17257
|
-
const sql = `INSERT or IGNORE into lds_data (key, data) values ${objectInfos
|
|
17258
|
-
.map(() => '(?, ?)')
|
|
17259
|
-
.join(',')};`;
|
|
17260
|
-
const params = [];
|
|
17261
|
-
objectInfos.forEach((objectInfo) => {
|
|
17262
|
-
const key = keyBuilderObjectInfo(this.getLuvio(), { apiName: objectInfo.apiName });
|
|
17263
|
-
params.push(key);
|
|
17264
|
-
params.push(JSON.stringify(objectInfo));
|
|
17265
|
-
});
|
|
17266
|
-
return this.store.query(sql, params).then(() => { });
|
|
17267
|
-
}
|
|
17268
17322
|
}
|
|
17269
17323
|
function batchArray(arr, batchSize = BATCH_SIZE) {
|
|
17270
17324
|
const batches = [];
|
|
@@ -17339,9 +17393,7 @@ function primingSessionFactory(config) {
|
|
|
17339
17393
|
recordLoader,
|
|
17340
17394
|
recordIngestor,
|
|
17341
17395
|
store: primingStore,
|
|
17342
|
-
objectInfoLoader:
|
|
17343
|
-
getObjectInfos: objectInfoService.getObjectInfos.bind(objectInfoService),
|
|
17344
|
-
},
|
|
17396
|
+
objectInfoLoader: objectInfoService,
|
|
17345
17397
|
concurrency: config.concurrency,
|
|
17346
17398
|
batchSize: config.batchSize,
|
|
17347
17399
|
ldsRecordRefresher: new LdsPrimingRecordRefresher(config.getRecords),
|
|
@@ -17507,4 +17559,4 @@ register({
|
|
|
17507
17559
|
});
|
|
17508
17560
|
|
|
17509
17561
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17510
|
-
// version: 1.
|
|
17562
|
+
// version: 1.250.0-9df9bc3d1
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { PrimingStore, RecordWithMetadata, WriteResult } from '@salesforce/lds-priming';
|
|
2
2
|
import type { Luvio } from '@luvio/engine';
|
|
3
|
-
import type { ObjectInfoDirectoryRepresentation, ObjectInfoRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
4
3
|
import type { SqliteStore } from '@salesforce/lds-store-sql';
|
|
5
4
|
export declare class SqlitePrimingStore implements PrimingStore {
|
|
6
5
|
private readonly getLuvio;
|
|
@@ -9,8 +8,4 @@ export declare class SqlitePrimingStore implements PrimingStore {
|
|
|
9
8
|
readRecords(ids: string[]): Promise<RecordWithMetadata[]>;
|
|
10
9
|
writeRecords(records: RecordWithMetadata[], overwrite: boolean): Promise<WriteResult>;
|
|
11
10
|
private writeBatch;
|
|
12
|
-
readObjectInfoDirectory(): Promise<ObjectInfoDirectoryRepresentation | undefined>;
|
|
13
|
-
readObjectApiNames(): Promise<Set<string>>;
|
|
14
|
-
writeObjectInfoDirectory(directory: ObjectInfoDirectoryRepresentation): Promise<void>;
|
|
15
|
-
writeObjectInfos(objectInfos: ObjectInfoRepresentation[]): Promise<void>;
|
|
16
11
|
}
|
|
@@ -10,6 +10,9 @@ type ObjectInfoDirectoryConfig = Parameters<ObjectInfoDirectoryAdapterReturn>[0]
|
|
|
10
10
|
export type ObjectInfoMap = {
|
|
11
11
|
[apiName: string]: ObjectInfoRepresentation;
|
|
12
12
|
};
|
|
13
|
+
interface ObjectInfoStatus {
|
|
14
|
+
expiration: number;
|
|
15
|
+
}
|
|
13
16
|
export declare class ObjectInfoService {
|
|
14
17
|
private getObjectInfoAdapter;
|
|
15
18
|
private getObjectInfosAdapter;
|
|
@@ -31,8 +34,10 @@ export declare class ObjectInfoService {
|
|
|
31
34
|
getObjectInfos(apiNames: string[]): Promise<ObjectInfoMap>;
|
|
32
35
|
getObjectInfoDirectory(): Promise<ObjectInfoDirectoryRepresentation | undefined>;
|
|
33
36
|
ensureObjectInfoCached: (apiName: string, entry?: ObjectInfoRepresentation) => Promise<void>;
|
|
37
|
+
getCachedObjectInfoStatus: () => Promise<Map<string, ObjectInfoStatus>>;
|
|
34
38
|
private isObjectInfoInDurableStore;
|
|
35
39
|
private loadObjectInfoMaps;
|
|
40
|
+
private readObjectInfoDataFromDurableStore;
|
|
36
41
|
private updateObjectInfoMapping;
|
|
37
42
|
}
|
|
38
43
|
export {};
|
package/package.json
CHANGED
package/sfdc/main.js
CHANGED
|
@@ -16,7 +16,8 @@ import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrum
|
|
|
16
16
|
import { HttpStatusCode, StoreKeySet, serializeStructuredKey, StringKeyInMemoryStore, Reader, deepFreeze, emitAdapterEvent, createCustomAdapterEventEmitter, StoreKeyMap, isFileReference, Environment, Luvio, InMemoryStore } from 'force/luvioEngine';
|
|
17
17
|
import excludeStaleRecordsGate from '@salesforce/gate/lds.graphqlEvalExcludeStaleRecords';
|
|
18
18
|
import { parseAndVisit, Kind, buildSchema, isObjectType, defaultFieldResolver, visit, execute, parse as parse$7, extendSchema, isScalarType } from 'force/ldsGraphqlParser';
|
|
19
|
-
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion,
|
|
19
|
+
import { RECORD_ID_PREFIX, RECORD_FIELDS_KEY_JUNCTION, isStoreKeyRecordViewEntity, getRecordId18, RECORD_REPRESENTATION_NAME, extractRecordIdFromStoreKey, keyBuilderQuickActionExecutionRepresentation, ingestQuickActionExecutionRepresentation, keyBuilderContentDocumentCompositeRepresentation, getResponseCacheKeysContentDocumentCompositeRepresentation, keyBuilderFromTypeContentDocumentCompositeRepresentation, ingestContentDocumentCompositeRepresentation, keyBuilderRecord, RECORD_VIEW_ENTITY_ID_PREFIX, getTypeCacheKeysRecord, keyBuilderFromTypeRecordRepresentation, ingestRecord, RecordRepresentationRepresentationType, ObjectInfoRepresentationType, getRecordAdapterFactory, getObjectInfoAdapterFactory, getObjectInfosAdapterFactory, getObjectInfoDirectoryAdapterFactory, UiApiNamespace, RecordRepresentationType, RecordRepresentationTTL, RecordRepresentationVersion, getRecordsAdapterFactory } from 'force/ldsAdaptersUiapi';
|
|
20
|
+
import ldsIdempotencyWriteDisabled from '@salesforce/gate/lds.idempotencyWriteDisabled';
|
|
20
21
|
import caseSensitiveUserId from '@salesforce/user/Id';
|
|
21
22
|
import { idleDetector, getInstrumentation } from 'o11y/client';
|
|
22
23
|
import ldsUseShortUrlGate from '@salesforce/gate/lds.useShortUrl';
|
|
@@ -1237,12 +1238,12 @@ function makeDurable(environment, { durableStore, instrumentation, useRevivingSt
|
|
|
1237
1238
|
}
|
|
1238
1239
|
return environment.getNode(key, stagingStore);
|
|
1239
1240
|
};
|
|
1240
|
-
const wrapNormalizedGraphNode = function (normalized) {
|
|
1241
|
+
const wrapNormalizedGraphNode = function (normalized, key) {
|
|
1241
1242
|
validateNotDisposed();
|
|
1242
1243
|
if (stagingStore === null) {
|
|
1243
1244
|
stagingStore = buildIngestStagingStore(environment);
|
|
1244
1245
|
}
|
|
1245
|
-
return environment.wrapNormalizedGraphNode(normalized, stagingStore);
|
|
1246
|
+
return environment.wrapNormalizedGraphNode(normalized, key, stagingStore);
|
|
1246
1247
|
};
|
|
1247
1248
|
const rebuildSnapshot = function (snapshot, onRebuild) {
|
|
1248
1249
|
validateNotDisposed();
|
|
@@ -4377,7 +4378,7 @@ function rootQuery(recordNodes, input) {
|
|
|
4377
4378
|
if (fails.length > 0) {
|
|
4378
4379
|
return failure(fails);
|
|
4379
4380
|
}
|
|
4380
|
-
return success({ type: 'root', connections });
|
|
4381
|
+
return success({ type: 'root', connections, queryKeys: input.queryKeys });
|
|
4381
4382
|
}
|
|
4382
4383
|
/**
|
|
4383
4384
|
* Given a connection array of LuvioSelectionCustomFieldNode
|
|
@@ -4597,11 +4598,11 @@ class StoreEvalPreconditioner {
|
|
|
4597
4598
|
// require at least this top level record present to resolve relationship lookups
|
|
4598
4599
|
const recordSelections = findRecordSelections(ast);
|
|
4599
4600
|
let metadata = {};
|
|
4601
|
+
const queryKeys = recordSelections.map((rs) => connectionKeyBuilder(rs, variables));
|
|
4600
4602
|
if (excludeStaleRecordsGate.isOpen({ fallback: false })) {
|
|
4601
|
-
|
|
4602
|
-
let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${keys
|
|
4603
|
+
let sqlResult = await sqliteStore.query(`select key, metadata from lds_data where key in (${queryKeys
|
|
4603
4604
|
.map(() => '?')
|
|
4604
|
-
.join(',')})`,
|
|
4605
|
+
.join(',')})`, queryKeys);
|
|
4605
4606
|
metadata = sqlResult.rows.reduce((metadata, row) => {
|
|
4606
4607
|
metadata[row[0]] = JSON.parse(row[1]);
|
|
4607
4608
|
return metadata;
|
|
@@ -4653,6 +4654,7 @@ class StoreEvalPreconditioner {
|
|
|
4653
4654
|
draftFunctions,
|
|
4654
4655
|
connectionKeyBuilder,
|
|
4655
4656
|
metadata,
|
|
4657
|
+
queryKeys,
|
|
4656
4658
|
});
|
|
4657
4659
|
if (astTransformResult.isSuccess === false) {
|
|
4658
4660
|
for (const error of astTransformResult.error) {
|
|
@@ -4724,6 +4726,11 @@ async function evaluateSqlite(query, eventEmitter, store) {
|
|
|
4724
4726
|
eventEmitter({ type: 'graphql-db-read', sql, bindings, duration: Date.now() - start });
|
|
4725
4727
|
const data = JSON.parse(rawValue);
|
|
4726
4728
|
const seenRecords = createSeenRecords$1(data);
|
|
4729
|
+
if (query.queryKeys) {
|
|
4730
|
+
for (const queryKey of query.queryKeys) {
|
|
4731
|
+
seenRecords.add(queryKey);
|
|
4732
|
+
}
|
|
4733
|
+
}
|
|
4727
4734
|
return { data, seenRecords };
|
|
4728
4735
|
}
|
|
4729
4736
|
const wrapStartEndEvents = (storeEval) => {
|
|
@@ -4950,6 +4957,21 @@ class AsyncWorkerPool {
|
|
|
4950
4957
|
}
|
|
4951
4958
|
}
|
|
4952
4959
|
|
|
4960
|
+
/**
|
|
4961
|
+
Use Math.random to generate v4 RFC4122 compliant uuid
|
|
4962
|
+
*/
|
|
4963
|
+
function uuidv4() {
|
|
4964
|
+
const uuid = [];
|
|
4965
|
+
for (let i = 0; i < 32; i++) {
|
|
4966
|
+
const random = (Math.random() * 16) | 0;
|
|
4967
|
+
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
|
4968
|
+
uuid.push('-');
|
|
4969
|
+
}
|
|
4970
|
+
uuid.push((i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16));
|
|
4971
|
+
}
|
|
4972
|
+
return uuid.join('');
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4953
4975
|
/**
|
|
4954
4976
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
4955
4977
|
* All rights reserved.
|
|
@@ -5163,20 +5185,6 @@ function generateUniqueDraftActionId(existingIds) {
|
|
|
5163
5185
|
}
|
|
5164
5186
|
return newId.toString();
|
|
5165
5187
|
}
|
|
5166
|
-
/**
|
|
5167
|
-
Use Math.random to generate v4 RFC4122 compliant uuid
|
|
5168
|
-
*/
|
|
5169
|
-
function uuidv4() {
|
|
5170
|
-
const uuid = [];
|
|
5171
|
-
for (let i = 0; i < 32; i++) {
|
|
5172
|
-
const random = (Math.random() * 16) | 0;
|
|
5173
|
-
if (i === 8 || i === 12 || i === 16 || i === 20) {
|
|
5174
|
-
uuid.push('-');
|
|
5175
|
-
}
|
|
5176
|
-
uuid.push((i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16));
|
|
5177
|
-
}
|
|
5178
|
-
return uuid.join('');
|
|
5179
|
-
}
|
|
5180
5188
|
|
|
5181
5189
|
const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
|
|
5182
5190
|
const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
|
|
@@ -6053,7 +6061,12 @@ class AbstractResourceRequestActionHandler {
|
|
|
6053
6061
|
// the luvio store redirect table, during which a new draft might be enqueued
|
|
6054
6062
|
// which would not see a necessary mapping.
|
|
6055
6063
|
this.ephemeralRedirects = {};
|
|
6064
|
+
// determined by Server setup.
|
|
6056
6065
|
this.isIdempotencySupported = true;
|
|
6066
|
+
// idempotency write flag set by lds
|
|
6067
|
+
this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
|
|
6068
|
+
fallback: false,
|
|
6069
|
+
});
|
|
6057
6070
|
}
|
|
6058
6071
|
enqueue(data) {
|
|
6059
6072
|
return this.draftQueue.enqueue(this.handlerId, data);
|
|
@@ -6410,7 +6423,7 @@ class AbstractResourceRequestActionHandler {
|
|
|
6410
6423
|
return [action.targetId];
|
|
6411
6424
|
}
|
|
6412
6425
|
hasIdempotencySupport() {
|
|
6413
|
-
return this.isIdempotencySupported;
|
|
6426
|
+
return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
|
|
6414
6427
|
}
|
|
6415
6428
|
async ingestResponses(responses, action) {
|
|
6416
6429
|
const luvio = this.getLuvio();
|
|
@@ -6941,6 +6954,20 @@ function buildQueryTypeStringKey(args) {
|
|
|
6941
6954
|
return `${keyPrefix}::${schemaName}::${queryTypeName}[${serializeOperationNode(operationNode, variables, fragmentMap)}]`;
|
|
6942
6955
|
}
|
|
6943
6956
|
|
|
6957
|
+
/**
|
|
6958
|
+
* @description Spec compliant way to retrieve the correct Operation from the Document that Luvio should operate on. https://spec.graphql.org/June2018/#sec-Named-Operation-Definitions
|
|
6959
|
+
* @param document
|
|
6960
|
+
* @param operationName
|
|
6961
|
+
* @returns The Operation in the GraphQL document we should use for the current call.
|
|
6962
|
+
*/
|
|
6963
|
+
function getOperationFromDocument(document, operationName) {
|
|
6964
|
+
const operations = document.definitions.filter((def) => def.kind === 'OperationDefinition');
|
|
6965
|
+
if (operationName) {
|
|
6966
|
+
return operations.find((def) => def.name !== undefined && def.name.value === operationName);
|
|
6967
|
+
}
|
|
6968
|
+
return operations[0]; // If a named operation is not provided, we return the first one
|
|
6969
|
+
}
|
|
6970
|
+
|
|
6944
6971
|
/**
|
|
6945
6972
|
* Copyright (c) 2022, Salesforce, Inc.,
|
|
6946
6973
|
* All rights reserved.
|
|
@@ -6948,14 +6975,20 @@ function buildQueryTypeStringKey(args) {
|
|
|
6948
6975
|
*/
|
|
6949
6976
|
|
|
6950
6977
|
|
|
6978
|
+
const MAX_BATCH_SIZE = 2000;
|
|
6951
6979
|
class DataLoader {
|
|
6952
|
-
constructor(batchLoadFn) {
|
|
6980
|
+
constructor(batchLoadFn, options) {
|
|
6953
6981
|
this._batchLoadFn = batchLoadFn;
|
|
6954
6982
|
this._batch = null;
|
|
6955
6983
|
this._batchScheduleFn = function (fn) {
|
|
6956
6984
|
setTimeout(fn, 0);
|
|
6957
6985
|
};
|
|
6958
6986
|
this._cacheMap = new Map();
|
|
6987
|
+
this._maxBatchSize = MAX_BATCH_SIZE;
|
|
6988
|
+
if (options !== undefined) {
|
|
6989
|
+
const { maxBatchSize } = options;
|
|
6990
|
+
this._maxBatchSize = maxBatchSize || MAX_BATCH_SIZE;
|
|
6991
|
+
}
|
|
6959
6992
|
}
|
|
6960
6993
|
load(key) {
|
|
6961
6994
|
if (key === null || key === undefined) {
|
|
@@ -6985,7 +7018,9 @@ class DataLoader {
|
|
|
6985
7018
|
// If there is an existing batch which has not yet dispatched and is within
|
|
6986
7019
|
// the limit of the batch size, then return it.
|
|
6987
7020
|
const existingBatch = this._batch;
|
|
6988
|
-
if (existingBatch !== null &&
|
|
7021
|
+
if (existingBatch !== null &&
|
|
7022
|
+
!existingBatch.hasDispatched &&
|
|
7023
|
+
existingBatch.keys.length < this._maxBatchSize) {
|
|
6989
7024
|
return existingBatch;
|
|
6990
7025
|
}
|
|
6991
7026
|
// Otherwise, create a new batch for this loader.
|
|
@@ -9378,7 +9413,9 @@ function extendSchemaWithObjectInfos(cache, objectInfoMap) {
|
|
|
9378
9413
|
];
|
|
9379
9414
|
// extend the schema and add resolvers
|
|
9380
9415
|
const schema = addResolversToSchema(extendSchema(cache.getSchema(), extensions), polymorphicFieldTypeNames);
|
|
9416
|
+
const polymorphicFieldTypeNamesSet = new Set(polymorphicFieldTypeNames);
|
|
9381
9417
|
cache.setSchema(schema);
|
|
9418
|
+
cache.setPolymorphicFieldTypeNames([...polymorphicFieldTypeNamesSet]);
|
|
9382
9419
|
return cache;
|
|
9383
9420
|
}
|
|
9384
9421
|
/**
|
|
@@ -9534,8 +9571,11 @@ function extendExistingRecordType(schema, type, objectInfo, objectInfoMap) {
|
|
|
9534
9571
|
let typedScalars = new Set();
|
|
9535
9572
|
let parentRelationshipFields = new Set();
|
|
9536
9573
|
const existingFields = keys$4(type.getFields());
|
|
9537
|
-
const missingFields = values$2(objectInfo.fields).filter((field) =>
|
|
9538
|
-
|
|
9574
|
+
const missingFields = values$2(objectInfo.fields).filter((field) => {
|
|
9575
|
+
return (existingFields.includes(field.apiName) === false ||
|
|
9576
|
+
(field.relationshipName !== null && field.referenceToInfos.length > 0));
|
|
9577
|
+
});
|
|
9578
|
+
const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached', existingFields);
|
|
9539
9579
|
const { apiName, childRelationships } = objectInfo;
|
|
9540
9580
|
// handles child relationship
|
|
9541
9581
|
const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields, existingFields);
|
|
@@ -9610,7 +9650,7 @@ function makeSpanningRecordConnections(schema, childRelationships, objectInfoMap
|
|
|
9610
9650
|
* @param recordTypeInSchema
|
|
9611
9651
|
* @returns
|
|
9612
9652
|
*/
|
|
9613
|
-
function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema) {
|
|
9653
|
+
function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema, existingFields = []) {
|
|
9614
9654
|
const polymorphicFieldTypeNames = new Set();
|
|
9615
9655
|
let fields = ``;
|
|
9616
9656
|
for (const field of values$2(fieldRepresentations)) {
|
|
@@ -9627,15 +9667,18 @@ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRela
|
|
|
9627
9667
|
// Only add the relationship if there is relevant objectinfos for it,
|
|
9628
9668
|
// otherwise we'd be defining types we cannot satisfy and aren't referenced in
|
|
9629
9669
|
// the query.
|
|
9630
|
-
if (objectInfoMap[relation.apiName] !== undefined
|
|
9670
|
+
if (objectInfoMap[relation.apiName] !== undefined &&
|
|
9671
|
+
existingFields.includes(field.relationshipName) === false) {
|
|
9631
9672
|
existingParentRelationships.add(field.relationshipName);
|
|
9632
9673
|
fields += `${field.relationshipName}: ${relation.apiName}\n`;
|
|
9633
9674
|
}
|
|
9634
9675
|
// For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
|
|
9635
9676
|
}
|
|
9636
9677
|
else if (field.referenceToInfos.length > 1) {
|
|
9637
|
-
|
|
9638
|
-
|
|
9678
|
+
if (recordTypeInSchema === 'Missing') {
|
|
9679
|
+
existingParentRelationships.add(field.relationshipName);
|
|
9680
|
+
fields += `${field.relationshipName}: Record\n`;
|
|
9681
|
+
}
|
|
9639
9682
|
for (const relation of field.referenceToInfos) {
|
|
9640
9683
|
if (objectInfoMap[relation.apiName] !== undefined) {
|
|
9641
9684
|
polymorphicFieldTypeNames.add(relation.apiName);
|
|
@@ -9698,6 +9741,8 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9698
9741
|
// this is only wrapped in a try to execute the event after the result was returned
|
|
9699
9742
|
try {
|
|
9700
9743
|
eventEmitter({ type: 'graphql-eval-start' });
|
|
9744
|
+
const operationNode = getOperationFromDocument(config.query);
|
|
9745
|
+
let topLevelQueries = [];
|
|
9701
9746
|
// assume that 'config.query' has required injected fields.
|
|
9702
9747
|
const modifiedAST = visit(config.query, {
|
|
9703
9748
|
Field: {
|
|
@@ -9727,6 +9772,25 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9727
9772
|
};
|
|
9728
9773
|
},
|
|
9729
9774
|
},
|
|
9775
|
+
Directive: {
|
|
9776
|
+
enter(node, _key, _parent, _path, ancester) {
|
|
9777
|
+
if (node.name.value === 'category' && node.arguments !== undefined) {
|
|
9778
|
+
for (let i = 0, len = node.arguments.length; i < len; i++) {
|
|
9779
|
+
const argument = node.arguments[i];
|
|
9780
|
+
if (isStringValueNode(argument.value) &&
|
|
9781
|
+
argument.value.value === 'recordQuery') {
|
|
9782
|
+
const parentNode = ancester[ancester.length - 1];
|
|
9783
|
+
if (isFieldNode(parentNode)) {
|
|
9784
|
+
topLevelQueries.push({
|
|
9785
|
+
argumentNodes: parentNode.arguments || [],
|
|
9786
|
+
recordName: parentNode.name.value,
|
|
9787
|
+
});
|
|
9788
|
+
}
|
|
9789
|
+
}
|
|
9790
|
+
}
|
|
9791
|
+
}
|
|
9792
|
+
},
|
|
9793
|
+
},
|
|
9730
9794
|
});
|
|
9731
9795
|
eventEmitter({ type: 'graphql-preconditions-met' });
|
|
9732
9796
|
// create the resolver request context, runtime values and functions for
|
|
@@ -9745,7 +9809,18 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
|
|
|
9745
9809
|
rootValue: {},
|
|
9746
9810
|
}));
|
|
9747
9811
|
eventEmitter({ type: 'graphql-evaluated' });
|
|
9748
|
-
|
|
9812
|
+
// add record key to seen ids
|
|
9813
|
+
const seenRecordIds = [...contextValue.seenRecordIds].map((id) => `UiApi::RecordRepresentation:${id}`);
|
|
9814
|
+
// if we have all the data to build the top level query
|
|
9815
|
+
// add it to the seen ids
|
|
9816
|
+
if (operationNode !== undefined && topLevelQueries.length > 0) {
|
|
9817
|
+
topLevelQueries.forEach((query) => {
|
|
9818
|
+
const { recordName, argumentNodes } = query;
|
|
9819
|
+
const queryString = buildKeyStringForRecordQuery(operationNode, config.variables || {}, argumentNodes, recordName);
|
|
9820
|
+
seenRecordIds.push(queryString);
|
|
9821
|
+
});
|
|
9822
|
+
}
|
|
9823
|
+
return { result, seenRecordIds };
|
|
9749
9824
|
}
|
|
9750
9825
|
finally {
|
|
9751
9826
|
eventEmitter({ type: 'graphql-eval-end' });
|
|
@@ -12084,34 +12159,42 @@ function applyReferenceLinksToDraft(record, draftMetadata) {
|
|
|
12084
12159
|
}
|
|
12085
12160
|
const { dataType, relationshipName, referenceToInfos } = fieldInfo;
|
|
12086
12161
|
const draftFieldValue = record.fields[draftField].value;
|
|
12087
|
-
if (dataType === 'Reference' && relationshipName !== null
|
|
12088
|
-
if (
|
|
12089
|
-
|
|
12162
|
+
if (dataType === 'Reference' && relationshipName !== null) {
|
|
12163
|
+
if (draftFieldValue === null) {
|
|
12164
|
+
recordFields[relationshipName] = {
|
|
12165
|
+
displayValue: null,
|
|
12166
|
+
value: null,
|
|
12167
|
+
};
|
|
12090
12168
|
}
|
|
12091
|
-
|
|
12092
|
-
|
|
12093
|
-
|
|
12094
|
-
displayValue: null,
|
|
12095
|
-
value: createLink(key),
|
|
12096
|
-
};
|
|
12097
|
-
// for custom objects, we select the 'Name' field
|
|
12098
|
-
// otherwise we check the object info for name fields.
|
|
12099
|
-
//if there are multiple we select 'Name' if it exists, otherwise the first one
|
|
12100
|
-
if (referencedRecord !== undefined && referenceToInfos.length > 0) {
|
|
12101
|
-
let nameField;
|
|
12102
|
-
const referenceToInfo = referenceToInfos[0];
|
|
12103
|
-
const nameFields = referenceToInfo.nameFields;
|
|
12104
|
-
if (nameFields.length !== 0) {
|
|
12105
|
-
nameField = nameFields.find((x) => x === 'Name');
|
|
12106
|
-
if (nameField === undefined) {
|
|
12107
|
-
nameField = nameFields[0];
|
|
12108
|
-
}
|
|
12169
|
+
else {
|
|
12170
|
+
if (typeof draftFieldValue !== 'string') {
|
|
12171
|
+
throw Error('reference field value is not a string');
|
|
12109
12172
|
}
|
|
12110
|
-
|
|
12111
|
-
|
|
12112
|
-
|
|
12113
|
-
|
|
12114
|
-
|
|
12173
|
+
const key = getRecordKeyForId(luvio, draftFieldValue);
|
|
12174
|
+
const referencedRecord = referencedRecords.get(key);
|
|
12175
|
+
recordFields[relationshipName] = {
|
|
12176
|
+
displayValue: null,
|
|
12177
|
+
value: createLink(key),
|
|
12178
|
+
};
|
|
12179
|
+
// for custom objects, we select the 'Name' field
|
|
12180
|
+
// otherwise we check the object info for name fields.
|
|
12181
|
+
//if there are multiple we select 'Name' if it exists, otherwise the first one
|
|
12182
|
+
if (referencedRecord !== undefined && referenceToInfos.length > 0) {
|
|
12183
|
+
let nameField;
|
|
12184
|
+
const referenceToInfo = referenceToInfos[0];
|
|
12185
|
+
const nameFields = referenceToInfo.nameFields;
|
|
12186
|
+
if (nameFields.length !== 0) {
|
|
12187
|
+
nameField = nameFields.find((x) => x === 'Name');
|
|
12188
|
+
if (nameField === undefined) {
|
|
12189
|
+
nameField = nameFields[0];
|
|
12190
|
+
}
|
|
12191
|
+
}
|
|
12192
|
+
if (nameField !== undefined) {
|
|
12193
|
+
const nameFieldRef = referencedRecord.fields[nameField];
|
|
12194
|
+
if (nameFieldRef) {
|
|
12195
|
+
recordFields[relationshipName].displayValue =
|
|
12196
|
+
(_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
|
|
12197
|
+
}
|
|
12115
12198
|
}
|
|
12116
12199
|
}
|
|
12117
12200
|
}
|
|
@@ -12404,17 +12487,8 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
|
|
|
12404
12487
|
};
|
|
12405
12488
|
for (const fieldName of keys$3(recordWithSpanningRefLinks.fields)) {
|
|
12406
12489
|
const fieldKey = buildRecordFieldStoreKey(key, fieldName);
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12410
|
-
publishData(fieldKey, fieldData);
|
|
12411
|
-
}
|
|
12412
|
-
else if (recordWithSpanningRefLinks.fields[fieldName] &&
|
|
12413
|
-
recordWithSpanningRefLinks.fields[fieldName].value &&
|
|
12414
|
-
recordWithSpanningRefLinks.fields[fieldName].value.__ref !== undefined) {
|
|
12415
|
-
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12416
|
-
publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
|
|
12417
|
-
}
|
|
12490
|
+
normalizedRecord.fields[fieldName] = { __ref: fieldKey };
|
|
12491
|
+
publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
|
|
12418
12492
|
}
|
|
12419
12493
|
// publish the normalized record
|
|
12420
12494
|
publishData(key, normalizedRecord);
|
|
@@ -13126,14 +13200,12 @@ function isLocalEvalSnapshot(snapshot) {
|
|
|
13126
13200
|
return 'rebuildWithLocalEval' in snapshot;
|
|
13127
13201
|
}
|
|
13128
13202
|
function createSeenRecords(ids, currentSnapshot) {
|
|
13129
|
-
let seenRecords = ids
|
|
13130
|
-
.map((id) => `UiApi::RecordRepresentation:${id}`)
|
|
13131
|
-
.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
|
|
13203
|
+
let seenRecords = ids.reduce((acc, curr) => (acc.add(curr), acc), new StoreKeySet());
|
|
13132
13204
|
if (currentSnapshot.state !== 'Error') {
|
|
13133
13205
|
currentSnapshot.seenRecords.forEach((record) => {
|
|
13134
13206
|
let keyString = typeof record !== 'string' ? serializeStructuredKey(record) : record;
|
|
13135
|
-
if (keyString !== 'UiApi::
|
|
13136
|
-
keyString !== 'UiApi::
|
|
13207
|
+
if (keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi' &&
|
|
13208
|
+
keyString !== 'UiApi::uiapi::Query[uiapi]__uiapi__query' &&
|
|
13137
13209
|
seenRecords.has(record) === false) {
|
|
13138
13210
|
seenRecords.add(record);
|
|
13139
13211
|
}
|
|
@@ -15375,6 +15447,14 @@ class ObjectInfoService {
|
|
|
15375
15447
|
return this.updateObjectInfoMapping(keyPrefix, apiName);
|
|
15376
15448
|
}
|
|
15377
15449
|
};
|
|
15450
|
+
this.getCachedObjectInfoStatus = async () => {
|
|
15451
|
+
const infos = await this.readObjectInfoDataFromDurableStore();
|
|
15452
|
+
const map = new Map();
|
|
15453
|
+
infos.forEach(({ apiName, expirationTimestamp }) => {
|
|
15454
|
+
map.set(apiName, { expiration: expirationTimestamp });
|
|
15455
|
+
});
|
|
15456
|
+
return map;
|
|
15457
|
+
};
|
|
15378
15458
|
this.isObjectInfoInDurableStore = async (apiName) => {
|
|
15379
15459
|
if (this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined) {
|
|
15380
15460
|
return Promise.resolve(true);
|
|
@@ -15383,12 +15463,10 @@ class ObjectInfoService {
|
|
|
15383
15463
|
return this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined;
|
|
15384
15464
|
};
|
|
15385
15465
|
this.loadObjectInfoMaps = async () => {
|
|
15386
|
-
const
|
|
15387
|
-
|
|
15388
|
-
const apiName = row[0];
|
|
15389
|
-
const keyPrefix = row[1];
|
|
15466
|
+
const infos = await this.readObjectInfoDataFromDurableStore();
|
|
15467
|
+
infos.forEach(({ keyPrefix, apiName }) => {
|
|
15390
15468
|
this.updateObjectInfoMapping(keyPrefix, apiName);
|
|
15391
|
-
}
|
|
15469
|
+
});
|
|
15392
15470
|
};
|
|
15393
15471
|
this.updateObjectInfoMapping = (keyPrefix, apiName) => {
|
|
15394
15472
|
this.apiNameToKeyPrefixMemoryCache[apiName] = keyPrefix;
|
|
@@ -15432,6 +15510,24 @@ class ObjectInfoService {
|
|
|
15432
15510
|
}
|
|
15433
15511
|
return snapshot.data;
|
|
15434
15512
|
}
|
|
15513
|
+
async readObjectInfoDataFromDurableStore() {
|
|
15514
|
+
const rows = (await this.durableStore.query(`
|
|
15515
|
+
SELECT
|
|
15516
|
+
json_extract(data, '$.apiName') as ApiName,
|
|
15517
|
+
json_extract(data, '$.keyPrefix') as keyPrefix,
|
|
15518
|
+
JSON_EXTRACT(metadata, '$.expirationTimestamp') AS expirationTimestamp
|
|
15519
|
+
from
|
|
15520
|
+
lds_data
|
|
15521
|
+
where
|
|
15522
|
+
key like '%ObjectInfoRepresentation%'`, [])).rows;
|
|
15523
|
+
return rows.map((row) => {
|
|
15524
|
+
return {
|
|
15525
|
+
apiName: row[0],
|
|
15526
|
+
keyPrefix: row[1],
|
|
15527
|
+
expirationTimestamp: row[2],
|
|
15528
|
+
};
|
|
15529
|
+
});
|
|
15530
|
+
}
|
|
15435
15531
|
}
|
|
15436
15532
|
|
|
15437
15533
|
function instrumentGraphQLEval(adapter) {
|
|
@@ -16530,7 +16626,7 @@ class ConflictPool {
|
|
|
16530
16626
|
}
|
|
16531
16627
|
}
|
|
16532
16628
|
|
|
16533
|
-
const DEFAULT_BATCH_SIZE = 500;
|
|
16629
|
+
const DEFAULT_BATCH_SIZE$1 = 500;
|
|
16534
16630
|
const DEFAULT_CONCURRENCY = 6;
|
|
16535
16631
|
const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
|
|
16536
16632
|
class PrimingSession extends EventEmitter {
|
|
@@ -16538,7 +16634,7 @@ class PrimingSession extends EventEmitter {
|
|
|
16538
16634
|
var _a, _b;
|
|
16539
16635
|
super();
|
|
16540
16636
|
this.useBatchGQL = false;
|
|
16541
|
-
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
|
|
16637
|
+
this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE$1;
|
|
16542
16638
|
this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
|
|
16543
16639
|
this.recordLoader = config.recordLoader;
|
|
16544
16640
|
this.recordIngestor = config.recordIngestor;
|
|
@@ -17159,10 +17255,6 @@ class NimbusPrimingNetworkAdapter {
|
|
|
17159
17255
|
// ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
|
|
17160
17256
|
const SQLITE_MAX_VARIABLE_NUMBER = 999;
|
|
17161
17257
|
const PARAMS_PER_RECORD = 3;
|
|
17162
|
-
/**
|
|
17163
|
-
* No key builder (or adapter) exists for the object info directory, we need to build the key manually
|
|
17164
|
-
*/
|
|
17165
|
-
const ObjectInfoDirectoryKey = `${UiApiNamespace}::${ObjectInfoDirectoryEntryRepresentationType}:`;
|
|
17166
17258
|
// We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
|
|
17167
17259
|
const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
|
|
17168
17260
|
class SqlitePrimingStore {
|
|
@@ -17227,44 +17319,6 @@ class SqlitePrimingStore {
|
|
|
17227
17319
|
};
|
|
17228
17320
|
}
|
|
17229
17321
|
}
|
|
17230
|
-
async readObjectInfoDirectory() {
|
|
17231
|
-
const sql = 'SELECT data FROM lds_data WHERE key = ?';
|
|
17232
|
-
const params = [ObjectInfoDirectoryKey];
|
|
17233
|
-
const result = await this.store.query(sql, params);
|
|
17234
|
-
if (result.rows.length === 1) {
|
|
17235
|
-
return JSON.parse(result.rows[0][0]);
|
|
17236
|
-
}
|
|
17237
|
-
return undefined;
|
|
17238
|
-
}
|
|
17239
|
-
async readObjectApiNames() {
|
|
17240
|
-
const sql = 'SELECT key FROM lds_data WHERE key like ?';
|
|
17241
|
-
const params = [`%${ObjectInfoRepresentationType}%`];
|
|
17242
|
-
const result = await this.store.query(sql, params);
|
|
17243
|
-
const apiNames = new Set();
|
|
17244
|
-
result.rows.forEach((row) => {
|
|
17245
|
-
const key = row[0];
|
|
17246
|
-
const parts = key.split(':');
|
|
17247
|
-
apiNames.add(parts[parts.length - 1]);
|
|
17248
|
-
});
|
|
17249
|
-
return apiNames;
|
|
17250
|
-
}
|
|
17251
|
-
writeObjectInfoDirectory(directory) {
|
|
17252
|
-
const sql = 'INSERT or IGNORE into lds_data (key, data) values (?, ?)';
|
|
17253
|
-
const params = [ObjectInfoDirectoryKey, JSON.stringify(directory)];
|
|
17254
|
-
return this.store.query(sql, params).then(() => { });
|
|
17255
|
-
}
|
|
17256
|
-
writeObjectInfos(objectInfos) {
|
|
17257
|
-
const sql = `INSERT or IGNORE into lds_data (key, data) values ${objectInfos
|
|
17258
|
-
.map(() => '(?, ?)')
|
|
17259
|
-
.join(',')};`;
|
|
17260
|
-
const params = [];
|
|
17261
|
-
objectInfos.forEach((objectInfo) => {
|
|
17262
|
-
const key = keyBuilderObjectInfo(this.getLuvio(), { apiName: objectInfo.apiName });
|
|
17263
|
-
params.push(key);
|
|
17264
|
-
params.push(JSON.stringify(objectInfo));
|
|
17265
|
-
});
|
|
17266
|
-
return this.store.query(sql, params).then(() => { });
|
|
17267
|
-
}
|
|
17268
17322
|
}
|
|
17269
17323
|
function batchArray(arr, batchSize = BATCH_SIZE) {
|
|
17270
17324
|
const batches = [];
|
|
@@ -17339,9 +17393,7 @@ function primingSessionFactory(config) {
|
|
|
17339
17393
|
recordLoader,
|
|
17340
17394
|
recordIngestor,
|
|
17341
17395
|
store: primingStore,
|
|
17342
|
-
objectInfoLoader:
|
|
17343
|
-
getObjectInfos: objectInfoService.getObjectInfos.bind(objectInfoService),
|
|
17344
|
-
},
|
|
17396
|
+
objectInfoLoader: objectInfoService,
|
|
17345
17397
|
concurrency: config.concurrency,
|
|
17346
17398
|
batchSize: config.batchSize,
|
|
17347
17399
|
ldsRecordRefresher: new LdsPrimingRecordRefresher(config.getRecords),
|
|
@@ -17507,4 +17559,4 @@ register({
|
|
|
17507
17559
|
});
|
|
17508
17560
|
|
|
17509
17561
|
export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
|
|
17510
|
-
// version: 1.
|
|
17562
|
+
// version: 1.250.0-9df9bc3d1
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { PrimingStore, RecordWithMetadata, WriteResult } from '@salesforce/lds-priming';
|
|
2
2
|
import type { Luvio } from '@luvio/engine';
|
|
3
|
-
import type { ObjectInfoDirectoryRepresentation, ObjectInfoRepresentation } from '@salesforce/lds-adapters-uiapi';
|
|
4
3
|
import type { SqliteStore } from '@salesforce/lds-store-sql';
|
|
5
4
|
export declare class SqlitePrimingStore implements PrimingStore {
|
|
6
5
|
private readonly getLuvio;
|
|
@@ -9,8 +8,4 @@ export declare class SqlitePrimingStore implements PrimingStore {
|
|
|
9
8
|
readRecords(ids: string[]): Promise<RecordWithMetadata[]>;
|
|
10
9
|
writeRecords(records: RecordWithMetadata[], overwrite: boolean): Promise<WriteResult>;
|
|
11
10
|
private writeBatch;
|
|
12
|
-
readObjectInfoDirectory(): Promise<ObjectInfoDirectoryRepresentation | undefined>;
|
|
13
|
-
readObjectApiNames(): Promise<Set<string>>;
|
|
14
|
-
writeObjectInfoDirectory(directory: ObjectInfoDirectoryRepresentation): Promise<void>;
|
|
15
|
-
writeObjectInfos(objectInfos: ObjectInfoRepresentation[]): Promise<void>;
|
|
16
11
|
}
|
|
@@ -10,6 +10,9 @@ type ObjectInfoDirectoryConfig = Parameters<ObjectInfoDirectoryAdapterReturn>[0]
|
|
|
10
10
|
export type ObjectInfoMap = {
|
|
11
11
|
[apiName: string]: ObjectInfoRepresentation;
|
|
12
12
|
};
|
|
13
|
+
interface ObjectInfoStatus {
|
|
14
|
+
expiration: number;
|
|
15
|
+
}
|
|
13
16
|
export declare class ObjectInfoService {
|
|
14
17
|
private getObjectInfoAdapter;
|
|
15
18
|
private getObjectInfosAdapter;
|
|
@@ -31,8 +34,10 @@ export declare class ObjectInfoService {
|
|
|
31
34
|
getObjectInfos(apiNames: string[]): Promise<ObjectInfoMap>;
|
|
32
35
|
getObjectInfoDirectory(): Promise<ObjectInfoDirectoryRepresentation | undefined>;
|
|
33
36
|
ensureObjectInfoCached: (apiName: string, entry?: ObjectInfoRepresentation) => Promise<void>;
|
|
37
|
+
getCachedObjectInfoStatus: () => Promise<Map<string, ObjectInfoStatus>>;
|
|
34
38
|
private isObjectInfoInDurableStore;
|
|
35
39
|
private loadObjectInfoMaps;
|
|
40
|
+
private readObjectInfoDataFromDurableStore;
|
|
36
41
|
private updateObjectInfoMapping;
|
|
37
42
|
}
|
|
38
43
|
export {};
|