@usequeek/app-sdk 0.6.2 → 0.7.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/LICENSE +1 -1
- package/README.md +340 -337
- package/dist/app-auth.d.ts +1 -2
- package/dist/app-auth.js +1 -2
- package/dist/auth-fetch.d.ts +12 -1
- package/dist/auth-fetch.js +13 -2
- package/dist/background.d.ts +9 -1
- package/dist/background.js +9 -1
- package/dist/browser.d.ts +1 -2
- package/dist/browser.js +1 -2
- package/dist/client.d.ts +130 -7
- package/dist/client.js +198 -41
- package/dist/collected.d.ts +0 -1
- package/dist/collected.js +0 -1
- package/dist/crypto.d.ts +11 -1
- package/dist/crypto.js +22 -7
- package/dist/delivery.d.ts +18 -1
- package/dist/delivery.js +26 -17
- package/dist/frame.d.ts +0 -1
- package/dist/frame.js +0 -1
- package/dist/handoff.d.ts +0 -1
- package/dist/handoff.js +0 -1
- package/dist/hono.d.ts +0 -1
- package/dist/hono.js +0 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +1 -2
- package/dist/install-handlers.d.ts +22 -2
- package/dist/install-handlers.js +37 -7
- package/dist/logger.d.ts +7 -1
- package/dist/logger.js +7 -1
- package/dist/merchant-schema.d.ts +1184 -336
- package/dist/merchant-schema.js +0 -1
- package/dist/proxy.d.ts +2 -3
- package/dist/proxy.js +4 -5
- package/dist/react.d.ts +9 -1
- package/dist/react.js +10 -2
- package/dist/resync.d.ts +11 -1
- package/dist/resync.js +18 -16
- package/dist/scopes.d.ts +14 -1
- package/dist/scopes.js +18 -17
- package/dist/server.d.ts +0 -1
- package/dist/server.js +0 -1
- package/dist/session.d.ts +20 -1
- package/dist/session.js +20 -1
- package/dist/signatures.d.ts +26 -1
- package/dist/signatures.js +29 -3
- package/dist/store.d.ts +6 -1
- package/dist/store.js +0 -1
- package/dist/theme.d.ts +0 -1
- package/dist/theme.js +0 -1
- package/dist/tokens.d.ts +9 -1
- package/dist/tokens.js +23 -47
- package/dist/webhooks.d.ts +28 -3
- package/dist/webhooks.js +25 -7
- package/package.json +18 -2
- package/dist/app-auth.d.ts.map +0 -1
- package/dist/app-auth.js.map +0 -1
- package/dist/auth-fetch.d.ts.map +0 -1
- package/dist/auth-fetch.js.map +0 -1
- package/dist/background.d.ts.map +0 -1
- package/dist/background.js.map +0 -1
- package/dist/browser.d.ts.map +0 -1
- package/dist/browser.js.map +0 -1
- package/dist/client.d.ts.map +0 -1
- package/dist/client.js.map +0 -1
- package/dist/collected.d.ts.map +0 -1
- package/dist/collected.js.map +0 -1
- package/dist/crypto.d.ts.map +0 -1
- package/dist/crypto.js.map +0 -1
- package/dist/delivery.d.ts.map +0 -1
- package/dist/delivery.js.map +0 -1
- package/dist/frame.d.ts.map +0 -1
- package/dist/frame.js.map +0 -1
- package/dist/handoff.d.ts.map +0 -1
- package/dist/handoff.js.map +0 -1
- package/dist/hono.d.ts.map +0 -1
- package/dist/hono.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/install-handlers.d.ts.map +0 -1
- package/dist/install-handlers.js.map +0 -1
- package/dist/logger.d.ts.map +0 -1
- package/dist/logger.js.map +0 -1
- package/dist/merchant-schema.d.ts.map +0 -1
- package/dist/merchant-schema.js.map +0 -1
- package/dist/proxy.d.ts.map +0 -1
- package/dist/proxy.js.map +0 -1
- package/dist/react.d.ts.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/resync.d.ts.map +0 -1
- package/dist/resync.js.map +0 -1
- package/dist/scopes.d.ts.map +0 -1
- package/dist/scopes.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/session.d.ts.map +0 -1
- package/dist/session.js.map +0 -1
- package/dist/signatures.d.ts.map +0 -1
- package/dist/signatures.js.map +0 -1
- package/dist/store.d.ts.map +0 -1
- package/dist/store.js.map +0 -1
- package/dist/theme.d.ts.map +0 -1
- package/dist/theme.js.map +0 -1
- package/dist/tokens.d.ts.map +0 -1
- package/dist/tokens.js.map +0 -1
- package/dist/webhooks.d.ts.map +0 -1
- package/dist/webhooks.js.map +0 -1
package/dist/app-auth.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export declare const INVALID_CLIENT_KEY_CODE = "invalid_client_key";
|
|
|
72
72
|
export declare function isTokenRefusal(status: number, code: string | undefined): boolean;
|
|
73
73
|
/** True when a merchant-API 403 means the cached token predates a grant change. */
|
|
74
74
|
export declare function isInsufficientScope(status: number, code: string | undefined): boolean;
|
|
75
|
-
/** App-JWT `iat` is backdated this far
|
|
75
|
+
/** App-JWT `iat` is backdated this far, so a small clock skew between the app and Queek cannot make a fresh token look not-yet-valid. */
|
|
76
76
|
export declare const APP_JWT_SKEW_SECONDS = 60;
|
|
77
77
|
/** App-JWT lifetime: `exp = iat + 540`, so `exp − iat` (540 s) stays ≤ the 600 s wire bound. */
|
|
78
78
|
export declare const APP_JWT_TTL_SECONDS = 540;
|
|
@@ -156,4 +156,3 @@ export interface SignAppJwtOptions {
|
|
|
156
156
|
* compact JWT; NEVER logs it or the private key.
|
|
157
157
|
*/
|
|
158
158
|
export declare function signAppJwt(options: SignAppJwtOptions): string;
|
|
159
|
-
//# sourceMappingURL=app-auth.d.ts.map
|
package/dist/app-auth.js
CHANGED
|
@@ -97,7 +97,7 @@ export function isInsufficientScope(status, code) {
|
|
|
97
97
|
// ---------------------------------------------------------------------------
|
|
98
98
|
// Timing bounds (all in the open, all injectable in tests).
|
|
99
99
|
// ---------------------------------------------------------------------------
|
|
100
|
-
/** App-JWT `iat` is backdated this far
|
|
100
|
+
/** App-JWT `iat` is backdated this far, so a small clock skew between the app and Queek cannot make a fresh token look not-yet-valid. */
|
|
101
101
|
export const APP_JWT_SKEW_SECONDS = 60;
|
|
102
102
|
/** App-JWT lifetime: `exp = iat + 540`, so `exp − iat` (540 s) stays ≤ the 600 s wire bound. */
|
|
103
103
|
export const APP_JWT_TTL_SECONDS = 540;
|
|
@@ -234,4 +234,3 @@ export function signAppJwt(options) {
|
|
|
234
234
|
}
|
|
235
235
|
return `${signingInput}.${signature.toString("base64url")}`;
|
|
236
236
|
}
|
|
237
|
-
//# sourceMappingURL=app-auth.js.map
|
package/dist/auth-fetch.d.ts
CHANGED
|
@@ -85,5 +85,16 @@ export declare function readLaunchToken(href: string, param?: string): string |
|
|
|
85
85
|
* present.
|
|
86
86
|
*/
|
|
87
87
|
export declare function stripLaunchToken(href: string, param?: string): string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Install same-origin session carriage for an embedded page. Reads and strips
|
|
90
|
+
* the first-load token, exchanges it once, and returns a `fetch` that adds
|
|
91
|
+
* `Authorization: Bearer <session>` to same-origin requests.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const auth = installAuthFetch({
|
|
95
|
+
* dashboardOrigin: "https://dashboard.usequeek.com",
|
|
96
|
+
* exchange: async (token) => (await fetch("/api/session", { method: "POST", body: token })).text(),
|
|
97
|
+
* });
|
|
98
|
+
* const response = await auth.fetch("/api/orders");
|
|
99
|
+
*/
|
|
88
100
|
export declare function installAuthFetch(options: AuthFetchOptions): InstalledAuth;
|
|
89
|
-
//# sourceMappingURL=auth-fetch.d.ts.map
|
package/dist/auth-fetch.js
CHANGED
|
@@ -61,6 +61,18 @@ export function stripLaunchToken(href, param = LAUNCH_TOKEN_PARAM) {
|
|
|
61
61
|
return null;
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Install same-origin session carriage for an embedded page. Reads and strips
|
|
66
|
+
* the first-load token, exchanges it once, and returns a `fetch` that adds
|
|
67
|
+
* `Authorization: Bearer <session>` to same-origin requests.
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* const auth = installAuthFetch({
|
|
71
|
+
* dashboardOrigin: "https://dashboard.usequeek.com",
|
|
72
|
+
* exchange: async (token) => (await fetch("/api/session", { method: "POST", body: token })).text(),
|
|
73
|
+
* });
|
|
74
|
+
* const response = await auth.fetch("/api/orders");
|
|
75
|
+
*/
|
|
64
76
|
export function installAuthFetch(options) {
|
|
65
77
|
const param = options.param ?? LAUNCH_TOKEN_PARAM;
|
|
66
78
|
const win = globalWindow();
|
|
@@ -68,7 +80,7 @@ export function installAuthFetch(options) {
|
|
|
68
80
|
const replaceUrl = options.replaceUrl ?? ((url) => win?.history.replaceState(null, "", url));
|
|
69
81
|
const fetchImpl = options.fetchImpl ?? globalThis.fetch ?? null;
|
|
70
82
|
if (fetchImpl === null) {
|
|
71
|
-
throw new Error("installAuthFetch needs a fetch implementation");
|
|
83
|
+
throw new Error("installAuthFetch needs a fetch implementation: pass `fetchImpl`, or run where a global `fetch` exists.");
|
|
72
84
|
}
|
|
73
85
|
const fetchFn = fetchImpl;
|
|
74
86
|
const listenTarget = options.listenTarget ?? win ?? undefined;
|
|
@@ -258,4 +270,3 @@ export function installAuthFetch(options) {
|
|
|
258
270
|
},
|
|
259
271
|
};
|
|
260
272
|
}
|
|
261
|
-
//# sourceMappingURL=auth-fetch.js.map
|
package/dist/background.d.ts
CHANGED
|
@@ -15,6 +15,14 @@ export interface DetachOptions {
|
|
|
15
15
|
label: string;
|
|
16
16
|
onError: (error: unknown) => void;
|
|
17
17
|
}
|
|
18
|
+
/**
|
|
19
|
+
* Run a promise in the background without leaving it unhandled: a rejection
|
|
20
|
+
* goes to `options.onError` with the `label`, never to an unhandled-rejection
|
|
21
|
+
* crash. Use it to answer a delivery fast and finish the work after.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* detach(syncCatalogue(installationId), { label: "catalogue sync", onError: (e) => logger.error("sync failed", { e: String(e) }) });
|
|
25
|
+
*/
|
|
18
26
|
export declare function detach(promise: Promise<unknown>, options: DetachOptions): void;
|
|
19
27
|
/** How many background tasks are currently running (health/debug only). */
|
|
20
28
|
export declare function backgroundTaskCount(): number;
|
|
@@ -64,5 +72,5 @@ export interface CatchupOptions {
|
|
|
64
72
|
/** Injectable uniform [0,1) for jitter. Defaults to `Math.random`. */
|
|
65
73
|
random?: () => number;
|
|
66
74
|
}
|
|
75
|
+
/** Run `forInstallation` for every stored installation on a jittered schedule. See the notes above for the guarantees. */
|
|
67
76
|
export declare function runInstallationCatchup(options: CatchupOptions): Promise<CatchupResult>;
|
|
68
|
-
//# sourceMappingURL=background.d.ts.map
|
package/dist/background.js
CHANGED
|
@@ -12,6 +12,14 @@
|
|
|
12
12
|
* when replicas or an outbound queue need shared state.
|
|
13
13
|
*/
|
|
14
14
|
const inFlight = new Set();
|
|
15
|
+
/**
|
|
16
|
+
* Run a promise in the background without leaving it unhandled: a rejection
|
|
17
|
+
* goes to `options.onError` with the `label`, never to an unhandled-rejection
|
|
18
|
+
* crash. Use it to answer a delivery fast and finish the work after.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* detach(syncCatalogue(installationId), { label: "catalogue sync", onError: (e) => logger.error("sync failed", { e: String(e) }) });
|
|
22
|
+
*/
|
|
15
23
|
export function detach(promise, options) {
|
|
16
24
|
inFlight.add(promise);
|
|
17
25
|
promise
|
|
@@ -29,6 +37,7 @@ export function backgroundTaskCount() {
|
|
|
29
37
|
function defaultSleep(ms) {
|
|
30
38
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
31
39
|
}
|
|
40
|
+
/** Run `forInstallation` for every stored installation on a jittered schedule. See the notes above for the guarantees. */
|
|
32
41
|
export async function runInstallationCatchup(options) {
|
|
33
42
|
const sleep = options.sleep ?? defaultSleep;
|
|
34
43
|
const random = options.random ?? Math.random;
|
|
@@ -75,4 +84,3 @@ export async function runInstallationCatchup(options) {
|
|
|
75
84
|
await Promise.all(Array.from({ length: Math.min(concurrency, resolved.length) }, () => worker()));
|
|
76
85
|
return result;
|
|
77
86
|
}
|
|
78
|
-
//# sourceMappingURL=background.js.map
|
package/dist/browser.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plain-browser entry for `@usequeek/app-sdk`: the embedded-
|
|
2
|
+
* Plain-browser entry for `@usequeek/app-sdk`: the embedded-page bridge plus
|
|
3
3
|
* `installAuthFetch`, with ZERO Node-only modules.
|
|
4
4
|
*
|
|
5
5
|
* Import from `@usequeek/app-sdk/browser` in any browser bundle (Vite,
|
|
@@ -22,4 +22,3 @@
|
|
|
22
22
|
export { type AuthFetchOptions, BRIDGE_TOKEN_TIMEOUT_MS, type InstalledAuth, installAuthFetch, LAUNCH_TOKEN_PARAM, readLaunchToken, stripLaunchToken, } from "./auth-fetch.js";
|
|
23
23
|
export { APP_SOURCE, type AppInboundMessage, type AppOutboundMessage, BRIDGE_VERSION, type BridgeTheme, DASHBOARD_SOURCE, type EmbedEvent, type EmbedEventTarget, type EmbedPostTarget, type FrameBridgeOptions, listenToDashboard, type PickResourceRequest, type ReadyOptions, type ResourceItem, type SaveBarAction, type SaveBarState, sendNavigated, sendOpen, sendPickResource, sendReady, sendResize, sendSaveBar, sendTitle, type ThemeMode, type TitleActionDef, } from "./frame.js";
|
|
24
24
|
export { applyTheme, getThemeModeFromUrl, installThemeListener, rememberThemeMode, syncThemeUrl, THEME_PARAM, THEME_STORAGE_KEY, type ThemeDocument, type ThemeDocumentElement, type ThemeListenerOptions, type ThemeStorage, type ThemeUrlOptions, themeBootstrapScript, } from "./theme.js";
|
|
25
|
-
//# sourceMappingURL=browser.d.ts.map
|
package/dist/browser.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Plain-browser entry for `@usequeek/app-sdk`: the embedded-
|
|
2
|
+
* Plain-browser entry for `@usequeek/app-sdk`: the embedded-page bridge plus
|
|
3
3
|
* `installAuthFetch`, with ZERO Node-only modules.
|
|
4
4
|
*
|
|
5
5
|
* Import from `@usequeek/app-sdk/browser` in any browser bundle (Vite,
|
|
@@ -24,4 +24,3 @@ export { APP_SOURCE, BRIDGE_VERSION, DASHBOARD_SOURCE, listenToDashboard, sendNa
|
|
|
24
24
|
// NOTE: ThemeMode comes from ./frame.js only — theme.js merely re-exports
|
|
25
25
|
// it, so listing it here too would be ambiguous.
|
|
26
26
|
export { applyTheme, getThemeModeFromUrl, installThemeListener, rememberThemeMode, syncThemeUrl, THEME_PARAM, THEME_STORAGE_KEY, themeBootstrapScript, } from "./theme.js";
|
|
27
|
-
//# sourceMappingURL=browser.js.map
|
package/dist/client.d.ts
CHANGED
|
@@ -23,7 +23,12 @@ import type { paths } from "./merchant-schema.js";
|
|
|
23
23
|
* is additive, so both are read). Switch on `code`, never on `message`.
|
|
24
24
|
* - 429s expose `retryAfterMs` parsed from `Retry-After` (seconds or
|
|
25
25
|
* HTTP-date). Plain `request()` never retries; `requestWithRetry()`
|
|
26
|
-
* retries 429s and network errors with
|
|
26
|
+
* retries 429s, 502/503/504, timeouts and network errors with jittered
|
|
27
|
+
* exponential backoff and the SAME idempotency key on every attempt.
|
|
28
|
+
* - Every request has a timeout (`timeoutMs`, default 30 s) and accepts an
|
|
29
|
+
* `AbortSignal`. Failures that never produced an HTTP answer are thrown as
|
|
30
|
+
* `QueekApiError` with status 0 and the code `network_error`,
|
|
31
|
+
* `request_timeout` or `request_aborted`, and the original error as `cause`.
|
|
27
32
|
*
|
|
28
33
|
* Typed surface: `getStore()` is typed from the app's Merchant API
|
|
29
34
|
* paths. Pass the app's own codegen output as the type argument
|
|
@@ -92,26 +97,40 @@ export declare function devApiHostsFromEnv(env?: NodeJS.ProcessEnv): string[];
|
|
|
92
97
|
* QUEEK_DEV_API_HOSTS`.
|
|
93
98
|
*/
|
|
94
99
|
export declare function resolveApiBase(raw: string, allowedApiHosts?: string[]): string;
|
|
100
|
+
/** Default time a single HTTP attempt may take before it fails with `request_timeout`. */
|
|
101
|
+
export declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
102
|
+
/** Default ceiling for honouring a 429 `Retry-After` inside `requestWithRetry()`. */
|
|
103
|
+
export declare const DEFAULT_MAX_RETRY_AFTER_MS = 30000;
|
|
95
104
|
export interface QueekClientOptions {
|
|
96
105
|
/** Handoff `api_base` (bare store host or full merchant base). Validated at construction. */
|
|
97
106
|
apiBase: string;
|
|
107
|
+
/** The installation credential, sent as `X-Client-Key`. Never logged and never put in an error message. */
|
|
98
108
|
apiKey: string;
|
|
99
109
|
fetchImpl?: typeof fetch;
|
|
110
|
+
/** Time one attempt may take, in ms (default 30000). `0` disables the client timeout. */
|
|
111
|
+
timeoutMs?: number;
|
|
100
112
|
/** Sent as User-Agent. Defaults to `queek-app/1.0`. */
|
|
101
113
|
userAgent?: string;
|
|
102
114
|
/** Extra allowed `apiBase` hosts (test/local API hosts). Production default always applies. */
|
|
103
115
|
allowedApiHosts?: string[];
|
|
104
116
|
}
|
|
105
117
|
export interface RequestOptions {
|
|
118
|
+
/** Query string values. `undefined` entries are skipped; everything else is URL-encoded for you. */
|
|
106
119
|
query?: Record<string, string | number | boolean | undefined>;
|
|
120
|
+
/** JSON-serialisable request body. */
|
|
107
121
|
body?: unknown;
|
|
108
122
|
/** Write idempotency (POST/PUT/PATCH/DELETE). Generated when omitted. */
|
|
109
123
|
idempotencyKey?: string;
|
|
124
|
+
/** Correlation id sent as `X-Request-Id` (8 to 128 characters of `A-Za-z0-9_.:-`). Generated when omitted. */
|
|
110
125
|
requestId?: string;
|
|
126
|
+
/** Aborts the call. An aborted call fails with code `request_aborted` and is never retried. */
|
|
111
127
|
signal?: AbortSignal;
|
|
128
|
+
/** Overrides the client's per-attempt timeout for this call, in ms. `0` disables it. */
|
|
129
|
+
timeoutMs?: number;
|
|
112
130
|
}
|
|
113
131
|
/** True for HTTP methods that carry an `Idempotency-Key` (POST/PUT/PATCH/DELETE). */
|
|
114
132
|
export declare function isWriteMethod(method: string): boolean;
|
|
133
|
+
/** A fresh `Idempotency-Key` value (a random UUID). */
|
|
115
134
|
export declare function newIdempotencyKey(): string;
|
|
116
135
|
export interface QueekErrorDetails {
|
|
117
136
|
status: number;
|
|
@@ -122,7 +141,31 @@ export interface QueekErrorDetails {
|
|
|
122
141
|
docUrl?: string;
|
|
123
142
|
requestId?: string;
|
|
124
143
|
retryAfterMs?: number;
|
|
144
|
+
/** The underlying error, when this one wraps another (a failed `fetch`, for example). */
|
|
145
|
+
cause?: unknown;
|
|
125
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* The one error every Queek HTTP call throws. Switch on `code`, never on
|
|
149
|
+
* `message`.
|
|
150
|
+
*
|
|
151
|
+
* Codes that come from Queek are the API's own (`validation_failed`,
|
|
152
|
+
* `insufficient_scope`, `too_many_requests`, ...). Codes the SDK raises when
|
|
153
|
+
* no usable HTTP answer arrived (`status` is 0 for the first three):
|
|
154
|
+
* `network_error`, `request_timeout`, `request_aborted`, and
|
|
155
|
+
* `invalid_response` (an HTTP success whose body was not JSON).
|
|
156
|
+
*
|
|
157
|
+
* `requestId` matches the `X-Request-Id` response header: quote it to Queek
|
|
158
|
+
* support.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* try {
|
|
162
|
+
* await client.request("POST", "/orders", { body });
|
|
163
|
+
* } catch (error) {
|
|
164
|
+
* if (error instanceof QueekApiError && error.code === "validation_failed") {
|
|
165
|
+
* console.error(error.field, error.errors, error.requestId);
|
|
166
|
+
* }
|
|
167
|
+
* }
|
|
168
|
+
*/
|
|
126
169
|
export declare class QueekApiError extends Error {
|
|
127
170
|
readonly status: number;
|
|
128
171
|
readonly code: string;
|
|
@@ -135,7 +178,26 @@ export declare class QueekApiError extends Error {
|
|
|
135
178
|
constructor(details: QueekErrorDetails);
|
|
136
179
|
get isRateLimited(): boolean;
|
|
137
180
|
get isAuthFailure(): boolean;
|
|
181
|
+
/**
|
|
182
|
+
* True when sending the same call again can succeed without any change:
|
|
183
|
+
* rate limits, gateway failures (502, 503, 504), timeouts and network
|
|
184
|
+
* errors. `requestWithRetry()` retries exactly these. A caller abort is
|
|
185
|
+
* never retryable.
|
|
186
|
+
*/
|
|
187
|
+
get isRetryable(): boolean;
|
|
138
188
|
}
|
|
189
|
+
/**
|
|
190
|
+
* The error for a call that never produced an HTTP answer. A caller abort is
|
|
191
|
+
* `request_aborted`, the client timeout is `request_timeout`, everything else
|
|
192
|
+
* (DNS, refused or reset connection, TLS) is `network_error`. The original
|
|
193
|
+
* error stays on `cause`.
|
|
194
|
+
*/
|
|
195
|
+
export declare function transportError(cause: unknown, signal?: AbortSignal): QueekApiError;
|
|
196
|
+
/**
|
|
197
|
+
* Combine a caller signal with the per-attempt timeout. Returns the caller
|
|
198
|
+
* signal untouched when `timeoutMs` is 0 or negative.
|
|
199
|
+
*/
|
|
200
|
+
export declare function requestSignal(signal: AbortSignal | undefined, timeoutMs: number): AbortSignal | undefined;
|
|
139
201
|
/**
|
|
140
202
|
* Build a `QueekApiError` from a status + parsed body + headers. Shared
|
|
141
203
|
* with the installation-token mint path (`tokens.ts`), so the merchant and
|
|
@@ -147,10 +209,17 @@ export interface RetryOptions {
|
|
|
147
209
|
maxAttempts?: number;
|
|
148
210
|
/** Base backoff between attempts in ms (default 250); doubles per attempt. */
|
|
149
211
|
baseDelayMs?: number;
|
|
150
|
-
/** Backoff ceiling in ms (default 5000).
|
|
212
|
+
/** Backoff ceiling in ms (default 5000). */
|
|
151
213
|
maxDelayMs?: number;
|
|
214
|
+
/**
|
|
215
|
+
* Longest `Retry-After` the call will wait out, in ms (default 30000). A
|
|
216
|
+
* 429 that asks for more is thrown at once instead of holding the caller.
|
|
217
|
+
*/
|
|
218
|
+
maxRetryAfterMs?: number;
|
|
152
219
|
/** Injectable clock for tests. Defaults to a real setTimeout sleep. */
|
|
153
220
|
sleep?: (ms: number) => Promise<void>;
|
|
221
|
+
/** Injectable randomness for the backoff jitter, in [0, 1). Defaults to `Math.random`. */
|
|
222
|
+
random?: () => number;
|
|
154
223
|
}
|
|
155
224
|
export interface QueekClient<TPaths = paths> {
|
|
156
225
|
/** `GET /store` — the handed-off key proving itself. Typed from the app's paths (`TPaths`, default: bundled shim). */
|
|
@@ -158,12 +227,66 @@ export interface QueekClient<TPaths = paths> {
|
|
|
158
227
|
/** Generic typed escape hatch: `request<T>("GET", "/orders", { query })`. Never retries. */
|
|
159
228
|
request<T>(method: string, path: string, options?: RequestOptions): Promise<T>;
|
|
160
229
|
/**
|
|
161
|
-
* `request()` with bounded retries on 429s
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
* a
|
|
230
|
+
* `request()` with bounded retries on 429s, 502/503/504, timeouts and
|
|
231
|
+
* network errors, with jittered exponential backoff. The SAME idempotency
|
|
232
|
+
* key is reused across attempts (generated once when the caller does not
|
|
233
|
+
* supply one), so a retry can never duplicate a write; a 429
|
|
234
|
+
* `Retry-After` is honoured over the backoff schedule. A caller abort is
|
|
235
|
+
* never retried.
|
|
165
236
|
*/
|
|
166
237
|
requestWithRetry<T>(method: string, path: string, options?: RequestOptions & RetryOptions): Promise<T>;
|
|
167
238
|
}
|
|
239
|
+
/**
|
|
240
|
+
* The retry loop both clients share. `run` receives the idempotency key to
|
|
241
|
+
* send (generated ONCE for writes when the caller gave none), so every
|
|
242
|
+
* attempt of one write carries the same key.
|
|
243
|
+
*/
|
|
244
|
+
export declare function runWithRetry<T>(method: string, options: RetryOptions & {
|
|
245
|
+
idempotencyKey?: string;
|
|
246
|
+
}, run: (idempotencyKey: string | undefined) => Promise<T>): Promise<T>;
|
|
247
|
+
/**
|
|
248
|
+
* Create a client for the Merchant API with one static installation key.
|
|
249
|
+
* Most apps want `createInstallationClient()` instead, which mints and
|
|
250
|
+
* refreshes the key for you.
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* const client = createQueekClient({ apiBase: install.api_base, apiKey: process.env.QUEEK_KEY ?? "" });
|
|
254
|
+
* const store = await client.getStore();
|
|
255
|
+
*/
|
|
168
256
|
export declare function createQueekClient<TPaths = paths>(clientOptions: QueekClientOptions): QueekClient<ResolvedPaths<TPaths>>;
|
|
169
|
-
|
|
257
|
+
/**
|
|
258
|
+
* Parse a non-JSON-safe error body: JSON when it parses, otherwise the first
|
|
259
|
+
* 500 characters as the error `message`.
|
|
260
|
+
*/
|
|
261
|
+
export declare function tryParseJson(text: string): unknown;
|
|
262
|
+
/**
|
|
263
|
+
* Parse an HTTP success body. An empty body is `null`; a body that is not
|
|
264
|
+
* JSON is an `invalid_response` error, never a silently mistyped value.
|
|
265
|
+
*/
|
|
266
|
+
export declare function parseSuccessBody<T>(response: Response, text: string): T;
|
|
267
|
+
/** One page of a cursor-paginated Merchant API list. */
|
|
268
|
+
export interface CursorPage<T> {
|
|
269
|
+
data: T[];
|
|
270
|
+
has_more: boolean;
|
|
271
|
+
next_cursor?: string | null;
|
|
272
|
+
}
|
|
273
|
+
export interface PaginateOptions {
|
|
274
|
+
/** Extra query parameters (filters, sort). `limit` and `starting_after` are managed for you. */
|
|
275
|
+
query?: Record<string, string | number | boolean | undefined>;
|
|
276
|
+
/** Rows per page. Each endpoint documents its own maximum. */
|
|
277
|
+
limit?: number;
|
|
278
|
+
signal?: AbortSignal;
|
|
279
|
+
/** Retry tuning for each page fetch. */
|
|
280
|
+
retry?: RetryOptions;
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Iterate every row of a cursor-paginated list (`data`, `has_more`,
|
|
284
|
+
* `next_cursor`, driven by `starting_after`), fetching one page at a time
|
|
285
|
+
* with `requestWithRetry()`. Stops when `has_more` is false.
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* for await (const order of paginate<{ p_id: string }>(client, "/orders", { limit: 50 })) {
|
|
289
|
+
* console.log(order.p_id);
|
|
290
|
+
* }
|
|
291
|
+
*/
|
|
292
|
+
export declare function paginate<T>(client: Pick<QueekClient<unknown>, "requestWithRetry">, path: string, options?: PaginateOptions): AsyncGenerator<T, void, undefined>;
|