@salesforce/lds-runtime-mobile 1.428.0-dev2 → 1.428.0-dev21

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.
@@ -25,6 +25,28 @@ export declare abstract class AbstractResourceRequestActionHandler<ResponseType>
25
25
  enqueue(data: ResourceRequest): Promise<PendingDraftAction<ResourceRequest>>;
26
26
  buildPendingAction(request: ResourceRequest, queue: DraftAction<unknown, unknown>[]): Promise<PendingDraftAction<ResourceRequest>>;
27
27
  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>;
28
+ /**
29
+ * Reads the persisted upload retry-attempt count from the action's durable metadata
30
+ * bag. The count lives in metadata (rather than in-memory) so it survives app
31
+ * restarts and queue re-creation — the failure modes that let the unbounded retry
32
+ * loop run for days.
33
+ */
34
+ private getUploadRetryCount;
35
+ /**
36
+ * Enforces the upload retry-attempt cap around the queue's `actionErrored` callback.
37
+ * Both retry paths — a retryable HTTP error and a thrown exception — funnel through
38
+ * here.
39
+ *
40
+ * The entire cap behavior is gated behind `lmr.draft-queue-max-retry-attempts`. When
41
+ * the gate is CLOSED this is a transparent passthrough: it issues the same
42
+ * `actionErrored(action, true, ...)` retry the queue made before this change, and
43
+ * writes no extra metadata. When the gate is OPEN it persists an incremented attempt
44
+ * count to the action's durable metadata and, once the action has failed
45
+ * {@link MAX_RETRY_ATTEMPTS} times, transitions it to Error (carrying a
46
+ * FetchResponse-shaped error that names the failure that exhausted the retries)
47
+ * instead of scheduling yet another retry.
48
+ */
49
+ private retryOrCap;
28
50
  handleActionEnqueued(action: PendingDraftAction<ResourceRequest>): Promise<void>;
29
51
  handleActionRemoved(action: DraftAction<ResourceRequest, ResponseType>): Promise<void>;
30
52
  handleActionReplaced(target: DraftAction<ResourceRequest, ResponseType>, source: DraftAction<ResourceRequest, ResponseType>): Promise<void>;
@@ -38,6 +60,7 @@ export declare abstract class AbstractResourceRequestActionHandler<ResponseType>
38
60
  private isActionOfType;
39
61
  private handleIdempotencyServerError;
40
62
  private isUiApiErrors;
63
+ private isServerGeneratedError;
41
64
  private isBackdatingError;
42
65
  getDraftIdsFromAction(action: DraftAction<ResourceRequest, ResponseType>): string[];
43
66
  hasIdempotencySupport(): boolean;
@@ -4,6 +4,7 @@ export interface UIApiError {
4
4
  message: string | null;
5
5
  }
6
6
  export declare const enum IdempotentWriteSpecificHttpStatusCode {
7
+ NoResponse = 0,
7
8
  RequestTimeout = 408,
8
9
  Conflict = 409,
9
10
  BadGateway = 502,
@@ -1,4 +1,33 @@
1
+ import type { FetchResponse } from '@luvio/engine';
1
2
  /**
2
3
  * This function takes an unknown error and normalizes it to an Error object
3
4
  */
4
5
  export declare function normalizeError(error: unknown): Error;
6
+ /**
7
+ * Maximum number of times an action's upload will be retried before the action is
8
+ * transitioned to Error and surfaced to the consumer. Caps the otherwise-unbounded
9
+ * retry loop that can wedge the single-worker draft queue when an upload fails
10
+ * deterministically on every dispatch. Only enforced when the
11
+ * `lmr.draft-queue-max-retry-attempts` gate is open.
12
+ */
13
+ export declare const MAX_RETRY_ATTEMPTS = 5;
14
+ export declare const MAX_RETRY_ERROR_CODE = "MAX_RETRY_ATTEMPTS_EXCEEDED";
15
+ /**
16
+ * The two things that can cause an upload attempt to fail and be retried:
17
+ * - a non-ok `FetchResponse` returned by the network adapter (the HTTP error path), or
18
+ * - an `Error` thrown out of the dispatch and caught by `handleAction` (the catch path;
19
+ * the raw caught value is normalized to an `Error` before it reaches here).
20
+ * Either way the cause is concretely typed — it is never an opaque `unknown`.
21
+ */
22
+ export type UploadFailure = FetchResponse<unknown> | Error;
23
+ /**
24
+ * Builds the FetchResponse-shaped error attached to an action that has exhausted its
25
+ * upload retries. Shaped like a real network error response (status/statusText/ok/
26
+ * headers/body) so downstream consumers (DraftManager, queue instrumentation) that
27
+ * read `action.error.status`/`.body`/etc. behave consistently with a server error.
28
+ *
29
+ * The failure that caused the final attempt — a non-ok response (HTTP path) or a thrown
30
+ * error (catch path) — is folded into the body so the surfaced error says WHAT failed,
31
+ * not merely that the attempt cap was reached.
32
+ */
33
+ export declare function buildMaxRetryError(lastFailure: UploadFailure): FetchResponse<unknown>;
@@ -10,6 +10,42 @@ export declare function reportGraphqlAdapterError(errorCode: string): void;
10
10
  export declare function reportGraphqlQueryInstrumentation(data: QueryInstrumentation): void;
11
11
  export declare function incrementGraphQLRefreshUndfined(): void;
12
12
  export declare function reportDraftActionEvent(state: 'added' | 'uploading' | 'completed' | 'deleted' | 'updated' | 'failed', draftCount: number, message?: string): void;
13
+ /**
14
+ * Reports an exception thrown while uploading a draft action. The thrown error is
15
+ * otherwise swallowed by the upload handler's retry path, leaving the failure
16
+ * invisible in telemetry; this surfaces it to o11y (log + counter) so a deterministic
17
+ * upload failure can be diagnosed instead of silently retried forever.
18
+ *
19
+ * PII: the device log line carries only the handler id, retry attempt, and the error
20
+ * NAME (e.g. "TypeError") — never the free-text error message, which can embed record
21
+ * ids (URL path segments) or server field-validation strings. The full normalized error
22
+ * (message + stack) is handed only to `ldsMobileInstrumentation.error`, the structured
23
+ * o11y error pipeline responsible for scrubbing/handling that detail. Also does NOT log
24
+ * the action's targetId/tag or any request body/field values.
25
+ */
26
+ export declare function reportDraftActionUploadError(error: unknown, handlerId: string, attempt: number): void;
27
+ export type IdempotencyKeyRotationReason = 'idempotency-error' | 'backdating-collision' | 'merge-actions' | 'generic-400-killswitch';
28
+ /**
29
+ * Emits a single greppable Splunk log line (and an o11y counter) every time a draft's
30
+ * Idempotency-Key is rotated — i.e. replaced with a fresh uuid rather than reused on
31
+ * retry. This is the instrumentation for the WOLI duplicate-record investigation
32
+ * (@W-23428669): a rotation mid-flight is the mechanism by which an already-committed
33
+ * write can be re-sent under a new key and duplicated on the server, so recording every
34
+ * rotation lets us reconstruct, on a future reproduction, exactly where and how often a
35
+ * key changed for a given draft.
36
+ *
37
+ * The message is prefixed with a stable literal ("Idempotency key rotated") so it can be
38
+ * pulled out of Splunk with a substring match, and carries:
39
+ * - draftId: the local (client-synthesized) draft-action target id, so this event can be
40
+ * correlated with the other draft-queue events for the same draft. This is NOT a server
41
+ * record id or any field value — it is the same local id already surfaced by the queue's
42
+ * other lifecycle logs, so it introduces no new PII.
43
+ * - reason: which rotation call site fired, to disambiguate the distinct rotation paths.
44
+ *
45
+ * @param draftId the local draft-action target id whose key was rotated
46
+ * @param reason the rotation call site (see IdempotencyKeyRotationReason)
47
+ */
48
+ export declare function reportIdempotencyKeyRotated(draftId: string, reason: IdempotencyKeyRotationReason): void;
13
49
  export declare function reportDraftQueueState(state: 'started' | 'error' | 'waiting' | 'stopped', draftCount: number): void;
14
50
  export declare function reportDraftAwareContentDocumentVersionSynthesizeError(err: unknown): void;
15
51
  export declare function reportDraftAwareContentVersionSynthesizeCalls(mimeType: string): void;