@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.
- package/dist/main.js +703 -285
- package/dist/types/drafts/AbstractResourceRequestActionHandler.d.ts +23 -0
- package/dist/types/drafts/IdempotentWrite.d.ts +1 -0
- package/dist/types/drafts/utils/error.d.ts +29 -0
- package/dist/types/instrumentation/metrics.d.ts +36 -0
- package/package.json +36 -36
- package/sfdc/main.js +703 -285
- package/sfdc/types/drafts/AbstractResourceRequestActionHandler.d.ts +23 -0
- package/sfdc/types/drafts/IdempotentWrite.d.ts +1 -0
- package/sfdc/types/drafts/utils/error.d.ts +29 -0
- package/sfdc/types/instrumentation/metrics.d.ts +36 -0
|
@@ -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;
|
|
@@ -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;
|