@salesforce/lds-runtime-mobile 1.129.1 → 1.130.9

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.
Files changed (3) hide show
  1. package/dist/main.js +119 -29
  2. package/package.json +3 -3
  3. package/sfdc/main.js +119 -29
package/dist/main.js CHANGED
@@ -1240,7 +1240,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1240
1240
  for (const cacheKeyMapKey of cacheKeyMapKeys) {
1241
1241
  const cacheKey = cacheKeyMap.get(cacheKeyMapKey);
1242
1242
  if (cacheKey.mergeable === true) {
1243
- keysToRevive.add(cacheKeyMapKey);
1243
+ const canonical = environment.storeGetCanonicalKey(cacheKeyMapKey);
1244
+ keysToRevive.add(canonical);
1244
1245
  }
1245
1246
  }
1246
1247
  let snapshotFromMemoryIngest = undefined;
@@ -2096,6 +2097,7 @@ const recordPrefix = '.data.uiapi.query';
2096
2097
  const recordSuffix = 'edges';
2097
2098
  const pathPrefix = '$';
2098
2099
  const recordsCTE = 'recordsCTE';
2100
+ const MultiPickListValueSeparator$1 = ';';
2099
2101
  function cteSql() {
2100
2102
  return (`WITH ${recordsCTE} AS NOT materialized ` +
2101
2103
  `(select data from lds_data where key like 'UiApi::RecordRepresentation:%')`);
@@ -2264,16 +2266,26 @@ function existsPredicateToSql(exists) {
2264
2266
  }
2265
2267
  function comparisonPredicateToSql(predicate) {
2266
2268
  const operator = comparisonOperatorToSql(predicate.operator);
2267
- const { sql: left, bindings: leftBindings } = expressionToSql(predicate.left);
2269
+ let { sql: left, bindings: leftBindings } = expressionToSql(predicate.left, undefined, predicate.operator);
2268
2270
  if (predicate.right.type === ValueType.DateEnum ||
2269
2271
  predicate.right.type === ValueType.DateTimeEnum ||
2270
2272
  predicate.right.type === ValueType.DateArray ||
2271
2273
  predicate.right.type === ValueType.DateTimeArray ||
2272
- predicate.right.type === ValueType.DateValue) {
2273
- const fieldDateValue = `date(${left})`;
2274
+ predicate.right.type === ValueType.DateValue ||
2275
+ predicate.right.type === ValueType.DateTimeValue) {
2276
+ const dateFunction = predicate.right.type === ValueType.DateTimeEnum ||
2277
+ predicate.right.type === ValueType.DateTimeArray ||
2278
+ predicate.right.type === ValueType.DateTimeValue
2279
+ ? 'datetime'
2280
+ : 'date';
2281
+ const fieldDateValue = `${dateFunction}(${left})`;
2274
2282
  return comparisonDateLiteralToSql(fieldDateValue, predicate.operator, predicate.right);
2275
2283
  }
2276
- const { sql: right, bindings: rightBindings } = expressionToSql(predicate.right);
2284
+ if (predicate.right.type === ValueType.RelativeDate) {
2285
+ const dateFunc = predicate.right.hasTime ? 'datetime' : 'date';
2286
+ left = `${dateFunc}(${left})`;
2287
+ }
2288
+ const { sql: right, bindings: rightBindings } = expressionToSql(predicate.right, undefined, predicate.operator);
2277
2289
  let bindings = leftBindings.concat(rightBindings);
2278
2290
  if (predicate.operator === ComparisonOperator.eq &&
2279
2291
  predicate.right.type === ValueType.StringLiteral &&
@@ -2358,11 +2370,21 @@ function coerceToTargetDataType(initialSql, targetDataType) {
2358
2370
  return initialSql;
2359
2371
  }
2360
2372
  }
2361
- function expressionToSql(expression, targetDataType) {
2373
+ function expressionToSql(expression, targetDataType, operator) {
2362
2374
  switch (expression.type) {
2363
2375
  case ValueType.Extract: {
2376
+ // displayValue's for Booleans are special, they return null
2377
+ if (expression.subfield === 'displayValue' && targetDataType === 'Boolean') {
2378
+ return { sql: 'null', bindings: [] };
2379
+ }
2364
2380
  let path = extractPath(expression.field, expression.subfield);
2365
- let sql = `json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}')`;
2381
+ // For multiple picklist includes/excluding filtering, we need to prefix and suffix the field value with ';'
2382
+ // to make the match safe.
2383
+ // sample: field value: 'item12;item123', input value is 'item1'; they need to be converted to
2384
+ // ';item12;item123;' and '%;item1;%' first, then do sqlite like operation.
2385
+ let sql = operator === ComparisonOperator.includes || operator === ComparisonOperator.excludes
2386
+ ? `'${MultiPickListValueSeparator$1}' || json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}') || '${MultiPickListValueSeparator$1}'`
2387
+ : `json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}')`;
2366
2388
  if (targetDataType !== undefined) {
2367
2389
  sql = coerceToTargetDataType(sql, targetDataType);
2368
2390
  }
@@ -2409,7 +2431,7 @@ function expressionToSql(expression, targetDataType) {
2409
2431
  case ValueType.StringLiteral:
2410
2432
  return stringLiteralToSql(expression);
2411
2433
  case ValueType.MultiPicklistSet:
2412
- return multiPicklistToSql$1(expression);
2434
+ return multiPicklistToSql$1(expression, operator);
2413
2435
  }
2414
2436
  }
2415
2437
  function stringLiteralToSql(string) {
@@ -2425,14 +2447,18 @@ function expressionArrayToSql(expressions, toSql) {
2425
2447
  const bindings = results.length > 0 ? results.map((v) => v.bindings).reduce(flatten$1) : [];
2426
2448
  return { sql, bindings };
2427
2449
  }
2428
- function multiPicklistToSql$1({ value }) {
2450
+ function multiPicklistToSql$1({ value }, operator) {
2429
2451
  // Individual multipicklist terms that delimited by semicolon are stored server-side
2430
2452
  // as lexically sorted strings and treated like logical ANDs. We can approximate this
2431
2453
  // behavior in SQL with wildcarded `LIKE` SQL operators. Terms with no delimiter can
2432
2454
  // be treated as string literals. Multiple terms are logically OR'd together to
2433
2455
  // match the behavior described in SOQL documentation (https://sfdc.co/c9j0r)
2456
+ // To make sure the match is safe for includes/excludes. the value is prefix and
2457
+ // suffix with ';', like 'abc' to '%;abc;%'. raw value for eq and ne.
2434
2458
  const sql = '?';
2435
- const binding = value && value.includes(';') ? `'%${value.split(';').join('%')}%'` : `'%${value}%'`;
2459
+ const binding = operator === ComparisonOperator.includes || operator === ComparisonOperator.excludes
2460
+ ? `%${MultiPickListValueSeparator$1}${value}${MultiPickListValueSeparator$1}%`
2461
+ : value;
2436
2462
  return { sql, bindings: [binding] };
2437
2463
  }
2438
2464
  function relativeDateToSql(expression) {
@@ -2503,8 +2529,12 @@ function comparisonDateLiteralToSql(leftOperand, operator, dateInput) {
2503
2529
  }
2504
2530
  }
2505
2531
  if (dateInput.type === ValueType.DateValue || dateInput.type === ValueType.DateTimeValue) {
2532
+ const dateFunction = dateInput.type === ValueType.DateTimeValue ? 'datetime' : 'date';
2506
2533
  const compOperator = comparisonOperatorToSql(operator);
2507
- return { sql: leftOperand + ` ${compOperator} ` + '?', bindings: [`${dateInput.value}`] };
2534
+ return {
2535
+ sql: leftOperand + ` ${compOperator} ` + `${dateFunction}(?)`,
2536
+ bindings: [`${dateInput.value}`],
2537
+ };
2508
2538
  }
2509
2539
  return { sql: '', bindings: [] };
2510
2540
  }
@@ -2735,15 +2765,41 @@ function fieldFilter(fieldName, fieldNode, alias, apiName, input, joins) {
2735
2765
  return dateRangeComparison(op.value, op.operator, extract);
2736
2766
  }
2737
2767
  if (op.type === 'MultiPicklistSetOperator') {
2768
+ const operator = op.operator === ComparisonOperator.includes
2769
+ ? CompoundOperator.or
2770
+ : CompoundOperator.and;
2771
+ const children = [];
2772
+ const length = op.value.value.length;
2773
+ for (let i = 0; i < length; i++) {
2774
+ const term = op.value.value[i];
2775
+ if (term !== null) {
2776
+ const splittedValue = term.split(MultiPickListValueSeparator$1);
2777
+ if (splittedValue.length === 1) {
2778
+ children.push(comparison(extract, op.operator, {
2779
+ type: ValueType.MultiPicklistSet,
2780
+ value: term,
2781
+ }));
2782
+ }
2783
+ else {
2784
+ children.push({
2785
+ type: PredicateType$1.compound,
2786
+ operator: op.operator === ComparisonOperator.includes
2787
+ ? CompoundOperator.and
2788
+ : CompoundOperator.or,
2789
+ children: splittedValue.map((singleValue) => {
2790
+ return comparison(extract, op.operator, {
2791
+ type: ValueType.MultiPicklistSet,
2792
+ value: singleValue,
2793
+ });
2794
+ }),
2795
+ });
2796
+ }
2797
+ }
2798
+ }
2738
2799
  return {
2739
2800
  type: PredicateType$1.compound,
2740
- operator: CompoundOperator.or,
2741
- children: op.value.value.map((term) => {
2742
- return comparison(extract, op.operator, {
2743
- type: ValueType.MultiPicklistSet,
2744
- value: term,
2745
- });
2746
- }),
2801
+ operator,
2802
+ children,
2747
2803
  };
2748
2804
  }
2749
2805
  if (op.type === 'StringSetOperator' && op.value.value.includes(null)) {
@@ -2807,8 +2863,6 @@ function dateRangeComparison(dateRange, operator, compareDate) {
2807
2863
  return comparison(compareDate, gte, dateRange.start);
2808
2864
  }
2809
2865
  }
2810
- const dateRegEx = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$/;
2811
- const dateTimeRegEx = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))T(2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](\.[0-9]{3})?Z$/;
2812
2866
  function dateFunctions(operatorNode, extract, dataType) {
2813
2867
  if (dataType !== 'Date' && dataType !== 'DateTime') {
2814
2868
  return success([]);
@@ -2886,7 +2940,7 @@ function isStringOperatorType(value) {
2886
2940
  return isScalarOperatorType(value) || value === like;
2887
2941
  }
2888
2942
  function isPicklistOperatorType(value) {
2889
- let values = [eq, ne];
2943
+ let values = [eq, ne, like, lt, gt, lte, gte];
2890
2944
  return values.includes(value);
2891
2945
  }
2892
2946
  function isCurrencyOperatorType(value) {
@@ -3237,8 +3291,11 @@ function operatorWithValue(operator, valueNode, objectInfoDataType) {
3237
3291
  message(`Comparison operator ${operator} is not supported for type ${objectInfoDataType}.`),
3238
3292
  ]);
3239
3293
  }
3294
+ function isValidDate(value) {
3295
+ return isNaN(Date.parse(value)) === false;
3296
+ }
3240
3297
  function dateInput(node) {
3241
- return parseDateNode(node, dateRegEx, false, 'YYYY-MM-DD').map((result) => {
3298
+ return parseDateNode(node, false, 'YYYY-MM-DD', isValidDate).map((result) => {
3242
3299
  switch (result.type) {
3243
3300
  case ValueType.NullValue:
3244
3301
  return result;
@@ -3252,7 +3309,7 @@ function dateInput(node) {
3252
3309
  });
3253
3310
  }
3254
3311
  function dateTimeInput(node) {
3255
- return parseDateNode(node, dateTimeRegEx, true, 'YYYY-MM-DDTHH:MM:SS.SSSZ or YYYY-MM-DDTHH:MM:SSZ').map((result) => {
3312
+ return parseDateNode(node, true, 'YYYY-MM-DDTHH:MM:SS.SSSZ, YYYY-MM-DDTHH:MM:SSZ, YYYY-MM-DDTHH:MM:SS.SSS+|-HH:MM, or YYYY-MM-DDTHH:MM:SS+|-HH:MM', isValidDate).map((result) => {
3256
3313
  switch (result.type) {
3257
3314
  case ValueType.NullValue:
3258
3315
  return result;
@@ -3277,7 +3334,7 @@ function parseNullValue(op) {
3277
3334
  }
3278
3335
  return failure(message(`Null can not be compared with ${op}`));
3279
3336
  }
3280
- function parseDateNode(node, regex, hasTime, dateFormat) {
3337
+ function parseDateNode(node, hasTime, dateFormat, isValidDate) {
3281
3338
  const typeName = hasTime ? 'DateTime' : 'Date';
3282
3339
  if (!isObjectValueNode$1(node)) {
3283
3340
  return failure(message(`Comparison value must be a ${typeName} input.`));
@@ -3285,7 +3342,12 @@ function parseDateNode(node, regex, hasTime, dateFormat) {
3285
3342
  const valueField = node.fields.value;
3286
3343
  if (valueField !== undefined) {
3287
3344
  if (is(valueField, 'StringValue')) {
3288
- if (valueField.value.match(regex)) {
3345
+ // check the date is valid
3346
+ // then make sure if it isnt suppose to contain time stamps that it doesnt
3347
+ // and if it should have a timestamp it should contain it
3348
+ const includesTimeStamp = valueField.value.includes('T');
3349
+ if (isValidDate(valueField.value) &&
3350
+ ((hasTime && includesTimeStamp) || (!hasTime && !includesTimeStamp))) {
3289
3351
  return success(stringLiteral(valueField.value));
3290
3352
  }
3291
3353
  return failure(message(`${typeName} format must be ${dateFormat}.`));
@@ -4277,7 +4339,20 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4277
4339
  // await it here to normalize the input to a snapshot
4278
4340
  const nonEvaluatedSnapshot = await nonEvaluatedSnapshotOrPromise;
4279
4341
  // if the non-eval result has errors we want to return to caller
4280
- if (isErrorSnapshotThatShouldGetReturnedToCaller$1(nonEvaluatedSnapshot)) {
4342
+ const nonEvaluatedGQLSnapshot = nonEvaluatedSnapshot;
4343
+ if (isErrorSnapshotThatShouldGetReturnedToCaller$1(nonEvaluatedGQLSnapshot)) {
4344
+ const { data: gqlData } = nonEvaluatedGQLSnapshot;
4345
+ if (hasGraphQlErrors$1(gqlData) && gqlData !== undefined) {
4346
+ return {
4347
+ ...nonEvaluatedSnapshot,
4348
+ data: undefined,
4349
+ state: 'Error',
4350
+ error: {
4351
+ errorType: 'adapterError',
4352
+ error: gqlData.errors,
4353
+ },
4354
+ };
4355
+ }
4281
4356
  return nonEvaluatedSnapshot;
4282
4357
  }
4283
4358
  let rootQuery;
@@ -10948,6 +11023,10 @@ function makeEnvironmentUiApiRecordDraftAware(luvio, options, env) {
10948
11023
  return create$2(adapterSpecificEnvironments, {});
10949
11024
  }
10950
11025
 
11026
+ function clone(obj) {
11027
+ return parse$2(stringify$2(obj));
11028
+ }
11029
+
10951
11030
  const DEFAULT_FIELD_CREATED_BY_ID = 'CreatedById';
10952
11031
  const DEFAULT_FIELD_CREATED_DATE = 'CreatedDate';
10953
11032
  const DEFAULT_FIELD_ID = 'Id';
@@ -10975,7 +11054,9 @@ function replayDraftsOnRecord(record, draftMetadata) {
10975
11054
  // no baseRecord
10976
11055
  return undefined;
10977
11056
  }
10978
- const { recordOperations } = draftMetadata;
11057
+ const { recordOperations: originalOperations } = draftMetadata;
11058
+ // since replaying drafts is destructive, we need to clone the original operations
11059
+ const recordOperations = clone(originalOperations);
10979
11060
  if (recordOperations.length === 0) {
10980
11061
  return undefined;
10981
11062
  }
@@ -11006,7 +11087,11 @@ function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations)
11006
11087
  }
11007
11088
  const draftActionType = draftOperation.type;
11008
11089
  if (draftActionType === 'create') {
11009
- throw Error('a create action cannot exist on an existing record');
11090
+ if (record.drafts !== undefined) {
11091
+ throw Error('a create action cannot exist on an existing draft record');
11092
+ }
11093
+ // the draft may have been uploaded already so skip the create operation and apply the rest (if any)
11094
+ return recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations);
11010
11095
  }
11011
11096
  // add the draft node
11012
11097
  if (record.drafts === undefined) {
@@ -11769,6 +11854,11 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
11769
11854
  let record = entry && entry.data;
11770
11855
  if (record === undefined) {
11771
11856
  record = storeRecords[recordKey];
11857
+ if (record === undefined) {
11858
+ // fields are being published without a record for them existing,
11859
+ // fields cannot exist standalone in the durable store
11860
+ continue;
11861
+ }
11772
11862
  }
11773
11863
  putRecords[recordId] = true;
11774
11864
  if (isStoreRecordError(record)) {
@@ -15636,4 +15726,4 @@ register({
15636
15726
  });
15637
15727
 
15638
15728
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15639
- // version: 1.129.1-eb59a8867
15729
+ // version: 1.130.9-2d38b9869
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-runtime-mobile",
3
- "version": "1.129.1",
3
+ "version": "1.130.9",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS runtime for mobile/hybrid environments.",
6
6
  "main": "dist/main.js",
@@ -58,7 +58,7 @@
58
58
  "path": "./dist/main.js",
59
59
  "maxSize": {
60
60
  "none": "650 kB",
61
- "min": "250 kB",
61
+ "min": "255 kB",
62
62
  "compressed": "100 kB"
63
63
  }
64
64
  },
@@ -66,7 +66,7 @@
66
66
  "path": "./sfdc/main.js",
67
67
  "maxSize": {
68
68
  "none": "650 kB",
69
- "min": "250 kB",
69
+ "min": "255 kB",
70
70
  "compressed": "100 kB"
71
71
  }
72
72
  }
package/sfdc/main.js CHANGED
@@ -1240,7 +1240,8 @@ function makeDurable(environment, { durableStore, instrumentation }) {
1240
1240
  for (const cacheKeyMapKey of cacheKeyMapKeys) {
1241
1241
  const cacheKey = cacheKeyMap.get(cacheKeyMapKey);
1242
1242
  if (cacheKey.mergeable === true) {
1243
- keysToRevive.add(cacheKeyMapKey);
1243
+ const canonical = environment.storeGetCanonicalKey(cacheKeyMapKey);
1244
+ keysToRevive.add(canonical);
1244
1245
  }
1245
1246
  }
1246
1247
  let snapshotFromMemoryIngest = undefined;
@@ -2096,6 +2097,7 @@ const recordPrefix = '.data.uiapi.query';
2096
2097
  const recordSuffix = 'edges';
2097
2098
  const pathPrefix = '$';
2098
2099
  const recordsCTE = 'recordsCTE';
2100
+ const MultiPickListValueSeparator$1 = ';';
2099
2101
  function cteSql() {
2100
2102
  return (`WITH ${recordsCTE} AS NOT materialized ` +
2101
2103
  `(select data from lds_data where key like 'UiApi::RecordRepresentation:%')`);
@@ -2264,16 +2266,26 @@ function existsPredicateToSql(exists) {
2264
2266
  }
2265
2267
  function comparisonPredicateToSql(predicate) {
2266
2268
  const operator = comparisonOperatorToSql(predicate.operator);
2267
- const { sql: left, bindings: leftBindings } = expressionToSql(predicate.left);
2269
+ let { sql: left, bindings: leftBindings } = expressionToSql(predicate.left, undefined, predicate.operator);
2268
2270
  if (predicate.right.type === ValueType.DateEnum ||
2269
2271
  predicate.right.type === ValueType.DateTimeEnum ||
2270
2272
  predicate.right.type === ValueType.DateArray ||
2271
2273
  predicate.right.type === ValueType.DateTimeArray ||
2272
- predicate.right.type === ValueType.DateValue) {
2273
- const fieldDateValue = `date(${left})`;
2274
+ predicate.right.type === ValueType.DateValue ||
2275
+ predicate.right.type === ValueType.DateTimeValue) {
2276
+ const dateFunction = predicate.right.type === ValueType.DateTimeEnum ||
2277
+ predicate.right.type === ValueType.DateTimeArray ||
2278
+ predicate.right.type === ValueType.DateTimeValue
2279
+ ? 'datetime'
2280
+ : 'date';
2281
+ const fieldDateValue = `${dateFunction}(${left})`;
2274
2282
  return comparisonDateLiteralToSql(fieldDateValue, predicate.operator, predicate.right);
2275
2283
  }
2276
- const { sql: right, bindings: rightBindings } = expressionToSql(predicate.right);
2284
+ if (predicate.right.type === ValueType.RelativeDate) {
2285
+ const dateFunc = predicate.right.hasTime ? 'datetime' : 'date';
2286
+ left = `${dateFunc}(${left})`;
2287
+ }
2288
+ const { sql: right, bindings: rightBindings } = expressionToSql(predicate.right, undefined, predicate.operator);
2277
2289
  let bindings = leftBindings.concat(rightBindings);
2278
2290
  if (predicate.operator === ComparisonOperator.eq &&
2279
2291
  predicate.right.type === ValueType.StringLiteral &&
@@ -2358,11 +2370,21 @@ function coerceToTargetDataType(initialSql, targetDataType) {
2358
2370
  return initialSql;
2359
2371
  }
2360
2372
  }
2361
- function expressionToSql(expression, targetDataType) {
2373
+ function expressionToSql(expression, targetDataType, operator) {
2362
2374
  switch (expression.type) {
2363
2375
  case ValueType.Extract: {
2376
+ // displayValue's for Booleans are special, they return null
2377
+ if (expression.subfield === 'displayValue' && targetDataType === 'Boolean') {
2378
+ return { sql: 'null', bindings: [] };
2379
+ }
2364
2380
  let path = extractPath(expression.field, expression.subfield);
2365
- let sql = `json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}')`;
2381
+ // For multiple picklist includes/excluding filtering, we need to prefix and suffix the field value with ';'
2382
+ // to make the match safe.
2383
+ // sample: field value: 'item12;item123', input value is 'item1'; they need to be converted to
2384
+ // ';item12;item123;' and '%;item1;%' first, then do sqlite like operation.
2385
+ let sql = operator === ComparisonOperator.includes || operator === ComparisonOperator.excludes
2386
+ ? `'${MultiPickListValueSeparator$1}' || json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}') || '${MultiPickListValueSeparator$1}'`
2387
+ : `json_extract("${expression.jsonAlias}.JSON", '${pathPrefix}.${path}')`;
2366
2388
  if (targetDataType !== undefined) {
2367
2389
  sql = coerceToTargetDataType(sql, targetDataType);
2368
2390
  }
@@ -2409,7 +2431,7 @@ function expressionToSql(expression, targetDataType) {
2409
2431
  case ValueType.StringLiteral:
2410
2432
  return stringLiteralToSql(expression);
2411
2433
  case ValueType.MultiPicklistSet:
2412
- return multiPicklistToSql$1(expression);
2434
+ return multiPicklistToSql$1(expression, operator);
2413
2435
  }
2414
2436
  }
2415
2437
  function stringLiteralToSql(string) {
@@ -2425,14 +2447,18 @@ function expressionArrayToSql(expressions, toSql) {
2425
2447
  const bindings = results.length > 0 ? results.map((v) => v.bindings).reduce(flatten$1) : [];
2426
2448
  return { sql, bindings };
2427
2449
  }
2428
- function multiPicklistToSql$1({ value }) {
2450
+ function multiPicklistToSql$1({ value }, operator) {
2429
2451
  // Individual multipicklist terms that delimited by semicolon are stored server-side
2430
2452
  // as lexically sorted strings and treated like logical ANDs. We can approximate this
2431
2453
  // behavior in SQL with wildcarded `LIKE` SQL operators. Terms with no delimiter can
2432
2454
  // be treated as string literals. Multiple terms are logically OR'd together to
2433
2455
  // match the behavior described in SOQL documentation (https://sfdc.co/c9j0r)
2456
+ // To make sure the match is safe for includes/excludes. the value is prefix and
2457
+ // suffix with ';', like 'abc' to '%;abc;%'. raw value for eq and ne.
2434
2458
  const sql = '?';
2435
- const binding = value && value.includes(';') ? `'%${value.split(';').join('%')}%'` : `'%${value}%'`;
2459
+ const binding = operator === ComparisonOperator.includes || operator === ComparisonOperator.excludes
2460
+ ? `%${MultiPickListValueSeparator$1}${value}${MultiPickListValueSeparator$1}%`
2461
+ : value;
2436
2462
  return { sql, bindings: [binding] };
2437
2463
  }
2438
2464
  function relativeDateToSql(expression) {
@@ -2503,8 +2529,12 @@ function comparisonDateLiteralToSql(leftOperand, operator, dateInput) {
2503
2529
  }
2504
2530
  }
2505
2531
  if (dateInput.type === ValueType.DateValue || dateInput.type === ValueType.DateTimeValue) {
2532
+ const dateFunction = dateInput.type === ValueType.DateTimeValue ? 'datetime' : 'date';
2506
2533
  const compOperator = comparisonOperatorToSql(operator);
2507
- return { sql: leftOperand + ` ${compOperator} ` + '?', bindings: [`${dateInput.value}`] };
2534
+ return {
2535
+ sql: leftOperand + ` ${compOperator} ` + `${dateFunction}(?)`,
2536
+ bindings: [`${dateInput.value}`],
2537
+ };
2508
2538
  }
2509
2539
  return { sql: '', bindings: [] };
2510
2540
  }
@@ -2735,15 +2765,41 @@ function fieldFilter(fieldName, fieldNode, alias, apiName, input, joins) {
2735
2765
  return dateRangeComparison(op.value, op.operator, extract);
2736
2766
  }
2737
2767
  if (op.type === 'MultiPicklistSetOperator') {
2768
+ const operator = op.operator === ComparisonOperator.includes
2769
+ ? CompoundOperator.or
2770
+ : CompoundOperator.and;
2771
+ const children = [];
2772
+ const length = op.value.value.length;
2773
+ for (let i = 0; i < length; i++) {
2774
+ const term = op.value.value[i];
2775
+ if (term !== null) {
2776
+ const splittedValue = term.split(MultiPickListValueSeparator$1);
2777
+ if (splittedValue.length === 1) {
2778
+ children.push(comparison(extract, op.operator, {
2779
+ type: ValueType.MultiPicklistSet,
2780
+ value: term,
2781
+ }));
2782
+ }
2783
+ else {
2784
+ children.push({
2785
+ type: PredicateType$1.compound,
2786
+ operator: op.operator === ComparisonOperator.includes
2787
+ ? CompoundOperator.and
2788
+ : CompoundOperator.or,
2789
+ children: splittedValue.map((singleValue) => {
2790
+ return comparison(extract, op.operator, {
2791
+ type: ValueType.MultiPicklistSet,
2792
+ value: singleValue,
2793
+ });
2794
+ }),
2795
+ });
2796
+ }
2797
+ }
2798
+ }
2738
2799
  return {
2739
2800
  type: PredicateType$1.compound,
2740
- operator: CompoundOperator.or,
2741
- children: op.value.value.map((term) => {
2742
- return comparison(extract, op.operator, {
2743
- type: ValueType.MultiPicklistSet,
2744
- value: term,
2745
- });
2746
- }),
2801
+ operator,
2802
+ children,
2747
2803
  };
2748
2804
  }
2749
2805
  if (op.type === 'StringSetOperator' && op.value.value.includes(null)) {
@@ -2807,8 +2863,6 @@ function dateRangeComparison(dateRange, operator, compareDate) {
2807
2863
  return comparison(compareDate, gte, dateRange.start);
2808
2864
  }
2809
2865
  }
2810
- const dateRegEx = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))$/;
2811
- const dateTimeRegEx = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))T(2[0-3]|[01][0-9]):[0-5][0-9]:[0-5][0-9](\.[0-9]{3})?Z$/;
2812
2866
  function dateFunctions(operatorNode, extract, dataType) {
2813
2867
  if (dataType !== 'Date' && dataType !== 'DateTime') {
2814
2868
  return success([]);
@@ -2886,7 +2940,7 @@ function isStringOperatorType(value) {
2886
2940
  return isScalarOperatorType(value) || value === like;
2887
2941
  }
2888
2942
  function isPicklistOperatorType(value) {
2889
- let values = [eq, ne];
2943
+ let values = [eq, ne, like, lt, gt, lte, gte];
2890
2944
  return values.includes(value);
2891
2945
  }
2892
2946
  function isCurrencyOperatorType(value) {
@@ -3237,8 +3291,11 @@ function operatorWithValue(operator, valueNode, objectInfoDataType) {
3237
3291
  message(`Comparison operator ${operator} is not supported for type ${objectInfoDataType}.`),
3238
3292
  ]);
3239
3293
  }
3294
+ function isValidDate(value) {
3295
+ return isNaN(Date.parse(value)) === false;
3296
+ }
3240
3297
  function dateInput(node) {
3241
- return parseDateNode(node, dateRegEx, false, 'YYYY-MM-DD').map((result) => {
3298
+ return parseDateNode(node, false, 'YYYY-MM-DD', isValidDate).map((result) => {
3242
3299
  switch (result.type) {
3243
3300
  case ValueType.NullValue:
3244
3301
  return result;
@@ -3252,7 +3309,7 @@ function dateInput(node) {
3252
3309
  });
3253
3310
  }
3254
3311
  function dateTimeInput(node) {
3255
- return parseDateNode(node, dateTimeRegEx, true, 'YYYY-MM-DDTHH:MM:SS.SSSZ or YYYY-MM-DDTHH:MM:SSZ').map((result) => {
3312
+ return parseDateNode(node, true, 'YYYY-MM-DDTHH:MM:SS.SSSZ, YYYY-MM-DDTHH:MM:SSZ, YYYY-MM-DDTHH:MM:SS.SSS+|-HH:MM, or YYYY-MM-DDTHH:MM:SS+|-HH:MM', isValidDate).map((result) => {
3256
3313
  switch (result.type) {
3257
3314
  case ValueType.NullValue:
3258
3315
  return result;
@@ -3277,7 +3334,7 @@ function parseNullValue(op) {
3277
3334
  }
3278
3335
  return failure(message(`Null can not be compared with ${op}`));
3279
3336
  }
3280
- function parseDateNode(node, regex, hasTime, dateFormat) {
3337
+ function parseDateNode(node, hasTime, dateFormat, isValidDate) {
3281
3338
  const typeName = hasTime ? 'DateTime' : 'Date';
3282
3339
  if (!isObjectValueNode$1(node)) {
3283
3340
  return failure(message(`Comparison value must be a ${typeName} input.`));
@@ -3285,7 +3342,12 @@ function parseDateNode(node, regex, hasTime, dateFormat) {
3285
3342
  const valueField = node.fields.value;
3286
3343
  if (valueField !== undefined) {
3287
3344
  if (is(valueField, 'StringValue')) {
3288
- if (valueField.value.match(regex)) {
3345
+ // check the date is valid
3346
+ // then make sure if it isnt suppose to contain time stamps that it doesnt
3347
+ // and if it should have a timestamp it should contain it
3348
+ const includesTimeStamp = valueField.value.includes('T');
3349
+ if (isValidDate(valueField.value) &&
3350
+ ((hasTime && includesTimeStamp) || (!hasTime && !includesTimeStamp))) {
3289
3351
  return success(stringLiteral(valueField.value));
3290
3352
  }
3291
3353
  return failure(message(`${typeName} format must be ${dateFormat}.`));
@@ -4277,7 +4339,20 @@ function makeStoreEval(preconditioner, objectInfoService, userId, contextProvide
4277
4339
  // await it here to normalize the input to a snapshot
4278
4340
  const nonEvaluatedSnapshot = await nonEvaluatedSnapshotOrPromise;
4279
4341
  // if the non-eval result has errors we want to return to caller
4280
- if (isErrorSnapshotThatShouldGetReturnedToCaller$1(nonEvaluatedSnapshot)) {
4342
+ const nonEvaluatedGQLSnapshot = nonEvaluatedSnapshot;
4343
+ if (isErrorSnapshotThatShouldGetReturnedToCaller$1(nonEvaluatedGQLSnapshot)) {
4344
+ const { data: gqlData } = nonEvaluatedGQLSnapshot;
4345
+ if (hasGraphQlErrors$1(gqlData) && gqlData !== undefined) {
4346
+ return {
4347
+ ...nonEvaluatedSnapshot,
4348
+ data: undefined,
4349
+ state: 'Error',
4350
+ error: {
4351
+ errorType: 'adapterError',
4352
+ error: gqlData.errors,
4353
+ },
4354
+ };
4355
+ }
4281
4356
  return nonEvaluatedSnapshot;
4282
4357
  }
4283
4358
  let rootQuery;
@@ -10948,6 +11023,10 @@ function makeEnvironmentUiApiRecordDraftAware(luvio, options, env) {
10948
11023
  return create$2(adapterSpecificEnvironments, {});
10949
11024
  }
10950
11025
 
11026
+ function clone(obj) {
11027
+ return parse$2(stringify$2(obj));
11028
+ }
11029
+
10951
11030
  const DEFAULT_FIELD_CREATED_BY_ID = 'CreatedById';
10952
11031
  const DEFAULT_FIELD_CREATED_DATE = 'CreatedDate';
10953
11032
  const DEFAULT_FIELD_ID = 'Id';
@@ -10975,7 +11054,9 @@ function replayDraftsOnRecord(record, draftMetadata) {
10975
11054
  // no baseRecord
10976
11055
  return undefined;
10977
11056
  }
10978
- const { recordOperations } = draftMetadata;
11057
+ const { recordOperations: originalOperations } = draftMetadata;
11058
+ // since replaying drafts is destructive, we need to clone the original operations
11059
+ const recordOperations = clone(originalOperations);
10979
11060
  if (recordOperations.length === 0) {
10980
11061
  return undefined;
10981
11062
  }
@@ -11006,7 +11087,11 @@ function recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations)
11006
11087
  }
11007
11088
  const draftActionType = draftOperation.type;
11008
11089
  if (draftActionType === 'create') {
11009
- throw Error('a create action cannot exist on an existing record');
11090
+ if (record.drafts !== undefined) {
11091
+ throw Error('a create action cannot exist on an existing draft record');
11092
+ }
11093
+ // the draft may have been uploaded already so skip the create operation and apply the rest (if any)
11094
+ return recursivelyApplyDraftsToRecord(record, draftMetadata, recordOperations);
11010
11095
  }
11011
11096
  // add the draft node
11012
11097
  if (record.drafts === undefined) {
@@ -11769,6 +11854,11 @@ function makeRecordDenormalizingDurableStore(luvio, durableStore, getStoreRecord
11769
11854
  let record = entry && entry.data;
11770
11855
  if (record === undefined) {
11771
11856
  record = storeRecords[recordKey];
11857
+ if (record === undefined) {
11858
+ // fields are being published without a record for them existing,
11859
+ // fields cannot exist standalone in the durable store
11860
+ continue;
11861
+ }
11772
11862
  }
11773
11863
  putRecords[recordId] = true;
11774
11864
  if (isStoreRecordError(record)) {
@@ -15636,4 +15726,4 @@ register({
15636
15726
  });
15637
15727
 
15638
15728
  export { getRuntime, registerReportObserver, reportGraphqlQueryParseError };
15639
- // version: 1.129.1-eb59a8867
15729
+ // version: 1.130.9-2d38b9869