@salesforce/lds-drafts 1.315.0 → 1.317.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/ldsDrafts.js CHANGED
@@ -4,10 +4,9 @@
4
4
  * For full license text, see the LICENSE.txt file
5
5
  */
6
6
 
7
- import { HttpStatusCode, StoreKeyMap } from '@luvio/engine';
8
- import { AsyncWorkerPool, uuidv4 } from '@salesforce/lds-utils-adapters';
9
- import ldsIdempotencyWriteDisabled from '@salesforce/gate/lds.idempotencyWriteDisabled';
10
- import ldsBackdatingEnabled from '@salesforce/gate/lds.backdatingEnabled';
7
+ import { HttpStatusCode } from '@luvio/engine';
8
+ import { AsyncWorkerPool } from '@salesforce/lds-utils-adapters';
9
+ export { uuidv4 } from '@salesforce/lds-utils-adapters';
11
10
 
12
11
  var DraftActionStatus;
13
12
  (function (DraftActionStatus) {
@@ -385,6 +384,11 @@ class DurableDraftQueue {
385
384
  this.userState = DraftQueueState.Stopped;
386
385
  this.uploadingActionId = undefined;
387
386
  this.timeoutHandler = undefined;
387
+ this.logger = typeof __nimbus !== 'undefined' &&
388
+ __nimbus.plugins !== undefined &&
389
+ __nimbus.plugins.JSLoggerPlugin !== undefined
390
+ ? __nimbus.plugins.JSLoggerPlugin
391
+ : undefined;
388
392
  this.handlers = {};
389
393
  this.draftStore = draftStore;
390
394
  this.workerPool = new AsyncWorkerPool(1);
@@ -425,12 +429,13 @@ class DurableDraftQueue {
425
429
  await this.notifyChangedListeners({
426
430
  type: DraftQueueEventType.QueueStateChanged,
427
431
  state: this.state,
432
+ draftCount: this.draftStore.getCount(),
428
433
  });
429
434
  const result = await this.processNextAction();
430
435
  switch (result) {
431
436
  case ProcessActionResult.BLOCKED_ON_ERROR:
432
437
  this.state = DraftQueueState.Error;
433
- return Promise.reject();
438
+ return Promise.reject('Unable to start queue - first action is in error state');
434
439
  default:
435
440
  return Promise.resolve();
436
441
  }
@@ -441,16 +446,22 @@ class DurableDraftQueue {
441
446
  // Do nothing if the queue state is already stopped
442
447
  return Promise.resolve();
443
448
  }
444
- this.stopQueueManually();
449
+ this.stopQueueManually(false);
445
450
  return this.notifyChangedListeners({
446
451
  type: DraftQueueEventType.QueueStateChanged,
447
452
  state: DraftQueueState.Stopped,
453
+ draftCount: this.draftStore.getCount(),
448
454
  });
449
455
  }
450
456
  /**
451
457
  * Used to stop the queue within DraftQueue without user interaction
452
458
  */
453
- stopQueueManually() {
459
+ stopQueueManually(internalReason) {
460
+ if (this.logger) {
461
+ this.logger.logInfo(internalReason
462
+ ? 'Draft queue stopped for internal reason'
463
+ : 'Draft queue stopped by app');
464
+ }
454
465
  if (this.timeoutHandler) {
455
466
  clearTimeout(this.timeoutHandler);
456
467
  this.timeoutHandler = undefined;
@@ -460,9 +471,6 @@ class DurableDraftQueue {
460
471
  async getQueueActions() {
461
472
  const drafts = (await this.draftStore.getAllDrafts());
462
473
  const queue = [];
463
- if (drafts === undefined) {
464
- return queue;
465
- }
466
474
  drafts.forEach((draft) => {
467
475
  if (draft.id === this.uploadingActionId) {
468
476
  draft.status = DraftActionStatus.Uploading;
@@ -493,6 +501,7 @@ class DurableDraftQueue {
493
501
  await this.notifyChangedListeners({
494
502
  type: DraftQueueEventType.ActionAdded,
495
503
  action: pendingAction,
504
+ draftCount: this.draftStore.getCount(),
496
505
  });
497
506
  await handler.handleActionEnqueued(pendingAction, queue);
498
507
  if (this.state === DraftQueueState.Started) {
@@ -525,6 +534,7 @@ class DurableDraftQueue {
525
534
  await this.notifyChangedListeners({
526
535
  type: DraftQueueEventType.ActionCompleted,
527
536
  action,
537
+ draftCount: this.draftStore.getCount(),
528
538
  });
529
539
  if (this.state === DraftQueueState.Started) {
530
540
  this.processNextAction();
@@ -574,6 +584,7 @@ class DurableDraftQueue {
574
584
  await this.notifyChangedListeners({
575
585
  type: DraftQueueEventType.ActionFailed,
576
586
  action: action,
587
+ draftCount: this.draftStore.getCount(),
577
588
  });
578
589
  return ProcessActionResult.BLOCKED_ON_ERROR;
579
590
  }
@@ -590,6 +601,7 @@ class DurableDraftQueue {
590
601
  await this.notifyChangedListeners({
591
602
  type: DraftQueueEventType.ActionUploading,
592
603
  action: { ...action, status: DraftActionStatus.Uploading },
604
+ draftCount: this.draftStore.getCount(),
593
605
  });
594
606
  return this.handle(action);
595
607
  }
@@ -611,6 +623,7 @@ class DurableDraftQueue {
611
623
  return this.notifyChangedListeners({
612
624
  type: DraftQueueEventType.ActionFailed,
613
625
  action: errorAction,
626
+ draftCount: this.draftStore.getCount(),
614
627
  });
615
628
  }
616
629
  async notifyChangedListeners(event) {
@@ -657,6 +670,7 @@ class DurableDraftQueue {
657
670
  await this.notifyChangedListeners({
658
671
  type: DraftQueueEventType.ActionDeleted,
659
672
  action,
673
+ draftCount: this.draftStore.getCount(),
660
674
  });
661
675
  if (this.userState === DraftQueueState.Started &&
662
676
  this.state !== DraftQueueState.Started &&
@@ -666,7 +680,7 @@ class DurableDraftQueue {
666
680
  }
667
681
  async updateDraftAction(action) {
668
682
  // stop queue manually
669
- this.stopQueueManually();
683
+ this.stopQueueManually(true);
670
684
  const actionStatus = await this.statusOfAction(action.id);
671
685
  if (actionStatus === DraftActionStatus.Uploading) {
672
686
  return Promise.reject('cannot update an uploading action');
@@ -696,7 +710,7 @@ class DurableDraftQueue {
696
710
  return this.replaceOrMergeActions(targetActionId, sourceActionId, true);
697
711
  }
698
712
  async retryAction(actionId) {
699
- this.stopQueueManually();
713
+ this.stopQueueManually(true);
700
714
  const actions = await this.getQueueActions();
701
715
  const target = actions[0];
702
716
  if (!target || target.id !== actionId) {
@@ -714,6 +728,7 @@ class DurableDraftQueue {
714
728
  await this.notifyChangedListeners({
715
729
  type: DraftQueueEventType.ActionUpdated,
716
730
  action: pendingAction,
731
+ draftCount: this.draftStore.getCount(),
717
732
  });
718
733
  await this.startQueueSafe();
719
734
  return pendingAction;
@@ -739,6 +754,7 @@ class DurableDraftQueue {
739
754
  await this.notifyChangedListeners({
740
755
  type: DraftQueueEventType.ActionUpdated,
741
756
  action: action,
757
+ draftCount: this.draftStore.getCount(),
742
758
  });
743
759
  return action;
744
760
  }
@@ -746,6 +762,7 @@ class DurableDraftQueue {
746
762
  await this.notifyChangedListeners({
747
763
  type: DraftQueueEventType.QueueStateChanged,
748
764
  state: DraftQueueState.Waiting,
765
+ draftCount: this.draftStore.getCount(),
749
766
  });
750
767
  this.timeoutHandler = setTimeout(() => {
751
768
  if (this.state !== DraftQueueState.Stopped) {
@@ -804,7 +821,7 @@ class DurableDraftQueue {
804
821
  if (this.replacingAction !== undefined) {
805
822
  throw Error('Cannot replace/merge actions while a replace/merge action operation is in progress.');
806
823
  }
807
- this.stopQueueManually();
824
+ this.stopQueueManually(true);
808
825
  const promise = this.getActionsForReplaceOrMerge(targetActionId, sourceActionId).then(async ({ target, source }) => {
809
826
  // put in a try/finally block so we don't leave this.replacingAction
810
827
  // indefinitely set
@@ -820,6 +837,7 @@ class DurableDraftQueue {
820
837
  await this.notifyChangedListeners({
821
838
  type: DraftQueueEventType.ActionUpdated,
822
839
  action: updatedTarget,
840
+ draftCount: this.draftStore.getCount(),
823
841
  });
824
842
  // remove the source from queue
825
843
  await this.removeDraftAction(sourceActionId);
@@ -958,6 +976,9 @@ class DurableDraftStore {
958
976
  };
959
977
  return this.enqueueAction(action);
960
978
  }
979
+ getCount() {
980
+ return keys(this.draftStore).length;
981
+ }
961
982
  /**
962
983
  * Runs a write operation against the draft store, if the initial
963
984
  * revive is still in progress, the action gets enqueued to run once the
@@ -1001,23 +1022,15 @@ class DurableDraftStore {
1001
1022
  for (let i = 0, len = keys$1.length; i < len; i++) {
1002
1023
  const entry = durableEntries[keys$1[i]];
1003
1024
  const action = entry.data;
1004
- if (action !== undefined) {
1005
- if (process.env.NODE_ENV !== 'production') {
1006
- // the `version` property was introduced in 242, we should assert version
1007
- // exists once we are sure there are no durable stores that contain
1008
- // versionless actions
1009
- if (action.version && action.version !== '242.0.0') {
1010
- return Promise.reject('Unexpected draft action version found in the durable store');
1011
- }
1012
- }
1013
- draftStore[action.id] = action;
1014
- }
1015
- else {
1016
- if (process.env.NODE_ENV !== 'production') {
1017
- const err = new Error('Expected draft action to be defined in the durable store');
1018
- return Promise.reject(err);
1025
+ if (process.env.NODE_ENV !== 'production') {
1026
+ // the `version` property was introduced in 242, we should assert version
1027
+ // exists once we are sure there are no durable stores that contain
1028
+ // versionless actions
1029
+ if (action.version && action.version !== '242.0.0') {
1030
+ return Promise.reject('Unexpected draft action version found in the durable store');
1019
1031
  }
1020
1032
  }
1033
+ draftStore[action.id] = action;
1021
1034
  }
1022
1035
  return this.runQueuedOperations();
1023
1036
  })
@@ -1053,494 +1066,6 @@ class DurableDraftStore {
1053
1066
  }
1054
1067
  }
1055
1068
 
1056
- const HTTP_HEADER_RETRY_AFTER = 'Retry-After';
1057
- const HTTP_HEADER_IDEMPOTENCY_KEY = 'Idempotency-Key';
1058
- const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = 'IDEMPOTENCY_FEATURE_NOT_ENABLED';
1059
- const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = 'IDEMPOTENCY_NOT_SUPPORTED';
1060
- const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = 'IDEMPOTENCY_KEY_USED_DIFFERENT_USER';
1061
- const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = 'IDEMPOTENCY_CONCURRENT_REQUEST';
1062
- const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = 'IDEMPOTENCY_KEY_ALREADY_USED';
1063
- const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = 'IDEMPOTENCY_BACKEND_OPERATION_ERROR';
1064
- /**
1065
- * Get the retry after in milliseconds from the response headers, undefined if not specified.
1066
- * The header could have two different format.
1067
- * Retry-After: <http-date>, like Wed, 21 Oct 2015 07:28:00 GMT
1068
- * Retry-After: <delay-seconds>, like 1.5s
1069
- * @param headers http headers
1070
- * @returns the time to delat in millisconds.
1071
- */
1072
- function getRetryAfterInMs(headers) {
1073
- const retryAfterHeader = headers && headers[HTTP_HEADER_RETRY_AFTER];
1074
- if (retryAfterHeader === undefined) {
1075
- return undefined;
1076
- }
1077
- const delayInSecond = parseFloat(retryAfterHeader);
1078
- if (retryAfterHeader === delayInSecond.toString()) {
1079
- return Math.round(delayInSecond * 1000);
1080
- }
1081
- const delayUntilDateTime = Date.parse(retryAfterHeader);
1082
- if (isNaN(delayUntilDateTime)) {
1083
- return undefined;
1084
- }
1085
- return delayUntilDateTime - Date.now();
1086
- }
1087
-
1088
- const DEFAULT_FIELD_LAST_MODIFIED_DATE = 'LastModifiedDate';
1089
- const DEFAULT_FIELD_CREATED_DATE = 'CreatedDate';
1090
- class AbstractResourceRequestActionHandler {
1091
- constructor(draftQueue, networkAdapter, getLuvio) {
1092
- this.draftQueue = draftQueue;
1093
- this.networkAdapter = networkAdapter;
1094
- this.getLuvio = getLuvio;
1095
- // NOTE[W-12567340]: This property stores in-memory mappings between draft
1096
- // ids and canonical ids for the current session. Having a local copy of
1097
- // these mappings is necessary to avoid a race condition between publishing
1098
- // new mappings to the durable store and those mappings being loaded into
1099
- // the luvio store redirect table, during which a new draft might be enqueued
1100
- // which would not see a necessary mapping.
1101
- this.ephemeralRedirects = {};
1102
- // determined by Server setup.
1103
- this.isIdempotencySupported = true;
1104
- // idempotency write flag set by lds
1105
- this.isLdsIdempotencyWriteDisabled = ldsIdempotencyWriteDisabled.isOpen({
1106
- fallback: false,
1107
- });
1108
- this.isBackdatingEnabled = ldsBackdatingEnabled.isOpen({ fallback: false });
1109
- }
1110
- enqueue(data) {
1111
- return this.draftQueue.enqueue(this.handlerId, data);
1112
- }
1113
- async handleAction(action, actionCompleted, actionErrored) {
1114
- const { data: request } = action;
1115
- // no context is stored in draft action
1116
- try {
1117
- const response = await this.networkAdapter(request, {});
1118
- if (response.ok) {
1119
- await actionCompleted({
1120
- ...action,
1121
- response,
1122
- status: DraftActionStatus.Completed,
1123
- });
1124
- return ProcessActionResult.ACTION_SUCCEEDED;
1125
- }
1126
- let shouldRetry = false;
1127
- let retryDelayInMs = undefined;
1128
- let actionDataChanged = false;
1129
- let updatedAction = action;
1130
- if (request && request.headers && request.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
1131
- const status = response.status;
1132
- switch (status) {
1133
- case 408 /* IdempotentWriteSpecificHttpStatusCode.RequestTimeout */:
1134
- case 503 /* IdempotentWriteSpecificHttpStatusCode.ServiceUnavailable */:
1135
- retryDelayInMs = getRetryAfterInMs(response.headers);
1136
- shouldRetry = true;
1137
- break;
1138
- case HttpStatusCode.ServerError: {
1139
- shouldRetry = true;
1140
- if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR)) {
1141
- this.isIdempotencySupported = false;
1142
- retryDelayInMs = 0;
1143
- actionDataChanged = true;
1144
- }
1145
- break;
1146
- }
1147
- case 409 /* IdempotentWriteSpecificHttpStatusCode.Conflict */: {
1148
- if (this.isUiApiErrors(response.body)) {
1149
- const errorCode = response.body[0].errorCode;
1150
- if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER)) {
1151
- retryDelayInMs = 0;
1152
- actionDataChanged = true;
1153
- }
1154
- else if (errorCode === ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST) {
1155
- retryDelayInMs = getRetryAfterInMs(response.headers);
1156
- }
1157
- shouldRetry = true;
1158
- }
1159
- break;
1160
- }
1161
- case HttpStatusCode.BadRequest: {
1162
- if (this.handleIdempotencyServerError(response.body, updatedAction, false, ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED, ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED)) {
1163
- retryDelayInMs = 0;
1164
- actionDataChanged = true;
1165
- shouldRetry = true;
1166
- }
1167
- break;
1168
- }
1169
- case 422 /* IdempotentWriteSpecificHttpStatusCode.UnProcessableEntity */: {
1170
- if (this.handleIdempotencyServerError(response.body, updatedAction, true, ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED)) {
1171
- retryDelayInMs = 0;
1172
- actionDataChanged = true;
1173
- shouldRetry = true;
1174
- }
1175
- break;
1176
- }
1177
- }
1178
- }
1179
- if (this.isBackdatingEnabled &&
1180
- response.status === HttpStatusCode.BadRequest &&
1181
- this.isBackdatingError(response.body, action)) {
1182
- updatedAction.timestamp = Date.now();
1183
- updatedAction.data.body.fields = {
1184
- ...updatedAction.data.body.fields,
1185
- LastModifiedDate: new Date(updatedAction.timestamp).toISOString(),
1186
- };
1187
- if (this.hasIdempotencySupport() &&
1188
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
1189
- updatedAction.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
1190
- }
1191
- shouldRetry = true;
1192
- actionDataChanged = true;
1193
- }
1194
- await actionErrored(shouldRetry
1195
- ? updatedAction
1196
- : {
1197
- ...updatedAction,
1198
- error: response,
1199
- status: DraftActionStatus.Error,
1200
- }, shouldRetry, retryDelayInMs, actionDataChanged);
1201
- return ProcessActionResult.ACTION_ERRORED;
1202
- }
1203
- catch (e) {
1204
- await actionErrored(action, true);
1205
- return ProcessActionResult.NETWORK_ERROR;
1206
- }
1207
- }
1208
- // true if response is an idempotency server error. updates or deletes idempotency key if the reponse is idempotency related error. Idempotency related error is in format of UiApiError array.
1209
- handleIdempotencyServerError(responseBody, action, updateIdempotencyKey, ...targetErrorCodes) {
1210
- if (this.isUiApiErrors(responseBody)) {
1211
- const errorCode = responseBody[0].errorCode;
1212
- if (targetErrorCodes.includes(errorCode)) {
1213
- action.data.headers = action.data.headers || {};
1214
- if (updateIdempotencyKey) {
1215
- action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
1216
- }
1217
- else {
1218
- delete action.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY];
1219
- }
1220
- return true;
1221
- }
1222
- }
1223
- return false;
1224
- }
1225
- // checks if the body is an array of UiApiError. Sometimes the body has `enhancedErrorType` field as an error indicator(one example is the field validation failure). In such case Action being processed updates to an Error Action.
1226
- isUiApiErrors(body) {
1227
- return body !== undefined && isArray(body) && body.length > 0 && body[0].errorCode;
1228
- }
1229
- isBackdatingError(body, action) {
1230
- if (body.enhancedErrorType &&
1231
- body.enhancedErrorType === 'RecordError' &&
1232
- body.output &&
1233
- body.output.errors &&
1234
- isArray(body.output.errors) &&
1235
- body.output.errors.length > 0 &&
1236
- action.data.body &&
1237
- action.data.body.fields &&
1238
- action.data.body.fields[DEFAULT_FIELD_LAST_MODIFIED_DATE]) {
1239
- return body.output.errors.some((error) => error.errorCode === 'CollisionDetectedException');
1240
- }
1241
- return false;
1242
- }
1243
- async buildPendingAction(request, queue) {
1244
- const targetId = await this.getIdFromRequest(request);
1245
- const tag = this.buildTagForTargetId(targetId);
1246
- const handlerActions = queue.filter((x) => x.handler === this.handlerId);
1247
- if (request.method === 'post' && actionsForTag(tag, handlerActions).length > 0) {
1248
- return Promise.reject(new Error('Cannot enqueue a POST draft action with an existing tag'));
1249
- }
1250
- if (deleteActionsForTag(tag, handlerActions).length > 0) {
1251
- return Promise.reject(new Error('Cannot enqueue a draft action for a deleted record'));
1252
- }
1253
- return {
1254
- handler: this.handlerId,
1255
- targetId,
1256
- tag,
1257
- data: request,
1258
- status: DraftActionStatus.Pending,
1259
- id: generateUniqueDraftActionId(queue.map((x) => x.id)),
1260
- timestamp: Date.now(),
1261
- metadata: {},
1262
- version: '242.0.0',
1263
- };
1264
- }
1265
- async handleActionEnqueued(_action) { }
1266
- handleActionRemoved(action) {
1267
- return this.reingestRecord(action);
1268
- }
1269
- getQueueOperationsForCompletingDrafts(queue, action) {
1270
- const queueOperations = [];
1271
- const redirects = this.getRedirectMappings(action);
1272
- if (redirects !== undefined) {
1273
- const { length } = queue;
1274
- for (let i = 0; i < length; i++) {
1275
- const queueAction = queue[i];
1276
- // if this queueAction is the action that is completing we can move on,
1277
- // it is about to be deleted and won't have the draft ID in it
1278
- if (queueAction.id === action.id) {
1279
- continue;
1280
- }
1281
- if (isResourceRequestAction(queueAction)) {
1282
- let queueOperationMutated = false;
1283
- let updatedActionTag = undefined;
1284
- let updatedActionTargetId = undefined;
1285
- const { tag: queueActionTag, data: queueActionRequest, id: queueActionId, } = queueAction;
1286
- let { basePath, body } = queueActionRequest;
1287
- let stringifiedBody = stringify(body);
1288
- // for each redirected ID/key we loop over the operation to see if it needs
1289
- // to be updated
1290
- for (const { draftId, draftKey, canonicalId, canonicalKey } of redirects) {
1291
- if (basePath.search(draftId) >= 0 || stringifiedBody.search(draftId) >= 0) {
1292
- basePath = basePath.replace(draftId, canonicalId);
1293
- stringifiedBody = stringifiedBody.replace(draftId, canonicalId);
1294
- queueOperationMutated = true;
1295
- }
1296
- // if the action is performed on a previous draft id, we need to replace the action
1297
- // with a new one at the updated canonical key
1298
- if (queueActionTag === draftKey) {
1299
- updatedActionTag = canonicalKey;
1300
- updatedActionTargetId = canonicalId;
1301
- }
1302
- }
1303
- if (queueOperationMutated) {
1304
- if (updatedActionTag !== undefined && updatedActionTargetId !== undefined) {
1305
- const updatedAction = {
1306
- ...queueAction,
1307
- tag: updatedActionTag,
1308
- targetId: updatedActionTargetId,
1309
- data: {
1310
- ...queueActionRequest,
1311
- basePath: basePath,
1312
- body: parse(stringifiedBody),
1313
- },
1314
- };
1315
- // item needs to be replaced with a new item at the new record key
1316
- queueOperations.push({
1317
- type: QueueOperationType.Delete,
1318
- id: queueActionId,
1319
- });
1320
- queueOperations.push({
1321
- type: QueueOperationType.Add,
1322
- action: updatedAction,
1323
- });
1324
- }
1325
- else {
1326
- const updatedAction = {
1327
- ...queueAction,
1328
- data: {
1329
- ...queueActionRequest,
1330
- basePath: basePath,
1331
- body: parse(stringifiedBody),
1332
- },
1333
- };
1334
- // item needs to be updated
1335
- queueOperations.push({
1336
- type: QueueOperationType.Update,
1337
- id: queueActionId,
1338
- action: updatedAction,
1339
- });
1340
- }
1341
- }
1342
- }
1343
- }
1344
- }
1345
- // delete completed action
1346
- queueOperations.push({
1347
- type: QueueOperationType.Delete,
1348
- id: action.id,
1349
- });
1350
- return queueOperations;
1351
- }
1352
- getRedirectMappings(action) {
1353
- if (action.data.method !== 'post') {
1354
- return undefined;
1355
- }
1356
- const body = action.response.body;
1357
- const canonicalId = this.getIdFromResponseBody(body);
1358
- const draftId = action.targetId;
1359
- if (draftId !== undefined && canonicalId !== undefined && draftId !== canonicalId) {
1360
- this.ephemeralRedirects[draftId] = canonicalId;
1361
- }
1362
- return [
1363
- {
1364
- draftId,
1365
- canonicalId,
1366
- draftKey: this.buildTagForTargetId(draftId),
1367
- canonicalKey: this.buildTagForTargetId(canonicalId),
1368
- },
1369
- ];
1370
- }
1371
- async handleActionCompleted(action, queueOperations, allHandlers) {
1372
- const { data: request, tag } = action;
1373
- const { method } = request;
1374
- if (method === 'delete') {
1375
- return this.evictKey(tag);
1376
- }
1377
- const recordsToIngest = [];
1378
- recordsToIngest.push({
1379
- response: action.response.body,
1380
- synchronousIngest: this.synchronousIngest.bind(this),
1381
- buildCacheKeysForResponse: this.buildCacheKeysFromResponse.bind(this),
1382
- });
1383
- const recordsNeedingReplay = queueOperations.filter((x) => x.type === QueueOperationType.Update);
1384
- for (const recordNeedingReplay of recordsNeedingReplay) {
1385
- const { action } = recordNeedingReplay;
1386
- if (isResourceRequestAction(action)) {
1387
- // We can't assume the queue operation is for our handler, have to find the handler.
1388
- const handler = allHandlers.find((h) => h.handlerId === action.handler);
1389
- if (handler !== undefined) {
1390
- const record = await handler.getDataForAction(action);
1391
- if (record !== undefined) {
1392
- recordsToIngest.push({
1393
- response: record,
1394
- synchronousIngest: handler.synchronousIngest.bind(handler),
1395
- buildCacheKeysForResponse: handler.buildCacheKeysFromResponse.bind(handler),
1396
- });
1397
- }
1398
- }
1399
- }
1400
- }
1401
- await this.ingestResponses(recordsToIngest, action);
1402
- }
1403
- handleReplaceAction(targetAction, sourceAction) {
1404
- //reject if the action to replace is a POST action
1405
- const pendingAction = targetAction;
1406
- if (pendingAction.data.method === 'post') {
1407
- throw Error('Cannot replace a POST action');
1408
- }
1409
- if (this.isActionOfType(targetAction) &&
1410
- this.isActionOfType(sourceAction)) {
1411
- targetAction.status = DraftActionStatus.Pending;
1412
- targetAction.data = sourceAction.data;
1413
- return targetAction;
1414
- }
1415
- else {
1416
- throw Error('Incompatible Action types to replace one another');
1417
- }
1418
- }
1419
- mergeActions(targetAction, sourceAction) {
1420
- const { id: targetId, data: targetData, metadata: targetMetadata, timestamp: targetTimestamp, } = targetAction;
1421
- const { method: targetMethod, body: targetBody } = targetData;
1422
- const { data: sourceData, metadata: sourceMetadata } = sourceAction;
1423
- const { method: sourceMethod, body: sourceBody } = sourceData;
1424
- if (targetMethod.toLowerCase() === 'delete' || sourceMethod.toLowerCase() === 'delete') {
1425
- throw Error('Cannot merge DELETE actions.');
1426
- }
1427
- if (targetMethod.toLowerCase() === 'patch' && sourceMethod.toLowerCase() === 'post') {
1428
- // overlaying a POST over a PATCH is not supported
1429
- throw Error('Cannot merge a POST action over top of a PATCH action.');
1430
- }
1431
- // overlay top-level properties, maintain target's timestamp and id
1432
- const merged = {
1433
- ...targetAction,
1434
- ...sourceAction,
1435
- timestamp: targetTimestamp,
1436
- id: targetId,
1437
- };
1438
- // overlay data
1439
- // NOTE: we stick to the target's ResourceRequest properties (except body
1440
- // which is merged) because we don't want to overwrite a POST with a PATCH
1441
- // (all other cases should be fine or wouldn't have passed pre-requisites)
1442
- merged.data = {
1443
- ...targetData,
1444
- body: this.mergeRequestBody(targetBody, sourceBody),
1445
- };
1446
- // Updates Idempotency key if target has one
1447
- if (targetData.headers && targetData.headers[HTTP_HEADER_IDEMPOTENCY_KEY]) {
1448
- merged.data.headers[HTTP_HEADER_IDEMPOTENCY_KEY] = uuidv4();
1449
- }
1450
- // overlay metadata
1451
- merged.metadata = { ...targetMetadata, ...sourceMetadata };
1452
- // put status back to pending to auto upload if queue is active and targed is at the head.
1453
- merged.status = DraftActionStatus.Pending;
1454
- return merged;
1455
- }
1456
- shouldDeleteActionByTagOnRemoval(action) {
1457
- return action.data.method === 'post';
1458
- }
1459
- updateMetadata(_existingMetadata, incomingMetadata) {
1460
- return incomingMetadata;
1461
- }
1462
- isActionOfType(action) {
1463
- return action.handler === this.handlerId;
1464
- }
1465
- async reingestRecord(action) {
1466
- const record = await this.getDataForAction(action);
1467
- if (record !== undefined) {
1468
- await this.ingestResponses([
1469
- {
1470
- response: record,
1471
- synchronousIngest: this.synchronousIngest.bind(this),
1472
- buildCacheKeysForResponse: this.buildCacheKeysFromResponse.bind(this),
1473
- },
1474
- ], action);
1475
- }
1476
- else {
1477
- await this.evictKey(action.tag);
1478
- }
1479
- }
1480
- // Given an action for this handler this method should return the draft IDs.
1481
- // Most of the time it will simply be the targetId, but certain handlers might
1482
- // have multiple draft-created IDs so they would override this to return them.
1483
- getDraftIdsFromAction(action) {
1484
- return [action.targetId];
1485
- }
1486
- hasIdempotencySupport() {
1487
- return this.isIdempotencySupported && !this.isLdsIdempotencyWriteDisabled;
1488
- }
1489
- async ingestResponses(responses, action) {
1490
- const luvio = this.getLuvio();
1491
- await luvio.handleSuccessResponse(() => {
1492
- if (action.status === DraftActionStatus.Completed) {
1493
- const mappings = this.getRedirectMappings(action);
1494
- if (mappings) {
1495
- mappings.forEach((mapping) => {
1496
- luvio.storeRedirect(mapping.draftKey, mapping.canonicalKey);
1497
- });
1498
- }
1499
- }
1500
- for (const entry of responses) {
1501
- const { response, synchronousIngest } = entry;
1502
- synchronousIngest(response, action);
1503
- }
1504
- return luvio.storeBroadcast();
1505
- },
1506
- // getTypeCacheKeysRecord uses the response, not the full path factory
1507
- // so 2nd parameter will be unused
1508
- () => {
1509
- const keySet = new StoreKeyMap();
1510
- for (const entry of responses) {
1511
- const { response, buildCacheKeysForResponse } = entry;
1512
- const set = buildCacheKeysForResponse(response);
1513
- for (const key of set.keys()) {
1514
- const value = set.get(key);
1515
- if (value !== undefined) {
1516
- keySet.set(key, value);
1517
- }
1518
- }
1519
- }
1520
- return keySet;
1521
- });
1522
- }
1523
- async evictKey(key) {
1524
- const luvio = this.getLuvio();
1525
- await luvio.handleSuccessResponse(() => {
1526
- luvio.storeEvict(key);
1527
- return luvio.storeBroadcast();
1528
- }, () => {
1529
- return new StoreKeyMap();
1530
- });
1531
- }
1532
- }
1533
- function actionsForTag(tag, queue) {
1534
- return queue.filter((action) => action.tag === tag);
1535
- }
1536
- function deleteActionsForTag(tag, queue) {
1537
- return queue.filter((action) => action.tag === tag && action.data.method === 'delete');
1538
- }
1539
- function isResourceRequestAction(action) {
1540
- const dataAsAny = action.data;
1541
- return (dataAsAny !== undefined && dataAsAny.method !== undefined && dataAsAny.body !== undefined);
1542
- }
1543
-
1544
1069
  /**
1545
1070
  * Denotes what kind of operation a DraftQueueItem represents.
1546
1071
  */
@@ -1761,16 +1286,17 @@ class DraftManager {
1761
1286
  return Promise.reject('cannot edit incompatible action type or uploading actions');
1762
1287
  }
1763
1288
  action.data.body.fields = { ...action.data.body.fields, ...fields };
1289
+ action.status = DraftActionStatus.Pending;
1764
1290
  await this.draftQueue.updateDraftAction(action);
1765
1291
  return this.buildDraftQueueItem(action);
1766
1292
  }
1767
1293
  isValidFieldMap(fields) {
1768
1294
  const keys$1 = keys(fields);
1769
- const validTypes = ['string', 'number', 'null', 'boolean'];
1295
+ const validTypes = ['string', 'number', 'boolean'];
1770
1296
  for (let i = 0; i < keys$1.length; i++) {
1771
1297
  const key = keys$1[i];
1772
1298
  const value = fields[key];
1773
- if (!validTypes.includes(typeof value)) {
1299
+ if (!validTypes.includes(typeof value) && value !== null) {
1774
1300
  return false;
1775
1301
  }
1776
1302
  }
@@ -1798,6 +1324,7 @@ class DraftManager {
1798
1324
  }
1799
1325
  const data = action.data;
1800
1326
  data.body.fields = { ...data.body.fields, ...fields };
1327
+ action.status = DraftActionStatus.Pending;
1801
1328
  await this.draftQueue.updateDraftAction(action);
1802
1329
  return this.buildDraftQueueItem(action);
1803
1330
  }
@@ -1914,6 +1441,10 @@ class DraftManager {
1914
1441
  });
1915
1442
  }
1916
1443
  }
1444
+ function isResourceRequestAction(action) {
1445
+ const dataAsAny = action.data;
1446
+ return (dataAsAny !== undefined && dataAsAny.method !== undefined && dataAsAny.body !== undefined);
1447
+ }
1917
1448
 
1918
1449
  function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueue) {
1919
1450
  const draftMetadata = {};
@@ -2005,4 +1536,4 @@ function makeEnvironmentDraftAware(luvio, env, durableStore, handlers, draftQueu
2005
1536
  });
2006
1537
  }
2007
1538
 
2008
- export { AbstractResourceRequestActionHandler, CustomActionResultType, DEFAULT_FIELD_CREATED_DATE, DEFAULT_FIELD_LAST_MODIFIED_DATE, DRAFT_ERROR_CODE, DRAFT_ID_MAPPINGS_SEGMENT, DRAFT_SEGMENT, DraftActionOperationType, DraftActionStatus, DraftErrorFetchResponse, DraftFetchResponse, DraftManager, DraftQueueEventType, DraftQueueState, DraftSynthesisError, DurableDraftQueue, DurableDraftStore, ProcessActionResult, createBadRequestResponse, createDeletedResponse, createDraftSynthesisErrorResponse, createInternalErrorResponse, createNotFoundResponse, createOkResponse, generateUniqueDraftActionId, isDraftSynthesisError, makeEnvironmentDraftAware, transformErrorToDraftSynthesisError };
1539
+ export { CustomActionResultType, DRAFT_ERROR_CODE, DRAFT_ID_MAPPINGS_SEGMENT, DRAFT_SEGMENT, DraftActionOperationType, DraftActionStatus, DraftErrorFetchResponse, DraftFetchResponse, DraftManager, DraftQueueEventType, DraftQueueState, DraftSynthesisError, DurableDraftQueue, DurableDraftStore, ProcessActionResult, QueueOperationType, createBadRequestResponse, createDeletedResponse, createDraftSynthesisErrorResponse, createInternalErrorResponse, createNotFoundResponse, createOkResponse, generateUniqueDraftActionId, isDraftSynthesisError, makeEnvironmentDraftAware, transformErrorToDraftSynthesisError };
@@ -103,31 +103,36 @@ export declare enum DraftQueueEventType {
103
103
  */
104
104
  QueueStateChanged = "state"
105
105
  }
106
- export interface DraftQueueAddEvent {
106
+ export interface DraftQueueStats {
107
+ draftCount: number;
108
+ }
109
+ export interface DraftQueueAddEvent extends DraftQueueStats {
107
110
  type: DraftQueueEventType.ActionAdded;
108
111
  action: PendingDraftAction<unknown>;
109
112
  }
110
- export interface DraftQueueUploadingEvent {
113
+ export interface DraftQueueUploadingEvent extends DraftQueueStats {
111
114
  type: DraftQueueEventType.ActionUploading;
112
115
  action: UploadingDraftAction<unknown>;
116
+ draftCount: number;
113
117
  }
114
- export interface DraftQueueDeleteEvent {
118
+ export interface DraftQueueDeleteEvent extends DraftQueueStats {
115
119
  type: DraftQueueEventType.ActionDeleted;
116
120
  action: DraftAction<unknown, unknown>;
121
+ draftCount: number;
117
122
  }
118
- export interface DraftQueueCompleteEvent {
123
+ export interface DraftQueueCompleteEvent extends DraftQueueStats {
119
124
  type: DraftQueueEventType.ActionCompleted;
120
125
  action: CompletedDraftAction<unknown, unknown>;
121
126
  }
122
- export interface DraftQueueActionFailedEvent {
127
+ export interface DraftQueueActionFailedEvent extends DraftQueueStats {
123
128
  type: DraftQueueEventType.ActionFailed;
124
129
  action: ErrorDraftAction<unknown>;
125
130
  }
126
- export interface DraftQueueActionUpdatedEvent {
131
+ export interface DraftQueueActionUpdatedEvent extends DraftQueueStats {
127
132
  type: DraftQueueEventType.ActionUpdated;
128
133
  action: DraftAction<unknown, unknown>;
129
134
  }
130
- export interface DraftQueueStateChangedEvent {
135
+ export interface DraftQueueStateChangedEvent extends DraftQueueStats {
131
136
  type: DraftQueueEventType.QueueStateChanged;
132
137
  state: DraftQueueState;
133
138
  }
@@ -8,4 +8,5 @@ export interface DraftStore {
8
8
  deleteDraft(actionId: string): Promise<void>;
9
9
  deleteByTag(tag: string): Promise<void>;
10
10
  completeAction(queueOperations: QueueOperation[]): Promise<void>;
11
+ getCount(): number;
11
12
  }
@@ -16,6 +16,7 @@ export declare class DurableDraftQueue implements DraftQueue {
16
16
  private replacingAction?;
17
17
  private uploadingActionId?;
18
18
  private timeoutHandler;
19
+ private logger;
19
20
  private workerPool;
20
21
  private handlers;
21
22
  private getHandler;
@@ -21,6 +21,7 @@ export declare class DurableDraftStore implements DraftStore {
21
21
  deleteDraft(id: string): Promise<void>;
22
22
  deleteByTag(tag: string): Promise<void>;
23
23
  completeAction(queueOperations: QueueOperation[]): Promise<void>;
24
+ getCount(): number;
24
25
  /**
25
26
  * Runs a write operation against the draft store, if the initial
26
27
  * revive is still in progress, the action gets enqueued to run once the
@@ -1,14 +1,13 @@
1
- export { DraftQueue, DraftQueueState, DraftAction, ErrorDraftAction, PendingDraftAction, CompletedDraftAction, DraftActionStatus, ProcessActionResult, DraftQueueChangeListener, DraftActionMetadata, DraftQueueEventType, QueueOperation, } from './DraftQueue';
1
+ export { DraftQueue, DraftQueueState, DraftAction, ErrorDraftAction, PendingDraftAction, CompletedDraftAction, DraftActionStatus, ProcessActionResult, DraftQueueChangeListener, DraftActionMetadata, DraftQueueEventType, QueueOperation, UpdateQueueOperation, QueueOperationType, } from './DraftQueue';
2
2
  export { DRAFT_ID_MAPPINGS_SEGMENT, DraftKeyMapping } from './DraftIdMapping';
3
3
  export { DurableDraftQueue, DRAFT_SEGMENT } from './DurableDraftQueue';
4
- export { generateUniqueDraftActionId } from './utils/id';
4
+ export { generateUniqueDraftActionId, uuidv4 } from './utils/id';
5
5
  export { DurableDraftStore } from './DurableDraftStore';
6
6
  export { DraftStore } from './DraftStore';
7
7
  export { DraftManager, DraftManagerState, DraftActionOperationType, DraftQueueItem, DraftQueueItemMetadata, } from './DraftManager';
8
8
  export { ActionHandler, ReplacingActions, DraftIdAndKeyMapping, } from './actionHandlers/ActionHandler';
9
9
  export type { CustomActionResult } from './actionHandlers/CustomActionHandler';
10
10
  export { CustomActionResultType, CustomActionExecutor } from './actionHandlers/CustomActionHandler';
11
- export { AbstractResourceRequestActionHandler, ResponseIngestionEntry, DEFAULT_FIELD_CREATED_DATE, DEFAULT_FIELD_LAST_MODIFIED_DATE, } from './actionHandlers/AbstractResourceRequestActionHandler';
12
11
  export { makeEnvironmentDraftAware } from './makeEnvironmentDraftAware';
13
12
  export type { DraftAwareEnvironment } from './makeEnvironmentDraftAware';
14
13
  export * from './DraftFetchResponse';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/lds-drafts",
3
- "version": "1.315.0",
3
+ "version": "1.317.0",
4
4
  "license": "SEE LICENSE IN LICENSE.txt",
5
5
  "description": "LDS Drafts",
6
6
  "main": "dist/ldsDrafts.js",
@@ -26,7 +26,10 @@
26
26
  "dependencies": {
27
27
  "@luvio/engine": "0.156.4",
28
28
  "@luvio/environments": "0.156.4",
29
- "@salesforce/lds-utils-adapters": "^1.315.0"
29
+ "@salesforce/lds-utils-adapters": "^1.317.0"
30
+ },
31
+ "devDependencies": {
32
+ "@salesforce/nimbus-plugin-lds": "^1.317.0"
30
33
  },
31
34
  "volta": {
32
35
  "extends": "../../package.json"
@@ -1,71 +0,0 @@
1
- import type { NetworkAdapter, ResourceRequest, Luvio, DurableStoreKeyMetadataMap } from '@luvio/engine';
2
- import type { DraftAction, CompletedDraftAction, QueueOperation, PendingDraftAction, DraftActionMetadata, DraftQueue } from '../DraftQueue';
3
- import { ProcessActionResult } from '../DraftQueue';
4
- import type { ActionHandler, DraftIdAndKeyMapping } from './ActionHandler';
5
- export type ResponseIngestionEntry<T = unknown> = {
6
- response: T;
7
- synchronousIngest: AbstractResourceRequestActionHandler<T, unknown>['synchronousIngest'];
8
- buildCacheKeysForResponse: AbstractResourceRequestActionHandler<T, unknown>['buildCacheKeysFromResponse'];
9
- };
10
- export declare const DEFAULT_FIELD_LAST_MODIFIED_DATE = "LastModifiedDate";
11
- export declare const DEFAULT_FIELD_CREATED_DATE = "CreatedDate";
12
- export declare abstract class AbstractResourceRequestActionHandler<ResponseType, DraftMetadata> implements ActionHandler<ResourceRequest, DraftMetadata, ResponseType> {
13
- protected readonly draftQueue: DraftQueue;
14
- protected readonly networkAdapter: NetworkAdapter;
15
- protected readonly getLuvio: () => Luvio;
16
- ephemeralRedirects: {
17
- [key: string]: string;
18
- };
19
- isIdempotencySupported: boolean;
20
- isLdsIdempotencyWriteDisabled: boolean;
21
- isBackdatingEnabled: boolean;
22
- constructor(draftQueue: DraftQueue, networkAdapter: NetworkAdapter, getLuvio: () => Luvio);
23
- enqueue(data: ResourceRequest): Promise<PendingDraftAction<ResourceRequest>>;
24
- handleAction(action: DraftAction<ResourceRequest, ResponseType>, actionCompleted: (action: CompletedDraftAction<ResourceRequest, ResponseType>) => Promise<void>, actionErrored: (action: DraftAction<ResourceRequest, ResponseType>, retry: boolean, retryDelayInMs?: number, actionDataChanged?: boolean) => Promise<void>): Promise<ProcessActionResult>;
25
- handleIdempotencyServerError(responseBody: any, action: DraftAction<ResourceRequest, ResponseType>, updateIdempotencyKey: boolean, ...targetErrorCodes: string[]): boolean;
26
- isUiApiErrors(body: any): boolean;
27
- isBackdatingError(body: any, action: DraftAction<ResourceRequest, ResponseType>): any;
28
- buildPendingAction(request: ResourceRequest, queue: DraftAction<unknown, unknown>[]): Promise<PendingDraftAction<ResourceRequest>>;
29
- handleActionEnqueued(_action: PendingDraftAction<ResourceRequest>): Promise<void>;
30
- handleActionRemoved(action: DraftAction<ResourceRequest, ResponseType>): Promise<void>;
31
- getQueueOperationsForCompletingDrafts(queue: DraftAction<unknown, unknown>[], action: CompletedDraftAction<ResourceRequest, ResponseType>): QueueOperation[];
32
- getRedirectMappings(action: CompletedDraftAction<ResourceRequest, ResponseType>): DraftIdAndKeyMapping[] | undefined;
33
- handleActionCompleted(action: CompletedDraftAction<ResourceRequest, ResponseType>, queueOperations: QueueOperation[], allHandlers: ActionHandler<unknown, unknown, unknown>[]): Promise<void>;
34
- handleReplaceAction(targetAction: DraftAction<ResourceRequest, ResponseType>, sourceAction: DraftAction<ResourceRequest, ResponseType>): DraftAction<ResourceRequest, ResponseType>;
35
- mergeActions(targetAction: DraftAction<ResourceRequest, ResponseType>, sourceAction: DraftAction<ResourceRequest, ResponseType>): DraftAction<ResourceRequest, ResponseType>;
36
- shouldDeleteActionByTagOnRemoval(action: DraftAction<ResourceRequest, ResponseType>): boolean;
37
- updateMetadata(_existingMetadata: DraftActionMetadata, incomingMetadata: DraftActionMetadata): DraftActionMetadata;
38
- private isActionOfType;
39
- protected reingestRecord(action: DraftAction<ResourceRequest, ResponseType>): Promise<void>;
40
- getDraftIdsFromAction(action: DraftAction<ResourceRequest, ResponseType>): string[];
41
- hasIdempotencySupport(): boolean;
42
- ingestResponses(responses: ResponseIngestionEntry[], action: DraftAction<ResourceRequest, ResponseType>): Promise<void>;
43
- evictKey(key: string): Promise<void>;
44
- abstract handlerId: string;
45
- abstract canHandlePublish(key: string): boolean;
46
- abstract canRepresentationContainDraftMetadata(representationName: string): boolean;
47
- /**
48
- * Extracts or synthesizes an id from a given resource request
49
- * @throws if the id cannot be determined or synthesized
50
- * @param request the request to get the id from
51
- */
52
- abstract getIdFromRequest(request: ResourceRequest): Promise<string>;
53
- abstract getIdFromResponseBody(responseBody: ResponseType): string;
54
- abstract buildTagForTargetId(id: string): string;
55
- abstract buildCacheKeysFromResponse(response: ResponseType): DurableStoreKeyMetadataMap;
56
- abstract synchronousIngest(response: ResponseType, action: DraftAction<ResourceRequest, ResponseType>): void;
57
- abstract getDataForAction(action: DraftAction<ResourceRequest, ResponseType>): Promise<ResponseType | undefined>;
58
- abstract getDraftMetadata(key: string): Promise<DraftMetadata | undefined>;
59
- abstract applyDraftsToIncomingData(key: string, data: unknown, draftMetadata: DraftMetadata | undefined, publishFn: (key: string, data: any) => void): void;
60
- /**
61
- * Returns true if the given targetId is a for a draft-created record
62
- *
63
- * @param targetId the targetId to check
64
- */
65
- abstract isDraftId(targetId: string): boolean;
66
- /**
67
- * Overlay the sourceBody over top of the targetBody
68
- */
69
- abstract mergeRequestBody<T = unknown>(targetBody: T, sourceBody: T): T;
70
- }
71
- export declare function isResourceRequestAction(action: DraftAction<unknown, unknown>): action is DraftAction<ResourceRequest, unknown>;
@@ -1,33 +0,0 @@
1
- import type { HttpStatusCode, Headers } from '@luvio/engine';
2
- export interface UIApiError {
3
- errorCode: string;
4
- message: string | null;
5
- }
6
- export declare const enum IdempotentWriteSpecificHttpStatusCode {
7
- RequestTimeout = 408,
8
- Conflict = 409,
9
- BadGateway = 502,
10
- ServiceUnavailable = 503,
11
- UnProcessableEntity = 422
12
- }
13
- /**
14
- * The http status code which could be returned from idempotent write.
15
- */
16
- export type IdempotentWriteHttpStatusCode = IdempotentWriteSpecificHttpStatusCode | HttpStatusCode;
17
- export declare const HTTP_HEADER_RETRY_AFTER = "Retry-After";
18
- export declare const HTTP_HEADER_IDEMPOTENCY_KEY = "Idempotency-Key";
19
- export declare const ERROR_CODE_IDEMPOTENCY_FEATURE_NOT_ENABLED = "IDEMPOTENCY_FEATURE_NOT_ENABLED";
20
- export declare const ERROR_CODE_IDEMPOTENCY_NOT_SUPPORTED = "IDEMPOTENCY_NOT_SUPPORTED";
21
- export declare const ERROR_CODE_IDEMPOTENCY_KEY_USED_DIFFERENT_USER = "IDEMPOTENCY_KEY_USED_DIFFERENT_USER";
22
- export declare const ERROR_CODE_IDEMPOTENCY_CONCURRENT_REQUEST = "IDEMPOTENCY_CONCURRENT_REQUEST";
23
- export declare const ERROR_CODE_IDEMPOTENCY_KEY_ALREADY_USED = "IDEMPOTENCY_KEY_ALREADY_USED";
24
- export declare const ERROR_CODE_IDEMPOTENCY_BACKEND_OPERATION_ERROR = "IDEMPOTENCY_BACKEND_OPERATION_ERROR";
25
- /**
26
- * Get the retry after in milliseconds from the response headers, undefined if not specified.
27
- * The header could have two different format.
28
- * Retry-After: <http-date>, like Wed, 21 Oct 2015 07:28:00 GMT
29
- * Retry-After: <delay-seconds>, like 1.5s
30
- * @param headers http headers
31
- * @returns the time to delat in millisconds.
32
- */
33
- export declare function getRetryAfterInMs(headers?: Headers): number | undefined;