@salesforce/lds-runtime-mobile 1.247.0 → 1.249.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 CHANGED
@@ -15,8 +15,9 @@ import { withRegistration, register } from '@salesforce/lds-default-luvio';
15
15
  import { setupInstrumentation, instrumentAdapter as instrumentAdapter$1, instrumentLuvio, setLdsAdaptersUiapiInstrumentation, setLdsNetworkAdapterInstrumentation } from '@salesforce/lds-instrumentation';
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
- import { parseAndVisit, Kind, visit, execute, buildSchema, isObjectType, defaultFieldResolver } 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, keyBuilderObjectInfo, ObjectInfoDirectoryEntryRepresentationType, getRecordsAdapterFactory } from '@salesforce/lds-adapters-uiapi';
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, 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();
@@ -4950,6 +4951,21 @@ class AsyncWorkerPool {
4950
4951
  }
4951
4952
  }
4952
4953
 
4954
+ /**
4955
+ Use Math.random to generate v4 RFC4122 compliant uuid
4956
+ */
4957
+ function uuidv4() {
4958
+ const uuid = [];
4959
+ for (let i = 0; i < 32; i++) {
4960
+ const random = (Math.random() * 16) | 0;
4961
+ if (i === 8 || i === 12 || i === 16 || i === 20) {
4962
+ uuid.push('-');
4963
+ }
4964
+ uuid.push((i === 12 ? 4 : i === 16 ? (random & 3) | 8 : random).toString(16));
4965
+ }
4966
+ return uuid.join('');
4967
+ }
4968
+
4953
4969
  /**
4954
4970
  * Copyright (c) 2022, Salesforce, Inc.,
4955
4971
  * All rights reserved.
@@ -5163,20 +5179,6 @@ function generateUniqueDraftActionId(existingIds) {
5163
5179
  }
5164
5180
  return newId.toString();
5165
5181
  }
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
5182
 
5181
5183
  const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
5182
5184
  const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
@@ -6053,7 +6055,12 @@ class AbstractResourceRequestActionHandler {
6053
6055
  // the luvio store redirect table, during which a new draft might be enqueued
6054
6056
  // which would not see a necessary mapping.
6055
6057
  this.ephemeralRedirects = {};
6058
+ // determined by Server setup.
6056
6059
  this.isIdempotencySupported = true;
6060
+ // idempotency write flag set by lds
6061
+ this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
6062
+ fallback: false,
6063
+ });
6057
6064
  }
6058
6065
  enqueue(data) {
6059
6066
  return this.draftQueue.enqueue(this.handlerId, data);
@@ -6410,7 +6417,7 @@ class AbstractResourceRequestActionHandler {
6410
6417
  return [action.targetId];
6411
6418
  }
6412
6419
  hasIdempotencySupport() {
6413
- return this.isIdempotencySupported;
6420
+ return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
6414
6421
  }
6415
6422
  async ingestResponses(responses, action) {
6416
6423
  const luvio = this.getLuvio();
@@ -6948,14 +6955,20 @@ function buildQueryTypeStringKey(args) {
6948
6955
  */
6949
6956
 
6950
6957
 
6958
+ const MAX_BATCH_SIZE = 2000;
6951
6959
  class DataLoader {
6952
- constructor(batchLoadFn) {
6960
+ constructor(batchLoadFn, options) {
6953
6961
  this._batchLoadFn = batchLoadFn;
6954
6962
  this._batch = null;
6955
6963
  this._batchScheduleFn = function (fn) {
6956
6964
  setTimeout(fn, 0);
6957
6965
  };
6958
6966
  this._cacheMap = new Map();
6967
+ this._maxBatchSize = MAX_BATCH_SIZE;
6968
+ if (options !== undefined) {
6969
+ const { maxBatchSize } = options;
6970
+ this._maxBatchSize = maxBatchSize || MAX_BATCH_SIZE;
6971
+ }
6959
6972
  }
6960
6973
  load(key) {
6961
6974
  if (key === null || key === undefined) {
@@ -6985,7 +6998,9 @@ class DataLoader {
6985
6998
  // If there is an existing batch which has not yet dispatched and is within
6986
6999
  // the limit of the batch size, then return it.
6987
7000
  const existingBatch = this._batch;
6988
- if (existingBatch !== null && !existingBatch.hasDispatched && !existingBatch.cacheHits) {
7001
+ if (existingBatch !== null &&
7002
+ !existingBatch.hasDispatched &&
7003
+ existingBatch.keys.length < this._maxBatchSize) {
6989
7004
  return existingBatch;
6990
7005
  }
6991
7006
  // Otherwise, create a new batch for this loader.
@@ -7077,7 +7092,7 @@ function isArrayLike(x) {
7077
7092
 
7078
7093
  const { create: create$4, keys: keys$4, values: values$2, entries: entries$3, assign: assign$4 } = Object;
7079
7094
  const { stringify: stringify$4, parse: parse$4 } = JSON;
7080
- const { isArray: isArray$2 } = Array;
7095
+ const { isArray: isArray$2, from: from$1 } = Array;
7081
7096
 
7082
7097
  function recordLoaderFactory(query) {
7083
7098
  async function batchRecordQuery(ids) {
@@ -9308,25 +9323,6 @@ const additionalSchemaDefinitions = /* GraphQL */ `
9308
9323
  }
9309
9324
  `;
9310
9325
  const baseTypeDefinitions = uiapiSchemaString + additionalSchemaDefinitions;
9311
- /**
9312
- *
9313
- * @param objectInfos
9314
- * @returns Type definition string and entity type names which support polymorphism.
9315
- */
9316
- function generateTypeDefinitions(objectInfos) {
9317
- if (keys$4(objectInfos).length === 0)
9318
- return { typeDefs: baseTypeDefinitions, polyFieldTypeNames: [] };
9319
- const { recordQueries, recordConnections, polyFieldTypeNameArr } = generateRecordQueries(objectInfos);
9320
- const typeDefs = `
9321
- ${baseTypeDefinitions}
9322
-
9323
- extend type RecordQuery {
9324
- ${recordQueries}
9325
- }
9326
- ${recordConnections}
9327
- `;
9328
- return { typeDefs, polyFieldTypeNames: polyFieldTypeNameArr };
9329
- }
9330
9326
  const fieldsStaticallyAdded = [
9331
9327
  'ApiName',
9332
9328
  'DisplayValue',
@@ -9336,99 +9332,347 @@ const fieldsStaticallyAdded = [
9336
9332
  'SystemModstamp',
9337
9333
  'WeakEtag',
9338
9334
  ];
9339
- function generateRecordQueries(objectInfos) {
9335
+ class CachedGraphQLSchema {
9336
+ constructor() {
9337
+ this._schema = buildBaseSchema();
9338
+ this._polymorphicFieldTypeNames = [];
9339
+ }
9340
+ getSchema() {
9341
+ return this._schema;
9342
+ }
9343
+ setSchema(value) {
9344
+ this._schema = value;
9345
+ }
9346
+ getPolymorphicFieldTypeNames() {
9347
+ return this._polymorphicFieldTypeNames;
9348
+ }
9349
+ setPolymorphicFieldTypeNames(value) {
9350
+ this._polymorphicFieldTypeNames = value;
9351
+ }
9352
+ set(schema, polymorphicFieldTypeNames) {
9353
+ this._schema = schema;
9354
+ this._polymorphicFieldTypeNames = polymorphicFieldTypeNames;
9355
+ }
9356
+ }
9357
+ /**
9358
+ * Looks at the injected object info map and checks to see if the existing objects
9359
+ * are within the current schema. It will extend the cached schema if it is not included.
9360
+ * @param objectInfos
9361
+ * @param cache
9362
+ * @returns GraphQLSchema
9363
+ */
9364
+ function createSchemaWithCache(objectInfos, cache) {
9365
+ const updatedCache = extendSchemaWithObjectInfos(cache, objectInfos);
9366
+ // set the new values to the passed cached schema & polymorphic field names
9367
+ cache.set(updatedCache.getSchema(), updatedCache.getPolymorphicFieldTypeNames());
9368
+ return cache.getSchema();
9369
+ }
9370
+ /**
9371
+ * Extends the current GraphQL Schema with new types based on the given object info map
9372
+ *
9373
+ * @param cache the existing cached schema object
9374
+ * @param objectInfoMap map of object info and apiname for key
9375
+ * @returns CachedGraphQLSchema
9376
+ */
9377
+ function extendSchemaWithObjectInfos(cache, objectInfoMap) {
9378
+ const { recordQueries, recordConnections, recordExtensions, polyFieldTypeNameArr } = generateRecordQueries(cache.getSchema(), objectInfoMap);
9379
+ const typeDefs = `
9380
+ ${recordQueries}
9381
+ ${recordConnections}
9382
+ ${recordExtensions}
9383
+ `;
9384
+ // if nothing new is added then return the current cache
9385
+ if (typeDefs.trim().length === 0) {
9386
+ return cache;
9387
+ }
9388
+ // parse extensions into DocumentNode to extend the schema
9389
+ const extensions = parse$7(typeDefs);
9390
+ const polymorphicFieldTypeNames = [
9391
+ ...polyFieldTypeNameArr,
9392
+ ...cache.getPolymorphicFieldTypeNames(),
9393
+ ];
9394
+ // extend the schema and add resolvers
9395
+ const schema = addResolversToSchema(extendSchema(cache.getSchema(), extensions), polymorphicFieldTypeNames);
9396
+ const polymorphicFieldTypeNamesSet = new Set(polymorphicFieldTypeNames);
9397
+ cache.setSchema(schema);
9398
+ cache.setPolymorphicFieldTypeNames([...polymorphicFieldTypeNamesSet]);
9399
+ return cache;
9400
+ }
9401
+ /**
9402
+ * Builds the base schema from uiapi graphql adapter with resolvers attached
9403
+ * @returns GraphQLSchema
9404
+ */
9405
+ function buildBaseSchema() {
9406
+ return addResolversToSchema(buildSchema(baseTypeDefinitions), []);
9407
+ }
9408
+ /**
9409
+ * Given the existing schema and the object infos it will create a new type for the schema
9410
+ * or extend an existing type to add new fields to it.
9411
+ *
9412
+ * Extends RecordQuery to add new top level queries
9413
+ * extend type RecordQuery {
9414
+ * Account(predicates): AccountConnection
9415
+ * }
9416
+ * @param schema
9417
+ * @param objectInfoMap
9418
+ * @returns
9419
+ */
9420
+ function generateRecordQueries(schema, objectInfoMap) {
9340
9421
  let recordQueries = ``;
9341
9422
  let recordConnections = ``;
9342
- const polymorphicFieldTypeNames = new Set();
9423
+ let recordExtensions = ``;
9424
+ // use a set to not allow duplicate scalars causing error(s)
9425
+ let addedTypedScalars = new Set();
9426
+ let allPolymorphicFieldTypeNames = new Set();
9427
+ for (const name of keys$4(objectInfoMap)) {
9428
+ const objectInfo = objectInfoMap[name];
9429
+ const { apiName } = objectInfo;
9430
+ const type = schema.getType(apiName);
9431
+ // if type is an ObjectType it exists in the schema
9432
+ if (isObjectType(type)) {
9433
+ const { recordExtension, typedScalars, polymorphicFieldTypeNames } = extendExistingRecordType(schema, type, objectInfo, objectInfoMap);
9434
+ recordExtensions += recordExtension;
9435
+ addedTypedScalars = new Set([...addedTypedScalars, ...typedScalars]);
9436
+ allPolymorphicFieldTypeNames = new Set([
9437
+ ...allPolymorphicFieldTypeNames,
9438
+ ...polymorphicFieldTypeNames,
9439
+ ]);
9440
+ }
9441
+ else {
9442
+ const { recordQueries: newRecordQueries, recordConnections: newRecordConnections, typedScalars, polymorphicFieldTypeNames, } = createNewRecordQuery(schema, objectInfo, objectInfoMap);
9443
+ recordQueries += newRecordQueries;
9444
+ recordConnections += newRecordConnections;
9445
+ addedTypedScalars = new Set([...addedTypedScalars, ...typedScalars]);
9446
+ allPolymorphicFieldTypeNames = new Set([
9447
+ ...allPolymorphicFieldTypeNames,
9448
+ ...polymorphicFieldTypeNames,
9449
+ ]);
9450
+ }
9451
+ }
9452
+ // transform added scalar types into a list of scalars in string
9453
+ const scalars = [...addedTypedScalars].map((scalar) => `scalar ${scalar}`).join('\n');
9454
+ recordConnections += scalars;
9455
+ // return empty string if no recordQueries were extended
9456
+ const extensionWrapper = recordQueries.length > 0
9457
+ ? `
9458
+ extend type RecordQuery {
9459
+ ${recordQueries}
9460
+ }`
9461
+ : '';
9462
+ return {
9463
+ recordQueries: extensionWrapper,
9464
+ recordConnections,
9465
+ recordExtensions,
9466
+ polyFieldTypeNameArr: from$1(allPolymorphicFieldTypeNames),
9467
+ };
9468
+ }
9469
+ /**
9470
+ * Will create a new record query extension for something that does not already exist in the schema
9471
+ *
9472
+ * generates:
9473
+ *
9474
+ * type {typename} implements Record {
9475
+ * ...scalar fields
9476
+ * Id: IDValue
9477
+ * ...spanning parent records
9478
+ * User: Parent
9479
+ * ...spanning children queries
9480
+ * Accounts(predicates): AccountConnection
9481
+ * }
9482
+ *
9483
+ * scalars SomeAddedScalar
9484
+ *
9485
+ * @param schema
9486
+ * @param objectInfo
9487
+ * @param objectInfoMap
9488
+ * @returns
9489
+ */
9490
+ function createNewRecordQuery(schema, objectInfo, objectInfoMap) {
9343
9491
  let typedScalars = new Set();
9344
9492
  let parentRelationshipFields = new Set();
9345
- for (const objectInfo of values$2(objectInfos)) {
9346
- const { apiName, childRelationships } = objectInfo;
9347
- let fields = ``;
9348
- typedScalars.add(`${apiName}_Filter`);
9349
- typedScalars.add(`${apiName}_OrderBy`);
9350
- for (const field of values$2(objectInfo.fields)) {
9351
- if (!fieldsStaticallyAdded.includes(field.apiName)) {
9352
- fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
9353
- }
9354
- //handles parent relationship
9355
- if (field.relationshipName === null) {
9356
- continue;
9493
+ const { apiName, childRelationships, fields: fieldsRepresentation } = objectInfo;
9494
+ typedScalars.add(`${apiName}_Filter`);
9495
+ typedScalars.add(`${apiName}_OrderBy`);
9496
+ const { fields, polymorphicFieldTypeNames } = makeRecordField(values$2(fieldsRepresentation), objectInfoMap, parentRelationshipFields, 'Missing');
9497
+ // handles child relationship
9498
+ const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields);
9499
+ typedScalars = new Set([...typedScalars, ...spanningConnectionTypedScalars]);
9500
+ const recordQueries = `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
9501
+ const isServiceAppointment = apiName === 'ServiceAppointment';
9502
+ const recordConnections = /* GraphQL */ `
9503
+ ${isServiceAppointment ? `scalar ${apiName.toUpperCase()}_SCOPE` : ''}
9504
+
9505
+ type ${apiName} implements Record {
9506
+ ApiName: String!
9507
+ DisplayValue: String
9508
+ LastModifiedById: IDValue
9509
+ LastModifiedDate: DateTimeValue
9510
+ RecordTypeId(fallback: Boolean): IDValue
9511
+ SystemModstamp: DateTimeValue
9512
+ WeakEtag: Long!
9513
+ _drafts: JSON
9514
+ ${fields}
9515
+ ${spanningRecordConnections}
9357
9516
  }
9358
- // For spanning parent relationships with no union types
9359
- if (field.referenceToInfos.length === 1) {
9360
- const [relation] = field.referenceToInfos;
9361
- // Only add the relationship if there is relevant objectinfos for it,
9362
- // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9363
- // the query.
9364
- if (objectInfos[relation.apiName] !== undefined) {
9365
- parentRelationshipFields.add(field.relationshipName);
9366
- fields += `${field.relationshipName}: ${relation.apiName}\n`;
9367
- }
9368
- // For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
9517
+
9518
+ type ${apiName}Connection {
9519
+ edges: [${apiName}Edge]
9520
+ pageInfo: PageInfo!
9521
+ totalCount: Int!
9369
9522
  }
9370
- else if (field.referenceToInfos.length > 1) {
9371
- parentRelationshipFields.add(field.relationshipName);
9372
- fields += `${field.relationshipName}: Record\n`;
9373
- for (const relation of field.referenceToInfos) {
9374
- if (objectInfos[relation.apiName] !== undefined) {
9375
- polymorphicFieldTypeNames.add(relation.apiName);
9376
- }
9377
- }
9523
+
9524
+ type ${apiName}Edge {
9525
+ node: ${apiName}
9526
+ cursor: String!
9378
9527
  }
9528
+
9529
+ `;
9530
+ return { recordQueries, recordConnections, typedScalars, polymorphicFieldTypeNames };
9531
+ }
9532
+ /**
9533
+ * Takes the current schema and will extend missing fields to the record.
9534
+ * Assume all scalar fields have already been added, but will extend spanning fields
9535
+ *
9536
+ * extend type Account {
9537
+ * ...spanning parent records
9538
+ * User: User
9539
+ * ...spanning children queries
9540
+ * Users(predicates): UserConnection
9541
+ * }
9542
+ *
9543
+ * @param schema
9544
+ * @param type
9545
+ * @param objectInfo
9546
+ * @param objectInfoMap
9547
+ * @returns
9548
+ */
9549
+ function extendExistingRecordType(schema, type, objectInfo, objectInfoMap) {
9550
+ // use a set to not allow duplicate scalars causing error(s)
9551
+ let typedScalars = new Set();
9552
+ let parentRelationshipFields = new Set();
9553
+ const existingFields = keys$4(type.getFields());
9554
+ const missingFields = values$2(objectInfo.fields).filter((field) => {
9555
+ return (existingFields.includes(field.apiName) === false ||
9556
+ (field.relationshipName !== null && field.referenceToInfos.length > 1));
9557
+ });
9558
+ const { fields, polymorphicFieldTypeNames } = makeRecordField(missingFields, objectInfoMap, parentRelationshipFields, 'Cached');
9559
+ const { apiName, childRelationships } = objectInfo;
9560
+ // handles child relationship
9561
+ const { spanningRecordConnections, typedScalars: spanningConnectionTypedScalars } = makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, parentRelationshipFields, existingFields);
9562
+ typedScalars = new Set([...typedScalars, ...spanningConnectionTypedScalars]);
9563
+ const hasExtensions = fields.length > 0 || spanningRecordConnections.length > 0;
9564
+ const recordExtension = hasExtensions
9565
+ ? `
9566
+ extend type ${apiName} {
9567
+ ${fields}
9568
+ ${spanningRecordConnections}
9569
+ }\n`
9570
+ : '';
9571
+ return { recordExtension, typedScalars, polymorphicFieldTypeNames };
9572
+ }
9573
+ /**
9574
+ * Converts child relationships into spanning record connections to be added to record types
9575
+ *
9576
+ * type Record {
9577
+ * Generates -> AccountConnection(first: Int, where: Account_Filter, etc...): AccountConnection
9578
+ * }
9579
+ *
9580
+ * will also generate typed scalars if they have not been added to the existing schema for the spanning connection
9581
+ * example: Account_Filter or Account_OrderBy scalars needed to be defined as predicate types
9582
+ * @param schema
9583
+ * @param childRelationships
9584
+ * @param objectInfoMap
9585
+ * @param existingParentRelationships
9586
+ * @param existingFields
9587
+ * @returns
9588
+ */
9589
+ function makeSpanningRecordConnections(schema, childRelationships, objectInfoMap, existingParentRelationships, existingFields = []) {
9590
+ let spanningRecordConnections = ``;
9591
+ let typedScalars = new Set();
9592
+ for (const childRelationship of childRelationships) {
9593
+ const { childObjectApiName, relationshipName } = childRelationship;
9594
+ // Only add the relationship if there is relevant objectinfos for it,
9595
+ // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9596
+ // the query.
9597
+ // If one field has both parent relationship and child relationship with the same name, the child relationship is ignored. This is how the server GQL has implemented as date of 08/07/2023
9598
+ if (existingFields.length > 0 && existingFields.includes(relationshipName)) {
9599
+ continue;
9379
9600
  }
9380
- // handles child relationship
9381
- for (const childRelationship of childRelationships) {
9382
- const { childObjectApiName } = childRelationship;
9601
+ if (objectInfoMap[childObjectApiName] !== undefined &&
9602
+ !existingParentRelationships.has(relationshipName)) {
9603
+ spanningRecordConnections += `${relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
9604
+ // if the record type has already been extended then these additional scalars have already been added
9605
+ // to add them again would throw an error
9606
+ const filterScalarType = schema.getType(`${childObjectApiName}_Filter`);
9607
+ if (isScalarType(filterScalarType) === false) {
9608
+ typedScalars.add(`${childObjectApiName}_Filter`);
9609
+ }
9610
+ const orderByScalarType = schema.getType(`${childObjectApiName}_OrderBy`);
9611
+ if (isScalarType(orderByScalarType) === false) {
9612
+ typedScalars.add(`${childObjectApiName}_OrderBy`);
9613
+ }
9614
+ }
9615
+ }
9616
+ return { spanningRecordConnections, typedScalars };
9617
+ }
9618
+ /**
9619
+ * Creates scalar and parent relationship fields for a record type
9620
+ *
9621
+ * type RecordName {
9622
+ * generates scalar -> Id: IDValue
9623
+ * generates relationship -> Account: Record
9624
+ * }
9625
+ *
9626
+ * can be used in a type definition or an extension
9627
+ * @param fieldRepresentations
9628
+ * @param objectInfoMap
9629
+ * @param existingParentRelationships
9630
+ * @param recordTypeInSchema
9631
+ * @returns
9632
+ */
9633
+ function makeRecordField(fieldRepresentations, objectInfoMap, existingParentRelationships, recordTypeInSchema) {
9634
+ const polymorphicFieldTypeNames = new Set();
9635
+ let fields = ``;
9636
+ for (const field of values$2(fieldRepresentations)) {
9637
+ if (!fieldsStaticallyAdded.includes(field.apiName) && recordTypeInSchema === 'Missing') {
9638
+ fields += `${field.apiName}: ${dataTypeToType(field.dataType, field.apiName)}\n`;
9639
+ }
9640
+ //handles parent relationship
9641
+ if (field.relationshipName === null) {
9642
+ continue;
9643
+ }
9644
+ // For spanning parent relationships with no union types
9645
+ if (field.referenceToInfos.length === 1) {
9646
+ const [relation] = field.referenceToInfos;
9383
9647
  // Only add the relationship if there is relevant objectinfos for it,
9384
9648
  // otherwise we'd be defining types we cannot satisfy and aren't referenced in
9385
9649
  // the query.
9386
- // If one field has both parent relationship and child relationship with the same name, the child relationship is ignored. This is how the server GQL has implemented as date of 08/07/2023
9387
- if (objectInfos[childObjectApiName] !== undefined &&
9388
- !parentRelationshipFields.has(childRelationship.relationshipName)) {
9389
- fields += `${childRelationship.relationshipName}(first: Int, where: ${childObjectApiName}_Filter, orderBy: ${childObjectApiName}_OrderBy, scope: SupportedScopes): ${childObjectApiName}Connection \n`;
9390
- typedScalars.add(`${childObjectApiName}_Filter`);
9391
- typedScalars.add(`${childObjectApiName}_OrderBy`);
9650
+ if (objectInfoMap[relation.apiName] !== undefined) {
9651
+ existingParentRelationships.add(field.relationshipName);
9652
+ fields += `${field.relationshipName}: ${relation.apiName}\n`;
9392
9653
  }
9654
+ // For polymorphic field, its type is 'Record' inteface. The concrete entity type name is saved for field resolving of next phase
9393
9655
  }
9394
- recordQueries += `${apiName}(first: Int, where: ${apiName}_Filter, orderBy: ${apiName}_OrderBy, scope: SupportedScopes): ${apiName}Connection\n`;
9395
- const isServiceAppointment = apiName === 'ServiceAppointment';
9396
- recordConnections += /* GraphQL */ `
9397
- ${isServiceAppointment ? `scalar ${apiName.toUpperCase()}_SCOPE` : ''}
9398
-
9399
- type ${apiName} implements Record {
9400
- ApiName: String!
9401
- DisplayValue: String
9402
- LastModifiedById: IDValue
9403
- LastModifiedDate: DateTimeValue
9404
- RecordTypeId(fallback: Boolean): IDValue
9405
- SystemModstamp: DateTimeValue
9406
- WeakEtag: Long!
9407
- _drafts: JSON
9408
- ${fields}
9409
- }
9410
-
9411
- type ${apiName}Connection {
9412
- edges: [${apiName}Edge]
9413
- pageInfo: PageInfo!
9414
- totalCount: Int!
9415
- }
9416
-
9417
- type ${apiName}Edge {
9418
- node: ${apiName}
9419
- cursor: String!
9656
+ else if (field.referenceToInfos.length > 1) {
9657
+ if (recordTypeInSchema === 'Missing') {
9658
+ existingParentRelationships.add(field.relationshipName);
9659
+ fields += `${field.relationshipName}: Record\n`;
9660
+ }
9661
+ for (const relation of field.referenceToInfos) {
9662
+ if (objectInfoMap[relation.apiName] !== undefined) {
9663
+ polymorphicFieldTypeNames.add(relation.apiName);
9664
+ }
9665
+ }
9420
9666
  }
9421
-
9422
- `;
9423
9667
  }
9424
- const scalars = [...typedScalars].reduce((accu, typed) => {
9425
- return accu + `scalar ${typed}\n`;
9426
- }, ``);
9427
- recordConnections += scalars;
9428
- const polyFieldTypeNameArr = [];
9429
- polymorphicFieldTypeNames.forEach((fieldType) => polyFieldTypeNameArr.push(fieldType));
9430
- return { recordQueries, recordConnections, polyFieldTypeNameArr };
9668
+ return { fields, polymorphicFieldTypeNames };
9431
9669
  }
9670
+ /**
9671
+ * converts the ObjectInfoRepresentation data type into a defined schema scalar type
9672
+ * @param objectInfoDataType
9673
+ * @param apiName
9674
+ * @returns
9675
+ */
9432
9676
  function dataTypeToType(objectInfoDataType, apiName) {
9433
9677
  if (apiName && apiName === 'Id') {
9434
9678
  return `ID!`;
@@ -9470,13 +9714,8 @@ function dataTypeToType(objectInfoDataType, apiName) {
9470
9714
  return 'String';
9471
9715
  }
9472
9716
  }
9473
- function createSchema(objectInfos) {
9474
- const { typeDefs: typeDefinitions, polyFieldTypeNames } = generateTypeDefinitions(objectInfos);
9475
- const schema = buildSchema(typeDefinitions);
9476
- return addResolversToSchema(schema, polyFieldTypeNames);
9477
- }
9478
9717
 
9479
- async function evaluate(config, observers, settings, objectInfos, store, snapshot, draftFunctions) {
9718
+ async function evaluate(config, observers, settings, objectInfos, store, snapshot, cache, draftFunctions) {
9480
9719
  const eventEmitter = createCustomAdapterEventEmitter(GRAPHQL_EVAL_NAMESPACE, observers);
9481
9720
  // this is only wrapped in a try to execute the event after the result was returned
9482
9721
  try {
@@ -9517,7 +9756,7 @@ async function evaluate(config, observers, settings, objectInfos, store, snapsho
9517
9756
  const contextValue = createContext(store, objectInfos, eventEmitter, settings, snapshot, draftFunctions);
9518
9757
  // We're building this from scratch from each request. If this becomes a
9519
9758
  // hotspot we can pull it up and memoize it later
9520
- const schema = createSchema(objectInfos);
9759
+ const schema = createSchemaWithCache(objectInfos, cache);
9521
9760
  eventEmitter({ type: 'graphql-schema-created' });
9522
9761
  // execute document against schema/context
9523
9762
  let result = (await execute({
@@ -11867,34 +12106,42 @@ function applyReferenceLinksToDraft(record, draftMetadata) {
11867
12106
  }
11868
12107
  const { dataType, relationshipName, referenceToInfos } = fieldInfo;
11869
12108
  const draftFieldValue = record.fields[draftField].value;
11870
- if (dataType === 'Reference' && relationshipName !== null && draftFieldValue !== null) {
11871
- if (typeof draftFieldValue !== 'string') {
11872
- throw Error('reference field value is not a string');
12109
+ if (dataType === 'Reference' && relationshipName !== null) {
12110
+ if (draftFieldValue === null) {
12111
+ recordFields[relationshipName] = {
12112
+ displayValue: null,
12113
+ value: null,
12114
+ };
11873
12115
  }
11874
- const key = getRecordKeyForId(luvio, draftFieldValue);
11875
- const referencedRecord = referencedRecords.get(key);
11876
- recordFields[relationshipName] = {
11877
- displayValue: null,
11878
- value: createLink(key),
11879
- };
11880
- // for custom objects, we select the 'Name' field
11881
- // otherwise we check the object info for name fields.
11882
- //if there are multiple we select 'Name' if it exists, otherwise the first one
11883
- if (referencedRecord !== undefined && referenceToInfos.length > 0) {
11884
- let nameField;
11885
- const referenceToInfo = referenceToInfos[0];
11886
- const nameFields = referenceToInfo.nameFields;
11887
- if (nameFields.length !== 0) {
11888
- nameField = nameFields.find((x) => x === 'Name');
11889
- if (nameField === undefined) {
11890
- nameField = nameFields[0];
11891
- }
12116
+ else {
12117
+ if (typeof draftFieldValue !== 'string') {
12118
+ throw Error('reference field value is not a string');
11892
12119
  }
11893
- if (nameField !== undefined) {
11894
- const nameFieldRef = referencedRecord.fields[nameField];
11895
- if (nameFieldRef) {
11896
- recordFields[relationshipName].displayValue =
11897
- (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
12120
+ const key = getRecordKeyForId(luvio, draftFieldValue);
12121
+ const referencedRecord = referencedRecords.get(key);
12122
+ recordFields[relationshipName] = {
12123
+ displayValue: null,
12124
+ value: createLink(key),
12125
+ };
12126
+ // for custom objects, we select the 'Name' field
12127
+ // otherwise we check the object info for name fields.
12128
+ //if there are multiple we select 'Name' if it exists, otherwise the first one
12129
+ if (referencedRecord !== undefined && referenceToInfos.length > 0) {
12130
+ let nameField;
12131
+ const referenceToInfo = referenceToInfos[0];
12132
+ const nameFields = referenceToInfo.nameFields;
12133
+ if (nameFields.length !== 0) {
12134
+ nameField = nameFields.find((x) => x === 'Name');
12135
+ if (nameField === undefined) {
12136
+ nameField = nameFields[0];
12137
+ }
12138
+ }
12139
+ if (nameField !== undefined) {
12140
+ const nameFieldRef = referencedRecord.fields[nameField];
12141
+ if (nameFieldRef) {
12142
+ recordFields[relationshipName].displayValue =
12143
+ (_a = nameFieldRef.displayValue) !== null && _a !== void 0 ? _a : nameFieldRef.value;
12144
+ }
11898
12145
  }
11899
12146
  }
11900
12147
  }
@@ -12187,17 +12434,8 @@ class UiApiActionHandler extends AbstractResourceRequestActionHandler {
12187
12434
  };
12188
12435
  for (const fieldName of keys$3(recordWithSpanningRefLinks.fields)) {
12189
12436
  const fieldKey = buildRecordFieldStoreKey(key, fieldName);
12190
- if (this.collectedFields[fieldKey] !== undefined) {
12191
- const fieldData = recordWithSpanningRefLinks.fields[fieldName];
12192
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
12193
- publishData(fieldKey, fieldData);
12194
- }
12195
- else if (recordWithSpanningRefLinks.fields[fieldName] &&
12196
- recordWithSpanningRefLinks.fields[fieldName].value &&
12197
- recordWithSpanningRefLinks.fields[fieldName].value.__ref !== undefined) {
12198
- normalizedRecord.fields[fieldName] = { __ref: fieldKey };
12199
- publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
12200
- }
12437
+ normalizedRecord.fields[fieldName] = { __ref: fieldKey };
12438
+ publishData(fieldKey, recordWithSpanningRefLinks.fields[fieldName]);
12201
12439
  }
12202
12440
  // publish the normalized record
12203
12441
  publishData(key, normalizedRecord);
@@ -13012,6 +13250,8 @@ const replaceDraftIdsInVariables = (variables, draftFunctions, unmappedDraftIDs)
13012
13250
  }, {});
13013
13251
  return newVariables;
13014
13252
  };
13253
+ // create the runtime cache for the graphql schema when the factory creates the adapter
13254
+ const graphqlSchemaCache = new CachedGraphQLSchema();
13015
13255
  function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio, isDraftId) {
13016
13256
  const getCanonicalId = (id) => {
13017
13257
  var _a;
@@ -13085,7 +13325,7 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
13085
13325
  ...config,
13086
13326
  //need to create another copy of the ast for future writes
13087
13327
  query: parse$3(stringify$3(injectedAST)),
13088
- }, observers, { userId }, objectInfoNeeded, store, nonEvaluatedSnapshot));
13328
+ }, observers, { userId }, objectInfoNeeded, store, nonEvaluatedSnapshot, graphqlSchemaCache));
13089
13329
  }
13090
13330
  catch (throwable) {
13091
13331
  const error = throwable;
@@ -13114,7 +13354,7 @@ function draftAwareGraphQLAdapterFactory(userId, objectInfoService, store, luvio
13114
13354
  let { result: rebuildResult, seenRecordIds } = await evaluate({
13115
13355
  ...config,
13116
13356
  query: injectedAST,
13117
- }, observers, { userId }, objectInfoNeeded, store, originalSnapshot, draftFunctions);
13357
+ }, observers, { userId }, objectInfoNeeded, store, originalSnapshot, graphqlSchemaCache, draftFunctions);
13118
13358
  if (!rebuildResult.errors) {
13119
13359
  rebuildResult = removeSyntheticFields(rebuildResult, config.query);
13120
13360
  }
@@ -15156,6 +15396,14 @@ class ObjectInfoService {
15156
15396
  return this.updateObjectInfoMapping(keyPrefix, apiName);
15157
15397
  }
15158
15398
  };
15399
+ this.getCachedObjectInfoStatus = async () => {
15400
+ const infos = await this.readObjectInfoDataFromDurableStore();
15401
+ const map = new Map();
15402
+ infos.forEach(({ apiName, expirationTimestamp }) => {
15403
+ map.set(apiName, { expiration: expirationTimestamp });
15404
+ });
15405
+ return map;
15406
+ };
15159
15407
  this.isObjectInfoInDurableStore = async (apiName) => {
15160
15408
  if (this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined) {
15161
15409
  return Promise.resolve(true);
@@ -15164,12 +15412,10 @@ class ObjectInfoService {
15164
15412
  return this.apiNameToKeyPrefixMemoryCache[apiName] !== undefined;
15165
15413
  };
15166
15414
  this.loadObjectInfoMaps = async () => {
15167
- const rows = (await this.durableStore.query(`SELECT json_extract(data, '$.apiName') as ApiName, json_extract(data, '$.keyPrefix') as keyPrefix from lds_data where key like '%ObjectInfoRepresentation%'`, [])).rows;
15168
- for (const row of rows) {
15169
- const apiName = row[0];
15170
- const keyPrefix = row[1];
15415
+ const infos = await this.readObjectInfoDataFromDurableStore();
15416
+ infos.forEach(({ keyPrefix, apiName }) => {
15171
15417
  this.updateObjectInfoMapping(keyPrefix, apiName);
15172
- }
15418
+ });
15173
15419
  };
15174
15420
  this.updateObjectInfoMapping = (keyPrefix, apiName) => {
15175
15421
  this.apiNameToKeyPrefixMemoryCache[apiName] = keyPrefix;
@@ -15213,6 +15459,24 @@ class ObjectInfoService {
15213
15459
  }
15214
15460
  return snapshot.data;
15215
15461
  }
15462
+ async readObjectInfoDataFromDurableStore() {
15463
+ const rows = (await this.durableStore.query(`
15464
+ SELECT
15465
+ json_extract(data, '$.apiName') as ApiName,
15466
+ json_extract(data, '$.keyPrefix') as keyPrefix,
15467
+ JSON_EXTRACT(metadata, '$.expirationTimestamp') AS expirationTimestamp
15468
+ from
15469
+ lds_data
15470
+ where
15471
+ key like '%ObjectInfoRepresentation%'`, [])).rows;
15472
+ return rows.map((row) => {
15473
+ return {
15474
+ apiName: row[0],
15475
+ keyPrefix: row[1],
15476
+ expirationTimestamp: row[2],
15477
+ };
15478
+ });
15479
+ }
15216
15480
  }
15217
15481
 
15218
15482
  function instrumentGraphQLEval(adapter) {
@@ -16311,7 +16575,7 @@ class ConflictPool {
16311
16575
  }
16312
16576
  }
16313
16577
 
16314
- const DEFAULT_BATCH_SIZE = 500;
16578
+ const DEFAULT_BATCH_SIZE$1 = 500;
16315
16579
  const DEFAULT_CONCURRENCY = 6;
16316
16580
  const DEFAULT_GQL_QUERY_BATCH_SIZE = 5;
16317
16581
  class PrimingSession extends EventEmitter {
@@ -16319,7 +16583,7 @@ class PrimingSession extends EventEmitter {
16319
16583
  var _a, _b;
16320
16584
  super();
16321
16585
  this.useBatchGQL = false;
16322
- this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE;
16586
+ this.batchSize = (_a = config.batchSize) !== null && _a !== void 0 ? _a : DEFAULT_BATCH_SIZE$1;
16323
16587
  this.concurrency = (_b = config.concurrency) !== null && _b !== void 0 ? _b : DEFAULT_CONCURRENCY;
16324
16588
  this.recordLoader = config.recordLoader;
16325
16589
  this.recordIngestor = config.recordIngestor;
@@ -16940,10 +17204,6 @@ class NimbusPrimingNetworkAdapter {
16940
17204
  // ref: https://gnome.pages.gitlab.gnome.org/tracker/docs/developer/limits.html?gi-language=c
16941
17205
  const SQLITE_MAX_VARIABLE_NUMBER = 999;
16942
17206
  const PARAMS_PER_RECORD = 3;
16943
- /**
16944
- * No key builder (or adapter) exists for the object info directory, we need to build the key manually
16945
- */
16946
- const ObjectInfoDirectoryKey = `${UiApiNamespace}::${ObjectInfoDirectoryEntryRepresentationType}:`;
16947
17207
  // We need to batch the records to avoid hitting the SQLITE_MAX_VARIABLE_NUMBER limit. Each record has 3 parameters
16948
17208
  const BATCH_SIZE = Math.floor(SQLITE_MAX_VARIABLE_NUMBER / PARAMS_PER_RECORD);
16949
17209
  class SqlitePrimingStore {
@@ -17008,44 +17268,6 @@ class SqlitePrimingStore {
17008
17268
  };
17009
17269
  }
17010
17270
  }
17011
- async readObjectInfoDirectory() {
17012
- const sql = 'SELECT data FROM lds_data WHERE key = ?';
17013
- const params = [ObjectInfoDirectoryKey];
17014
- const result = await this.store.query(sql, params);
17015
- if (result.rows.length === 1) {
17016
- return JSON.parse(result.rows[0][0]);
17017
- }
17018
- return undefined;
17019
- }
17020
- async readObjectApiNames() {
17021
- const sql = 'SELECT key FROM lds_data WHERE key like ?';
17022
- const params = [`%${ObjectInfoRepresentationType}%`];
17023
- const result = await this.store.query(sql, params);
17024
- const apiNames = new Set();
17025
- result.rows.forEach((row) => {
17026
- const key = row[0];
17027
- const parts = key.split(':');
17028
- apiNames.add(parts[parts.length - 1]);
17029
- });
17030
- return apiNames;
17031
- }
17032
- writeObjectInfoDirectory(directory) {
17033
- const sql = 'INSERT or IGNORE into lds_data (key, data) values (?, ?)';
17034
- const params = [ObjectInfoDirectoryKey, JSON.stringify(directory)];
17035
- return this.store.query(sql, params).then(() => { });
17036
- }
17037
- writeObjectInfos(objectInfos) {
17038
- const sql = `INSERT or IGNORE into lds_data (key, data) values ${objectInfos
17039
- .map(() => '(?, ?)')
17040
- .join(',')};`;
17041
- const params = [];
17042
- objectInfos.forEach((objectInfo) => {
17043
- const key = keyBuilderObjectInfo(this.getLuvio(), { apiName: objectInfo.apiName });
17044
- params.push(key);
17045
- params.push(JSON.stringify(objectInfo));
17046
- });
17047
- return this.store.query(sql, params).then(() => { });
17048
- }
17049
17271
  }
17050
17272
  function batchArray(arr, batchSize = BATCH_SIZE) {
17051
17273
  const batches = [];
@@ -17120,9 +17342,7 @@ function primingSessionFactory(config) {
17120
17342
  recordLoader,
17121
17343
  recordIngestor,
17122
17344
  store: primingStore,
17123
- objectInfoLoader: {
17124
- getObjectInfos: objectInfoService.getObjectInfos.bind(objectInfoService),
17125
- },
17345
+ objectInfoLoader: objectInfoService,
17126
17346
  concurrency: config.concurrency,
17127
17347
  batchSize: config.batchSize,
17128
17348
  ldsRecordRefresher: new LdsPrimingRecordRefresher(config.getRecords),
@@ -17288,4 +17508,4 @@ register({
17288
17508
  });
17289
17509
 
17290
17510
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
17291
- // version: 1.247.0-4fe38c091
17511
+ // version: 1.249.0-11c3e1ed5