@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.
Files changed (107) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +340 -337
  3. package/dist/app-auth.d.ts +1 -2
  4. package/dist/app-auth.js +1 -2
  5. package/dist/auth-fetch.d.ts +12 -1
  6. package/dist/auth-fetch.js +13 -2
  7. package/dist/background.d.ts +9 -1
  8. package/dist/background.js +9 -1
  9. package/dist/browser.d.ts +1 -2
  10. package/dist/browser.js +1 -2
  11. package/dist/client.d.ts +130 -7
  12. package/dist/client.js +198 -41
  13. package/dist/collected.d.ts +0 -1
  14. package/dist/collected.js +0 -1
  15. package/dist/crypto.d.ts +11 -1
  16. package/dist/crypto.js +22 -7
  17. package/dist/delivery.d.ts +18 -1
  18. package/dist/delivery.js +26 -17
  19. package/dist/frame.d.ts +0 -1
  20. package/dist/frame.js +0 -1
  21. package/dist/handoff.d.ts +0 -1
  22. package/dist/handoff.js +0 -1
  23. package/dist/hono.d.ts +0 -1
  24. package/dist/hono.js +0 -1
  25. package/dist/index.d.ts +1 -2
  26. package/dist/index.js +1 -2
  27. package/dist/install-handlers.d.ts +22 -2
  28. package/dist/install-handlers.js +37 -7
  29. package/dist/logger.d.ts +7 -1
  30. package/dist/logger.js +7 -1
  31. package/dist/merchant-schema.d.ts +1184 -336
  32. package/dist/merchant-schema.js +0 -1
  33. package/dist/proxy.d.ts +2 -3
  34. package/dist/proxy.js +4 -5
  35. package/dist/react.d.ts +9 -1
  36. package/dist/react.js +10 -2
  37. package/dist/resync.d.ts +11 -1
  38. package/dist/resync.js +18 -16
  39. package/dist/scopes.d.ts +14 -1
  40. package/dist/scopes.js +18 -17
  41. package/dist/server.d.ts +0 -1
  42. package/dist/server.js +0 -1
  43. package/dist/session.d.ts +20 -1
  44. package/dist/session.js +20 -1
  45. package/dist/signatures.d.ts +26 -1
  46. package/dist/signatures.js +29 -3
  47. package/dist/store.d.ts +6 -1
  48. package/dist/store.js +0 -1
  49. package/dist/theme.d.ts +0 -1
  50. package/dist/theme.js +0 -1
  51. package/dist/tokens.d.ts +9 -1
  52. package/dist/tokens.js +23 -47
  53. package/dist/webhooks.d.ts +28 -3
  54. package/dist/webhooks.js +25 -7
  55. package/package.json +18 -2
  56. package/dist/app-auth.d.ts.map +0 -1
  57. package/dist/app-auth.js.map +0 -1
  58. package/dist/auth-fetch.d.ts.map +0 -1
  59. package/dist/auth-fetch.js.map +0 -1
  60. package/dist/background.d.ts.map +0 -1
  61. package/dist/background.js.map +0 -1
  62. package/dist/browser.d.ts.map +0 -1
  63. package/dist/browser.js.map +0 -1
  64. package/dist/client.d.ts.map +0 -1
  65. package/dist/client.js.map +0 -1
  66. package/dist/collected.d.ts.map +0 -1
  67. package/dist/collected.js.map +0 -1
  68. package/dist/crypto.d.ts.map +0 -1
  69. package/dist/crypto.js.map +0 -1
  70. package/dist/delivery.d.ts.map +0 -1
  71. package/dist/delivery.js.map +0 -1
  72. package/dist/frame.d.ts.map +0 -1
  73. package/dist/frame.js.map +0 -1
  74. package/dist/handoff.d.ts.map +0 -1
  75. package/dist/handoff.js.map +0 -1
  76. package/dist/hono.d.ts.map +0 -1
  77. package/dist/hono.js.map +0 -1
  78. package/dist/index.d.ts.map +0 -1
  79. package/dist/index.js.map +0 -1
  80. package/dist/install-handlers.d.ts.map +0 -1
  81. package/dist/install-handlers.js.map +0 -1
  82. package/dist/logger.d.ts.map +0 -1
  83. package/dist/logger.js.map +0 -1
  84. package/dist/merchant-schema.d.ts.map +0 -1
  85. package/dist/merchant-schema.js.map +0 -1
  86. package/dist/proxy.d.ts.map +0 -1
  87. package/dist/proxy.js.map +0 -1
  88. package/dist/react.d.ts.map +0 -1
  89. package/dist/react.js.map +0 -1
  90. package/dist/resync.d.ts.map +0 -1
  91. package/dist/resync.js.map +0 -1
  92. package/dist/scopes.d.ts.map +0 -1
  93. package/dist/scopes.js.map +0 -1
  94. package/dist/server.d.ts.map +0 -1
  95. package/dist/server.js.map +0 -1
  96. package/dist/session.d.ts.map +0 -1
  97. package/dist/session.js.map +0 -1
  98. package/dist/signatures.d.ts.map +0 -1
  99. package/dist/signatures.js.map +0 -1
  100. package/dist/store.d.ts.map +0 -1
  101. package/dist/store.js.map +0 -1
  102. package/dist/theme.d.ts.map +0 -1
  103. package/dist/theme.js.map +0 -1
  104. package/dist/tokens.d.ts.map +0 -1
  105. package/dist/tokens.js.map +0 -1
  106. package/dist/webhooks.d.ts.map +0 -1
  107. package/dist/webhooks.js.map +0 -1
@@ -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 (clock skew, GitHub's recommendation). */
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 (clock skew, GitHub's recommendation). */
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
@@ -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
@@ -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
@@ -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
@@ -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-app bridge plus
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-app bridge plus
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 the SAME idempotency key.
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). A 429 `Retry-After` can exceed it. */
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 and network errors. The SAME
162
- * idempotency key is reused across attempts (generated once when the
163
- * caller does not supply one), so a retry can never duplicate a write;
164
- * a 429 `Retry-After` is honoured over the backoff schedule.
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
- //# sourceMappingURL=client.d.ts.map
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>;