@salesforce/lds-adapters-apex 0.1.0-dev1

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.
@@ -0,0 +1,642 @@
1
+ /**
2
+ * Copyright (c) 2022, Salesforce, Inc.,
3
+ * All rights reserved.
4
+ * For full license text, see the LICENSE.txt file
5
+ */
6
+
7
+ import { serializeStructuredKey, deepFreeze, StoreKeyMap, StoreKeySet } from '@luvio/engine';
8
+
9
+ const { keys: ObjectKeys, create: ObjectCreate } = Object;
10
+ const { stringify: JSONStringify } = JSON;
11
+ const { isArray: ArrayIsArray } = Array;
12
+ function untrustedIsObject(untrusted) {
13
+ return typeof untrusted === 'object' && untrusted !== null && ArrayIsArray(untrusted) === false;
14
+ }
15
+ const snapshotRefreshOptions = {
16
+ overrides: {
17
+ headers: {
18
+ 'Cache-Control': 'no-cache',
19
+ },
20
+ }
21
+ };
22
+ /**
23
+ * A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
24
+ * This is needed because insertion order for JSON.stringify(object) affects output:
25
+ * JSON.stringify({a: 1, b: 2})
26
+ * "{"a":1,"b":2}"
27
+ * JSON.stringify({b: 2, a: 1})
28
+ * "{"b":2,"a":1}"
29
+ * @param data Data to be JSON-stringified.
30
+ * @returns JSON.stringified value with consistent ordering of keys.
31
+ */
32
+ function stableJSONStringify$1(node) {
33
+ // This is for Date values.
34
+ if (node && node.toJSON && typeof node.toJSON === 'function') {
35
+ // eslint-disable-next-line no-param-reassign
36
+ node = node.toJSON();
37
+ }
38
+ if (node === undefined) {
39
+ return;
40
+ }
41
+ if (typeof node === 'number') {
42
+ return isFinite(node) ? '' + node : 'null';
43
+ }
44
+ if (typeof node !== 'object') {
45
+ return JSONStringify(node);
46
+ }
47
+ let i;
48
+ let out;
49
+ if (ArrayIsArray(node)) {
50
+ out = '[';
51
+ for (i = 0; i < node.length; i++) {
52
+ if (i) {
53
+ out += ',';
54
+ }
55
+ out += stableJSONStringify$1(node[i]) || 'null';
56
+ }
57
+ return out + ']';
58
+ }
59
+ if (node === null) {
60
+ return 'null';
61
+ }
62
+ const keys = ObjectKeys(node).sort();
63
+ out = '';
64
+ for (i = 0; i < keys.length; i++) {
65
+ const key = keys[i];
66
+ const value = stableJSONStringify$1(node[key]);
67
+ if (!value) {
68
+ continue;
69
+ }
70
+ if (out) {
71
+ out += ',';
72
+ }
73
+ out += JSONStringify(key) + ':' + value;
74
+ }
75
+ return '{' + out + '}';
76
+ }
77
+ const keyPrefix = 'Apex';
78
+
79
+ function createResourceRequest$1(config) {
80
+ const headers = {};
81
+ const header_xSFDCAllowContinuation = config.headers.xSFDCAllowContinuation;
82
+ if (header_xSFDCAllowContinuation !== undefined) {
83
+ headers['X-SFDC-Allow-Continuation'] = header_xSFDCAllowContinuation;
84
+ }
85
+ return {
86
+ baseUri: '/lwr/apex/v66.0',
87
+ basePath: '/' + config.urlParams.apexClass + '/' + config.urlParams.apexMethod + '',
88
+ method: 'get',
89
+ body: null,
90
+ urlParams: config.urlParams,
91
+ queryParams: config.queryParams,
92
+ headers,
93
+ priority: 'normal',
94
+ };
95
+ }
96
+
97
+ const { create, keys, values } = Object;
98
+ const { hasOwnProperty } = Object.prototype;
99
+ const { isArray } = Array;
100
+ const { stringify } = JSON;
101
+
102
+ function createLink(ref) {
103
+ return {
104
+ __ref: serializeStructuredKey(ref),
105
+ };
106
+ }
107
+
108
+ const CACHE_CONTROL = 'cache-control';
109
+ // eslint-disable-next-line @salesforce/lds/no-invalid-todo
110
+ // TODO: APEX_TTL, apexResponseEquals, apexResponseIngest, and validateAdapterConfig should have been code generated
111
+ // however compiler does not support response body type any so hand roll for now
112
+ /**
113
+ * Time to live for the Apex cache value. 5 minutes.
114
+ */
115
+ const APEX_TTL = 5 * 60 * 1000;
116
+ // apex is essentially versionless, we can never know the shape of apex data
117
+ // so we will rely on components to code defensively. All apex data will be ingested
118
+ // and looked up with this version
119
+ const APEX_VERSION = 'APEX_V_1';
120
+ const APEX_STORE_METADATA_PARAMS = {
121
+ ttl: APEX_TTL,
122
+ namespace: keyPrefix,
123
+ representationName: '',
124
+ version: APEX_VERSION,
125
+ };
126
+ function apexResponseEquals(existing, incoming) {
127
+ return stringify(incoming) === stringify(existing);
128
+ }
129
+ const apexResponseIngest = (input, path, luvio, store, timestamp) => {
130
+ // skip validation and normalization, since input type is any
131
+ const key = path.fullPath;
132
+ const incomingRecord = input;
133
+ const existingRecord = store.readEntry(key);
134
+ // freeze on ingest (luvio.opaque)
135
+ deepFreeze(incomingRecord);
136
+ if (existingRecord === undefined ||
137
+ apexResponseEquals(existingRecord, incomingRecord) === false) {
138
+ luvio.storePublish(key, incomingRecord);
139
+ }
140
+ luvio.publishStoreMetadata(key, {
141
+ ...APEX_STORE_METADATA_PARAMS,
142
+ ingestionTimestamp: timestamp,
143
+ });
144
+ return createLink(key);
145
+ };
146
+ function validateAdapterConfig(untrustedConfig) {
147
+ if (untrustedIsObject(untrustedConfig)) {
148
+ const values$1 = values(untrustedConfig);
149
+ return values$1.indexOf(undefined) === -1 ? untrustedConfig : null;
150
+ }
151
+ return untrustedConfig;
152
+ }
153
+ /**
154
+ * A standard delimiter when producing cache keys.
155
+ */
156
+ const KEY_DELIM = ':';
157
+ function isEmptyParam(param) {
158
+ return (param === undefined ||
159
+ param === null ||
160
+ (typeof param === 'object' && keys(param).length === 0));
161
+ }
162
+ function keyBuilder(classname, method, isContinuation, params) {
163
+ return [
164
+ classname.replace('__', KEY_DELIM),
165
+ method,
166
+ isContinuation,
167
+ isEmptyParam(params) ? '' : stableJSONStringify$1(params),
168
+ ].join(KEY_DELIM);
169
+ }
170
+ function configBuilder(config, classname, method, isContinuation) {
171
+ return {
172
+ apexMethod: method,
173
+ apexClass: classname,
174
+ methodParams: config,
175
+ xSFDCAllowContinuation: isContinuation + '',
176
+ };
177
+ }
178
+ function apexClassnameBuilder(namespace, classname) {
179
+ return namespace !== '' ? `${namespace}__${classname}` : classname;
180
+ }
181
+ function isCacheControlValueCacheable(value) {
182
+ if (value === undefined || value === null || typeof value !== 'string') {
183
+ return false;
184
+ }
185
+ return value.indexOf('no-cache') < 0 && value.indexOf('no-store') < 0;
186
+ }
187
+ function getCacheControlHeaderValue(headers) {
188
+ if (headers === undefined) {
189
+ return undefined;
190
+ }
191
+ // header fields are case-insensitive according to
192
+ // https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
193
+ const headerKeys = keys(headers);
194
+ for (let i = 0, len = headerKeys.length; i < len; i += 1) {
195
+ const key = headerKeys[i];
196
+ if (key.toLowerCase() === CACHE_CONTROL) {
197
+ return headers[key];
198
+ }
199
+ }
200
+ return undefined;
201
+ }
202
+ function shouldCache(response) {
203
+ const { headers } = response;
204
+ const headerValue = getCacheControlHeaderValue(headers);
205
+ return isCacheControlValueCacheable(headerValue);
206
+ }
207
+
208
+ function createResourceParams$1(config) {
209
+ const queryParams = create(null);
210
+ if (!isEmptyParam(config.methodParams)) {
211
+ queryParams.methodParams = config.methodParams;
212
+ }
213
+ return {
214
+ queryParams,
215
+ urlParams: {
216
+ apexMethod: config.apexMethod,
217
+ apexClass: config.apexClass,
218
+ },
219
+ headers: {
220
+ xSFDCAllowContinuation: config.xSFDCAllowContinuation,
221
+ },
222
+ };
223
+ }
224
+ function keyBuilderFromResourceParams$1(params) {
225
+ let classname = params.urlParams.apexClass.replace('__', KEY_DELIM);
226
+ return [
227
+ classname,
228
+ params.urlParams.apexMethod,
229
+ params.headers.xSFDCAllowContinuation,
230
+ isEmptyParam(params.queryParams.methodParams)
231
+ ? ''
232
+ : stableJSONStringify$1(params.queryParams.methodParams),
233
+ ].join(KEY_DELIM);
234
+ }
235
+ function ingestSuccess$1(luvio, resourceParams, response, snapshotRefresh) {
236
+ const { body } = response;
237
+ const recordId = keyBuilderFromResourceParams$1(resourceParams);
238
+ const select = {
239
+ recordId,
240
+ node: { kind: 'Fragment', opaque: true, private: [], version: APEX_VERSION },
241
+ variables: {},
242
+ };
243
+ luvio.storeIngest(recordId, apexResponseIngest, body);
244
+ const snapshot = luvio.storeLookup(select, snapshotRefresh);
245
+ if (process.env.NODE_ENV !== 'production') {
246
+ if (response.headers !== undefined && snapshot.state !== 'Fulfilled') {
247
+ throw new Error('Invalid network response. Expected resource response to result in Fulfilled snapshot');
248
+ }
249
+ if (!(snapshot.state === 'Fulfilled' || snapshot.state === 'Stale')) {
250
+ throw new Error('Invalid resource response. Expected resource response to result in Fulfilled or Stale snapshot');
251
+ }
252
+ }
253
+ return snapshot;
254
+ }
255
+ function buildCachedSnapshotCachePolicy$1(buildSnapshotContext, storeLookup) {
256
+ const { luvio, config } = buildSnapshotContext;
257
+ const { apexClass, apexMethod, xSFDCAllowContinuation, methodParams } = config;
258
+ const recordId = keyBuilder(apexClass, apexMethod, xSFDCAllowContinuation, methodParams);
259
+ return storeLookup({
260
+ recordId: recordId,
261
+ node: {
262
+ kind: 'Fragment',
263
+ opaque: true,
264
+ private: [],
265
+ version: APEX_VERSION,
266
+ },
267
+ variables: {},
268
+ }, {
269
+ config,
270
+ resolve: () => buildNetworkSnapshot$1(luvio, config, snapshotRefreshOptions),
271
+ });
272
+ }
273
+ function onFetchResponseSuccess$1(luvio, config, resourceParams, response) {
274
+ const recordId = keyBuilderFromResourceParams$1(resourceParams);
275
+ const select = {
276
+ recordId,
277
+ node: { kind: 'Fragment', opaque: true, private: [], version: APEX_VERSION },
278
+ variables: {},
279
+ };
280
+ if (shouldCache(response)) {
281
+ const snapshot = ingestSuccess$1(luvio, resourceParams, response, {
282
+ config,
283
+ resolve: () => buildNetworkSnapshot$1(luvio, config, snapshotRefreshOptions),
284
+ });
285
+ return luvio.storeBroadcast().then(() => snapshot);
286
+ }
287
+ // if Cache-Control is not set or set to 'no-cache', return a synthetic snapshot
288
+ return Promise.resolve({
289
+ recordId,
290
+ variables: {},
291
+ seenRecords: new StoreKeySet(),
292
+ select,
293
+ state: 'Fulfilled',
294
+ data: response.body,
295
+ });
296
+ }
297
+ function onFetchResponseError$1(luvio, config, _resourceParams, response) {
298
+ return Promise.resolve(luvio.errorSnapshot(response, {
299
+ config,
300
+ resolve: () => buildNetworkSnapshot$1(luvio, config, snapshotRefreshOptions),
301
+ }));
302
+ }
303
+ function buildNetworkSnapshot$1(luvio, config, options) {
304
+ const resourceParams = createResourceParams$1(config);
305
+ const request = createResourceRequest$1(resourceParams);
306
+ return luvio.dispatchResourceRequest(request, options).then((response) => {
307
+ return luvio.handleSuccessResponse(() => onFetchResponseSuccess$1(luvio, config, resourceParams, response),
308
+ // TODO [W-10490362]: Properly generate the response cache keys
309
+ () => {
310
+ return new StoreKeyMap();
311
+ });
312
+ }, (response) => {
313
+ return luvio.handleErrorResponse(() => onFetchResponseError$1(luvio, config, resourceParams, response));
314
+ });
315
+ }
316
+ function buildNetworkSnapshotCachePolicy$1(context, coercedAdapterRequestContext) {
317
+ const { luvio, config } = context;
318
+ const { networkPriority, requestCorrelator, eventObservers, sourceContext } = coercedAdapterRequestContext;
319
+ const dispatchOptions = {
320
+ resourceRequestContext: {
321
+ requestCorrelator,
322
+ sourceContext,
323
+ },
324
+ eventObservers,
325
+ };
326
+ if (networkPriority !== 'normal') {
327
+ dispatchOptions.overrides = {
328
+ priority: networkPriority,
329
+ };
330
+ }
331
+ return buildNetworkSnapshot$1(luvio, config, dispatchOptions);
332
+ }
333
+ const factory = (luvio, invokerParams) => {
334
+ const { namespace, classname, method, isContinuation } = invokerParams;
335
+ return getApexAdapterFactory(luvio, namespace, classname, method, isContinuation);
336
+ };
337
+ function getApexAdapterFactory(luvio, namespace, classname, method, isContinuation) {
338
+ return (untrustedConfig, requestContext) => {
339
+ // Even though the config is of type `any`,
340
+ // validation is required here because `undefined`
341
+ // values on a wire mean that properties on the component
342
+ // used in the config have not been loaded yet.
343
+ const config = validateAdapterConfig(untrustedConfig);
344
+ // Invalid or incomplete config
345
+ if (config === null) {
346
+ return null;
347
+ }
348
+ const configPlus = configBuilder(config, apexClassnameBuilder(namespace, classname), method, isContinuation);
349
+ return luvio.applyCachePolicy(requestContext || {}, { config: configPlus, luvio }, buildCachedSnapshotCachePolicy$1, buildNetworkSnapshotCachePolicy$1);
350
+ };
351
+ }
352
+
353
+ /**
354
+ * A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
355
+ * This is needed because insertion order for JSON.stringify(object) affects output:
356
+ * JSON.stringify({a: 1, b: 2})
357
+ * "{"a":1,"b":2}"
358
+ * JSON.stringify({b: 2, a: 1})
359
+ * "{"b":2,"a":1}"
360
+ * @param data Data to be JSON-stringified.
361
+ * @returns JSON.stringified value with consistent ordering of keys.
362
+ */
363
+ function stableJSONStringify(node) {
364
+ // This is for Date values.
365
+ if (node && node.toJSON && typeof node.toJSON === 'function') {
366
+ // eslint-disable-next-line no-param-reassign
367
+ node = node.toJSON();
368
+ }
369
+ if (node === undefined) {
370
+ return;
371
+ }
372
+ if (typeof node === 'number') {
373
+ return isFinite(node) ? '' + node : 'null';
374
+ }
375
+ if (typeof node !== 'object') {
376
+ return stringify(node);
377
+ }
378
+ let i;
379
+ let out;
380
+ if (isArray(node)) {
381
+ out = '[';
382
+ for (i = 0; i < node.length; i++) {
383
+ if (i) {
384
+ out += ',';
385
+ }
386
+ out += stableJSONStringify(node[i]) || 'null';
387
+ }
388
+ return out + ']';
389
+ }
390
+ if (node === null) {
391
+ return 'null';
392
+ }
393
+ const keys$1 = keys(node).sort();
394
+ out = '';
395
+ for (i = 0; i < keys$1.length; i++) {
396
+ const key = keys$1[i];
397
+ const value = stableJSONStringify(node[key]);
398
+ if (!value) {
399
+ continue;
400
+ }
401
+ if (out) {
402
+ out += ',';
403
+ }
404
+ out += stringify(key) + ':' + value;
405
+ }
406
+ return '{' + out + '}';
407
+ }
408
+ /**
409
+ * Returns the field API name, qualified with an object name if possible.
410
+ * @param value The value from which to get the qualified field API name.
411
+ * @return The qualified field API name.
412
+ */
413
+ function getFieldApiName(value) {
414
+ if (typeof value === 'string') {
415
+ return value;
416
+ }
417
+ else if (value &&
418
+ typeof value.objectApiName === 'string' &&
419
+ typeof value.fieldApiName === 'string') {
420
+ return value.objectApiName + '.' + value.fieldApiName;
421
+ }
422
+ throw new TypeError('Value is not a string or FieldId.');
423
+ }
424
+ /**
425
+ * Split the object API name and field API name from a qualified field name.
426
+ * Eg: Opportunity.Title returns ['Opportunity', 'Title']
427
+ * Eg: Opportunity.Account.Name returns ['Opportunity', 'Account.Name']
428
+ * @param fieldApiName The qualified field name.
429
+ * @return The object and field API names.
430
+ */
431
+ function splitQualifiedFieldApiName(fieldApiName) {
432
+ const idx = fieldApiName.indexOf('.');
433
+ if (idx < 1) {
434
+ // object api name must non-empty
435
+ throw new TypeError('Value does not include an object API name.');
436
+ }
437
+ return [fieldApiName.substring(0, idx), fieldApiName.substring(idx + 1)];
438
+ }
439
+
440
+ function createResourceRequest(config) {
441
+ const headers = {};
442
+ const header_xSFDCAllowContinuation = config.headers.xSFDCAllowContinuation;
443
+ if (header_xSFDCAllowContinuation !== undefined) {
444
+ headers['X-SFDC-Allow-Continuation'] = header_xSFDCAllowContinuation;
445
+ }
446
+ return {
447
+ baseUri: '/lwr/apex/v66.0',
448
+ basePath: '/' + config.urlParams.apexClass + '/' + config.urlParams.apexMethod + '',
449
+ method: 'post',
450
+ body: config.body,
451
+ urlParams: config.urlParams,
452
+ queryParams: {},
453
+ headers,
454
+ priority: 'normal',
455
+ };
456
+ }
457
+
458
+ function createResourceParams(config) {
459
+ return {
460
+ urlParams: {
461
+ apexMethod: config.apexMethod,
462
+ apexClass: config.apexClass,
463
+ },
464
+ body: config.methodParams,
465
+ headers: {
466
+ xSFDCAllowContinuation: config.xSFDCAllowContinuation,
467
+ },
468
+ };
469
+ }
470
+ function keyBuilderFromResourceParams(params) {
471
+ let classname = params.urlParams.apexClass.replace('__', KEY_DELIM);
472
+ return [
473
+ classname,
474
+ params.urlParams.apexMethod,
475
+ params.headers.xSFDCAllowContinuation,
476
+ isEmptyParam(params.body) ? '' : stableJSONStringify(params.body),
477
+ ].join(KEY_DELIM);
478
+ }
479
+ function ingestSuccess(luvio, resourceParams, response, snapshotRefresh) {
480
+ const { body } = response;
481
+ const recordId = keyBuilderFromResourceParams(resourceParams);
482
+ const select = {
483
+ recordId,
484
+ node: { kind: 'Fragment', opaque: true, private: [], version: APEX_VERSION },
485
+ variables: {},
486
+ };
487
+ luvio.storeIngest(recordId, apexResponseIngest, body);
488
+ const snapshot = luvio.storeLookup(select, snapshotRefresh);
489
+ if (process.env.NODE_ENV !== 'production') {
490
+ if (response.headers !== undefined && snapshot.state !== 'Fulfilled') {
491
+ throw new Error('Invalid network response. Expected resource response to result in Fulfilled snapshot');
492
+ }
493
+ if (!(snapshot.state === 'Fulfilled' || snapshot.state === 'Stale')) {
494
+ throw new Error('Invalid resource response. Expected resource response to result in Fulfilled or Stale snapshot');
495
+ }
496
+ }
497
+ return snapshot;
498
+ }
499
+ function buildCachedSnapshotCachePolicy(buildSnapshotContext, storeLookup) {
500
+ const { config } = buildSnapshotContext;
501
+ const { apexClass, apexMethod, xSFDCAllowContinuation, methodParams } = config;
502
+ const recordId = keyBuilder(apexClass, apexMethod, xSFDCAllowContinuation, methodParams);
503
+ return storeLookup({
504
+ recordId: recordId,
505
+ node: {
506
+ kind: 'Fragment',
507
+ opaque: true,
508
+ private: [],
509
+ version: APEX_VERSION,
510
+ },
511
+ variables: {},
512
+ });
513
+ }
514
+ function onFetchResponseSuccess(luvio, _config, resourceParams, response) {
515
+ const recordId = keyBuilderFromResourceParams(resourceParams);
516
+ const select = {
517
+ recordId,
518
+ node: { kind: 'Fragment', opaque: true, private: [], version: APEX_VERSION },
519
+ variables: {},
520
+ };
521
+ if (shouldCache(response)) {
522
+ const snapshot = ingestSuccess(luvio, resourceParams, response);
523
+ return luvio.storeBroadcast().then(() => snapshot);
524
+ }
525
+ // if Cache-Control is not set or set to 'no-cache', return a synthetic snapshot
526
+ return Promise.resolve({
527
+ recordId,
528
+ variables: {},
529
+ seenRecords: new StoreKeySet(),
530
+ select,
531
+ state: 'Fulfilled',
532
+ data: response.body,
533
+ });
534
+ }
535
+ function onFetchResponseError(luvio, _config, _resourceParams, response) {
536
+ return Promise.resolve(luvio.errorSnapshot(response));
537
+ }
538
+ function buildNetworkSnapshot(luvio, config, options) {
539
+ const resourceParams = createResourceParams(config);
540
+ const request = createResourceRequest(resourceParams);
541
+ return luvio.dispatchResourceRequest(request, options).then((response) => {
542
+ return luvio.handleSuccessResponse(() => onFetchResponseSuccess(luvio, config, resourceParams, response),
543
+ // TODO [W-10490362]: Properly generate response cache keys
544
+ () => {
545
+ return new StoreKeyMap();
546
+ });
547
+ }, (response) => {
548
+ return luvio.handleErrorResponse(() => onFetchResponseError(luvio, config, resourceParams, response));
549
+ });
550
+ }
551
+ function buildNetworkSnapshotCachePolicy(context, coercedAdapterRequestContext) {
552
+ const { luvio, config } = context;
553
+ const { networkPriority, requestCorrelator, eventObservers, sourceContext } = coercedAdapterRequestContext;
554
+ const dispatchOptions = {
555
+ resourceRequestContext: {
556
+ requestCorrelator,
557
+ sourceContext,
558
+ },
559
+ eventObservers,
560
+ };
561
+ if (networkPriority !== 'normal') {
562
+ dispatchOptions.overrides = {
563
+ priority: networkPriority,
564
+ };
565
+ }
566
+ return buildNetworkSnapshot(luvio, config, dispatchOptions);
567
+ }
568
+ function handleSnapshot(snapshot) {
569
+ if (snapshot.state === 'Error') {
570
+ throw snapshot.error;
571
+ }
572
+ return snapshot.data;
573
+ }
574
+ /**
575
+ * Returns a function that executes the supplied ldsAdapter,
576
+ * and handles unwrapping the snapshot to return to caller
577
+ *
578
+ * @param ldsAdapter adapter to be invoked
579
+ * @returns an ApexInvoker
580
+ */
581
+ function invoker(ldsAdapter) {
582
+ return (config, requestContext) => {
583
+ const snapshotOrPromise = ldsAdapter(config, requestContext);
584
+ return Promise.resolve(snapshotOrPromise).then(handleSnapshot);
585
+ };
586
+ }
587
+ const invokerFactory = (luvio, invokerParams, adapterFactory) => {
588
+ const { namespace, classname, method, isContinuation } = invokerParams;
589
+ const ldsAdapter = adapterFactory(luvio, namespace, classname, method, isContinuation);
590
+ return invoker(ldsAdapter);
591
+ };
592
+ const postInvoker = (luvio, invokerParams) => {
593
+ return invokerFactory(luvio, invokerParams, postApexAdapterFactory);
594
+ };
595
+ const getInvoker = (luvio, invokerParams) => {
596
+ return invokerFactory(luvio, invokerParams, getApexAdapterFactory);
597
+ };
598
+ function postApexAdapterFactory(luvio, namespace, classname, method, isContinuation) {
599
+ return (config, requestContext) => {
600
+ // config validation is unnecessary for this imperative adapter
601
+ // due to the config being of type `any`.
602
+ // however, we have special config validation for the wire adapter,
603
+ // explanation in getApex
604
+ const configPlus = configBuilder(config, apexClassnameBuilder(namespace, classname), method, isContinuation);
605
+ return luvio.applyCachePolicy(requestContext || {}, { config: configPlus, luvio }, buildCachedSnapshotCachePolicy, buildNetworkSnapshotCachePolicy);
606
+ };
607
+ }
608
+
609
+ /**
610
+ * Gets a field value from an Apex sObject.
611
+ * @param sobject The sObject holding the field.
612
+ * @param field The qualified API name of the field to return.
613
+ * @returns The field's value. If it doesn't exist, undefined is returned.
614
+ */
615
+ function getSObjectValue(sObject, field) {
616
+ if (untrustedIsObject(sObject) === false) {
617
+ return;
618
+ }
619
+ const unqualifiedField = splitQualifiedFieldApiName(getFieldApiName(field))[1];
620
+ const fields = unqualifiedField.split('.');
621
+ let ret = sObject;
622
+ for (let i = 0, fieldsLength = fields.length; i < fieldsLength; i++) {
623
+ const nextField = fields[i];
624
+ if (!hasOwnProperty.call(ret, nextField)) {
625
+ return undefined;
626
+ }
627
+ ret = ret[nextField];
628
+ }
629
+ return ret;
630
+ }
631
+
632
+ const engineForPrefetcherMap = new Map();
633
+ function registerPrefetcher(luvio, prefetcher) {
634
+ if (process.env.NODE_ENV !== 'production') {
635
+ if (engineForPrefetcherMap.has(luvio)) {
636
+ throw new Error('Environment error: Only one prefetcher per engine is allowed.');
637
+ }
638
+ }
639
+ engineForPrefetcherMap.set(luvio, prefetcher);
640
+ }
641
+
642
+ export { getInvoker as GetApexInvoker, factory as GetApexWireAdapterFactory, postInvoker as PostApexInvoker, getSObjectValue, registerPrefetcher };
@@ -0,0 +1,62 @@
1
+ import { Adapter as $64$luvio_engine_Adapter, Snapshot as $64$luvio_engine_Snapshot, UnfulfilledSnapshot as $64$luvio_engine_UnfulfilledSnapshot, AdapterConfigMetadata as $64$luvio_engine_AdapterConfigMetadata } from '@luvio/engine';
2
+ export declare const ObjectPrototypeHasOwnProperty: (v: PropertyKey) => boolean;
3
+ declare const ObjectKeys: {
4
+ (o: object): string[];
5
+ (o: {}): string[];
6
+ }, ObjectCreate: {
7
+ (o: object | null): any;
8
+ (o: object | null, properties: PropertyDescriptorMap & ThisType<any>): any;
9
+ };
10
+ export { ObjectCreate, ObjectKeys };
11
+ export declare const ArrayIsArray: (arg: any) => arg is any[];
12
+ export declare const ArrayPrototypePush: (...items: any[]) => number;
13
+ export interface AdapterValidationConfig {
14
+ displayName: string;
15
+ parameters: {
16
+ required: string[];
17
+ optional: string[];
18
+ unsupported?: string[];
19
+ };
20
+ }
21
+ /**
22
+ * Validates an adapter config is well-formed.
23
+ * @param config The config to validate.
24
+ * @param adapter The adapter validation configuration.
25
+ * @param oneOf The keys the config must contain at least one of.
26
+ * @throws A TypeError if config doesn't satisfy the adapter's config validation.
27
+ */
28
+ export declare function validateConfig<T>(config: Untrusted<T>, adapter: AdapterValidationConfig, oneOf?: string[]): void;
29
+ export declare function untrustedIsObject<Base>(untrusted: unknown): untrusted is Untrusted<Base>;
30
+ export type UncoercedConfiguration<Base, Options extends {
31
+ [key in keyof Base]?: any;
32
+ }> = {
33
+ [Key in keyof Base]?: Base[Key] | Options[Key];
34
+ };
35
+ export type Untrusted<Base> = Partial<Base>;
36
+ export declare function areRequiredParametersPresent<T>(config: any, configPropertyNames: AdapterValidationConfig): config is T;
37
+ export declare function refreshable<C, D, R>(adapter: $64$luvio_engine_Adapter<C, D>, resolve: (config: unknown) => Promise<$64$luvio_engine_Snapshot<R>>): $64$luvio_engine_Adapter<C, D>;
38
+ export declare const SNAPSHOT_STATE_FULFILLED = "Fulfilled";
39
+ export declare const SNAPSHOT_STATE_UNFULFILLED = "Unfulfilled";
40
+ export declare const snapshotRefreshOptions: {
41
+ overrides: {
42
+ headers: {
43
+ 'Cache-Control': string;
44
+ };
45
+ };
46
+ };
47
+ /**
48
+ * A deterministic JSON stringify implementation. Heavily adapted from https://github.com/epoberezkin/fast-json-stable-stringify.
49
+ * This is needed because insertion order for JSON.stringify(object) affects output:
50
+ * JSON.stringify({a: 1, b: 2})
51
+ * "{"a":1,"b":2}"
52
+ * JSON.stringify({b: 2, a: 1})
53
+ * "{"b":2,"a":1}"
54
+ * @param data Data to be JSON-stringified.
55
+ * @returns JSON.stringified value with consistent ordering of keys.
56
+ */
57
+ export declare function stableJSONStringify(node: any): string | undefined;
58
+ export declare function getFetchResponseStatusText(status: number): string;
59
+ export declare function isUnfulfilledSnapshot<T, U>(snapshot: $64$luvio_engine_Snapshot<T, U>): snapshot is $64$luvio_engine_UnfulfilledSnapshot<T, U>;
60
+ export declare function generateParamConfigMetadata(name: string, required: boolean, resourceType: $64$luvio_engine_AdapterConfigMetadata['resourceType'], typeCheckShape: $64$luvio_engine_AdapterConfigMetadata['typeCheckShape'], isArrayShape?: boolean, coerceFn?: (v: unknown) => unknown): $64$luvio_engine_AdapterConfigMetadata;
61
+ export declare function buildAdapterValidationConfig(displayName: string, paramsMeta: $64$luvio_engine_AdapterConfigMetadata[]): AdapterValidationConfig;
62
+ export declare const keyPrefix = "Apex";