@sdg.la/wishlist-stack-sdk 0.13.0 → 0.14.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/README.md +92 -19
- package/dist/index.cjs +196 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +122 -21
- package/dist/index.d.ts +122 -21
- package/dist/index.js +194 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -51,6 +51,10 @@ declare function withPagination<TExtra extends Query>(query?: TExtra, pagination
|
|
|
51
51
|
*/
|
|
52
52
|
declare function withSort<TExtra extends Query>(query?: TExtra, sort?: SortParams): (TExtra & SortParams) | undefined;
|
|
53
53
|
|
|
54
|
+
/**
|
|
55
|
+
* Create a new list. Do **not** send `variantIds` — the API may reject them.
|
|
56
|
+
* Create the list, then call `lists.addItems` or `lists.addItemsBatched`.
|
|
57
|
+
*/
|
|
54
58
|
type CreateListBody = {
|
|
55
59
|
name?: string;
|
|
56
60
|
description?: string;
|
|
@@ -64,6 +68,7 @@ type UpdateListBody = {
|
|
|
64
68
|
type AddItemsToListBody = {
|
|
65
69
|
items?: Array<{
|
|
66
70
|
variantId?: string;
|
|
71
|
+
/** Quantity for the item (1–999). */
|
|
67
72
|
quantity?: number;
|
|
68
73
|
note?: string;
|
|
69
74
|
properties?: Record<string, unknown> | null;
|
|
@@ -174,14 +179,23 @@ type GetListsResponse = {
|
|
|
174
179
|
lists: ListSummary[];
|
|
175
180
|
pagination: Pagination;
|
|
176
181
|
};
|
|
177
|
-
/** GET `/api/lists/:listId` */
|
|
178
|
-
type GetListResponse = ListDetail
|
|
182
|
+
/** GET `/api/lists/:listId` — items are paginated (max pageSize 25). */
|
|
183
|
+
type GetListResponse = ListDetail & {
|
|
184
|
+
pagination: Pagination;
|
|
185
|
+
};
|
|
179
186
|
type ListMutationResponse = Omit<ListSummary, "featuredProducts">;
|
|
180
187
|
type CreateListResponse = ListMutationResponse;
|
|
181
188
|
type DuplicateListResponse = ListMutationResponse;
|
|
182
189
|
type UpdateListDetailsResponse = ListMutationResponse;
|
|
183
190
|
type RemoveListResponse = OkResponse;
|
|
184
|
-
|
|
191
|
+
/** POST `/api/lists/:listId/add` — newly added items only. */
|
|
192
|
+
type AddItemsToListResponse = {
|
|
193
|
+
listId: string;
|
|
194
|
+
addedItems: HydratedWishlistItem[];
|
|
195
|
+
addedCount: number;
|
|
196
|
+
};
|
|
197
|
+
/** @deprecated Use `AddItemsToListResponse`. */
|
|
198
|
+
type AddItemsToListDeltaResponse = AddItemsToListResponse;
|
|
185
199
|
type UpdateListItemResponse = unknown;
|
|
186
200
|
type RemoveItemFromListResponse = OkResponse;
|
|
187
201
|
type ReorderListItemsResponse = {
|
|
@@ -196,12 +210,29 @@ type RevokeListSharedResponse = ListMutationResponse;
|
|
|
196
210
|
type RequestFn$2 = <TResponse = unknown, TBody = unknown>(args: RequestArgs<TBody>) => Promise<TResponse>;
|
|
197
211
|
type GetListsQuery = PaginatedQuery;
|
|
198
212
|
type GetListQuery = Omit<PaginatedQuery, "query">;
|
|
213
|
+
type AddItemsBatchedOptions = {
|
|
214
|
+
/** Max items per POST (API hard max is 25). */
|
|
215
|
+
batchSize?: number;
|
|
216
|
+
};
|
|
217
|
+
type GetByIdAllItemsOptions = {
|
|
218
|
+
pageSize?: number;
|
|
219
|
+
sortBy?: GetListQuery["sortBy"];
|
|
220
|
+
sortDirection?: GetListQuery["sortDirection"];
|
|
221
|
+
};
|
|
199
222
|
declare function createListsResource(request: RequestFn$2): {
|
|
200
223
|
/** Returns all lists for the authenticated customer under the current merchant. */
|
|
201
224
|
getAll: (query?: GetListsQuery) => Promise<GetListsResponse>;
|
|
202
225
|
/** Fetch list details by id. */
|
|
203
|
-
getById: (listId: string, query?: GetListQuery) => Promise<
|
|
204
|
-
/**
|
|
226
|
+
getById: (listId: string, query?: GetListQuery) => Promise<GetListResponse>;
|
|
227
|
+
/**
|
|
228
|
+
* Fetch every item on a list by walking pages (list detail is paginated;
|
|
229
|
+
* max pageSize 25).
|
|
230
|
+
*/
|
|
231
|
+
getByIdAllItems: (listId: string, opts?: GetByIdAllItemsOptions) => Promise<GetListResponse>;
|
|
232
|
+
/**
|
|
233
|
+
* Create a new list. Do not send `variantIds` — add items via `addItems`
|
|
234
|
+
* or `addItemsBatched` after create.
|
|
235
|
+
*/
|
|
205
236
|
create: (body: CreateListBody) => Promise<ListMutationResponse>;
|
|
206
237
|
/** Update list name/description. */
|
|
207
238
|
update: (listId: string, body: UpdateListBody) => Promise<ListMutationResponse>;
|
|
@@ -214,8 +245,17 @@ declare function createListsResource(request: RequestFn$2): {
|
|
|
214
245
|
* The copy is named `Copy of {original name}` and is not shared.
|
|
215
246
|
*/
|
|
216
247
|
duplicate: (listId: string, body?: DuplicateListBody) => Promise<ListMutationResponse>;
|
|
217
|
-
/**
|
|
218
|
-
|
|
248
|
+
/**
|
|
249
|
+
* Add one or more items to a list.
|
|
250
|
+
* Returns `{ listId, addedItems, addedCount }`. Prefer `addItemsBatched`
|
|
251
|
+
* when sending more than 25 items.
|
|
252
|
+
*/
|
|
253
|
+
addItems: (listId: string, body: AddItemsToListBody) => Promise<AddItemsToListResponse>;
|
|
254
|
+
/**
|
|
255
|
+
* Add items in sequential batches of ≤25 (API hard max).
|
|
256
|
+
* Merges `{ addedItems, addedCount }` across batches.
|
|
257
|
+
*/
|
|
258
|
+
addItemsBatched: (listId: string, body: AddItemsToListBody, opts?: AddItemsBatchedOptions) => Promise<AddItemsToListResponse>;
|
|
219
259
|
/** Update an item on a list. */
|
|
220
260
|
updateItem: (listId: string, itemId: string, body: UpdateListItemBody) => Promise<unknown>;
|
|
221
261
|
/** Remove an item from a list. */
|
|
@@ -230,7 +270,7 @@ declare function createListsResource(request: RequestFn$2): {
|
|
|
230
270
|
* Legacy endpoint kept in the OpenAPI spec (`/api/wishlists/{id}/add`).
|
|
231
271
|
* Prefer `addItems(listId, body)` unless your server only supports the legacy route.
|
|
232
272
|
*/
|
|
233
|
-
addItemsLegacy: (id: string, body: AddItemsToListBody) => Promise<
|
|
273
|
+
addItemsLegacy: (id: string, body: AddItemsToListBody) => Promise<AddItemsToListResponse>;
|
|
234
274
|
};
|
|
235
275
|
|
|
236
276
|
type CreateGroupBody = {
|
|
@@ -241,13 +281,20 @@ type UpdateGroupBody = {
|
|
|
241
281
|
name?: string;
|
|
242
282
|
description?: string;
|
|
243
283
|
};
|
|
244
|
-
|
|
245
|
-
|
|
284
|
+
/**
|
|
285
|
+
* Body for `POST /api/groups/reorder` — reorder the customer's groups.
|
|
286
|
+
* `groupIds` must list every group CUID in the desired order.
|
|
287
|
+
*/
|
|
288
|
+
type ReorderGroupsBody = {
|
|
289
|
+
groupIds: string[];
|
|
246
290
|
};
|
|
291
|
+
/** @deprecated Use `ReorderGroupsBody` (`groupIds` on `POST /api/groups/reorder`). */
|
|
292
|
+
type ReorderGroupBody = ReorderGroupsBody;
|
|
247
293
|
|
|
248
294
|
/**
|
|
249
295
|
* List embedded on a group when `includeLists=1` is passed to `groups.getAll()`.
|
|
250
|
-
* Items are
|
|
296
|
+
* Items are hydrated (same shape as `lists.getById` items).
|
|
297
|
+
* Expect at most **10 lists × 25 items** per group.
|
|
251
298
|
*/
|
|
252
299
|
type GroupSummaryList = {
|
|
253
300
|
id: string;
|
|
@@ -267,7 +314,10 @@ type GroupSummary = {
|
|
|
267
314
|
position: number;
|
|
268
315
|
shared: boolean;
|
|
269
316
|
listCount: number;
|
|
270
|
-
/**
|
|
317
|
+
/**
|
|
318
|
+
* Empty unless `includeLists=1` was requested.
|
|
319
|
+
* Cap: 10 lists × 25 items each.
|
|
320
|
+
*/
|
|
271
321
|
lists: GroupSummaryList[];
|
|
272
322
|
featuredItems: FeaturedItem[];
|
|
273
323
|
createdAt: string;
|
|
@@ -317,23 +367,40 @@ type CreateGroupResponse = GroupMutationResponse;
|
|
|
317
367
|
type DuplicateGroupResponse = GroupMutationResponse;
|
|
318
368
|
type UpdateGroupDetailsResponse = GroupMutationResponse;
|
|
319
369
|
type RemoveGroupResponse = OkResponse;
|
|
320
|
-
type
|
|
370
|
+
type ReorderGroupsResponse = {
|
|
371
|
+
updated: Array<{
|
|
372
|
+
id: string;
|
|
373
|
+
position: number;
|
|
374
|
+
}>;
|
|
375
|
+
};
|
|
376
|
+
/** @deprecated Prefer `ReorderGroupsResponse`. */
|
|
377
|
+
type ReorderGroupResponse = ReorderGroupsResponse;
|
|
321
378
|
type MarkGroupSharedResponse = GroupMutationResponse;
|
|
322
379
|
type RevokeGroupSharedResponse = GroupMutationResponse;
|
|
323
380
|
|
|
324
381
|
type RequestFn$1 = <TResponse = unknown, TBody = unknown>(args: RequestArgs<TBody>) => Promise<TResponse>;
|
|
325
382
|
type GetGroupsQuery = PaginatedQuery<{
|
|
326
383
|
/**
|
|
327
|
-
* When truthy (`true`, `1`, or `"1"`), each group includes
|
|
328
|
-
*
|
|
384
|
+
* When truthy (`true`, `1`, or `"1"`), each group includes embedded `lists`
|
|
385
|
+
* with hydrated items (`includeLists=1`).
|
|
386
|
+
*
|
|
387
|
+
* Expect at most **10 lists × 25 items** per group. For full data use
|
|
388
|
+
* `groups.getById` plus `lists.getById` / `lists.getByIdAllItems`. Groups
|
|
389
|
+
* endpoints may return **503** until the Groups API is enabled for the merchant.
|
|
329
390
|
*/
|
|
330
391
|
includeLists?: boolean | 1 | "1";
|
|
331
392
|
}>;
|
|
332
393
|
type GetGroupQuery = PaginatedQuery;
|
|
333
394
|
declare function createGroupsResource(request: RequestFn$1): {
|
|
334
|
-
/**
|
|
395
|
+
/**
|
|
396
|
+
* Fetch groups for the authenticated customer.
|
|
397
|
+
* May return **503** when the Groups API is disabled for this merchant.
|
|
398
|
+
*/
|
|
335
399
|
getAll: (query?: GetGroupsQuery) => Promise<GetGroupsResponse>;
|
|
336
|
-
/**
|
|
400
|
+
/**
|
|
401
|
+
* Fetch group details by id.
|
|
402
|
+
* May return **503** when the Groups API is disabled for this merchant.
|
|
403
|
+
*/
|
|
337
404
|
getById: (groupId: string, query?: GetGroupQuery) => Promise<GroupDetail>;
|
|
338
405
|
/** Create a new group. */
|
|
339
406
|
create: (body: CreateGroupBody) => Promise<GroupMutationResponse>;
|
|
@@ -346,8 +413,12 @@ declare function createGroupsResource(request: RequestFn$1): {
|
|
|
346
413
|
* The copy is named `Copy of {original name}` and is not shared.
|
|
347
414
|
*/
|
|
348
415
|
duplicate: (groupId: string) => Promise<GroupMutationResponse>;
|
|
349
|
-
/**
|
|
350
|
-
|
|
416
|
+
/**
|
|
417
|
+
* Reorder the customer's groups (`POST /api/groups/reorder`).
|
|
418
|
+
* Body must include `groupIds` in the desired order — not list IDs, and
|
|
419
|
+
* not a per-group path.
|
|
420
|
+
*/
|
|
421
|
+
reorder: (body: ReorderGroupsBody) => Promise<ReorderGroupsResponse>;
|
|
351
422
|
/** Mark group as shared (public read-only). */
|
|
352
423
|
share: (groupId: string) => Promise<GroupMutationResponse>;
|
|
353
424
|
/** Revoke group sharing. */
|
|
@@ -359,7 +430,7 @@ type GetSharedListQuery = Omit<PaginatedQuery, "query">;
|
|
|
359
430
|
type GetSharedGroupQuery = PaginatedQuery;
|
|
360
431
|
declare function createSharedResource(request: RequestFn): {
|
|
361
432
|
/** Public read-only view of a shared list. Requires only merchant apiKey. */
|
|
362
|
-
getSharedList: (listId: string, query?: GetSharedListQuery) => Promise<
|
|
433
|
+
getSharedList: (listId: string, query?: GetSharedListQuery) => Promise<GetListResponse>;
|
|
363
434
|
/** Public read-only view of a shared group. Requires only merchant apiKey. */
|
|
364
435
|
getSharedGroup: (groupId: string, query?: GetSharedGroupQuery) => Promise<GroupDetail>;
|
|
365
436
|
};
|
|
@@ -369,6 +440,10 @@ type CreateWishlistStackClientOptions = {
|
|
|
369
440
|
apiKey: string;
|
|
370
441
|
customerAccessToken?: string;
|
|
371
442
|
fetch?: FetchLike;
|
|
443
|
+
/** Default request timeout in ms when a call does not pass `timeoutMs`. */
|
|
444
|
+
defaultTimeoutMs?: number;
|
|
445
|
+
/** When true, retry once on HTTP 429 with jittered backoff (default: false). */
|
|
446
|
+
retryOnRateLimit?: boolean;
|
|
372
447
|
};
|
|
373
448
|
type WishlistStackClient$1 = {
|
|
374
449
|
lists: ReturnType<typeof createListsResource>;
|
|
@@ -389,6 +464,12 @@ type ApiErrorResponse = {
|
|
|
389
464
|
type GetSharedListResponse = GetListResponse;
|
|
390
465
|
type GetSharedGroupResponse = GetGroupResponse;
|
|
391
466
|
|
|
467
|
+
/**
|
|
468
|
+
* True when the value matches the add-items response shape
|
|
469
|
+
* (`{ listId, addedItems, addedCount }`).
|
|
470
|
+
*/
|
|
471
|
+
declare function isAddItemsDeltaResponse(value: AddItemsToListResponse | unknown): value is AddItemsToListResponse;
|
|
472
|
+
|
|
392
473
|
/**
|
|
393
474
|
* Class wrapper around `createWishlistStackClient()` for users who prefer `new`.
|
|
394
475
|
* Exposes `lists`, `groups`, and `shared` resources.
|
|
@@ -412,6 +493,14 @@ type WishlistStackApiErrorDetails<TBody = unknown> = {
|
|
|
412
493
|
method: string;
|
|
413
494
|
requestId?: string;
|
|
414
495
|
body?: TBody;
|
|
496
|
+
/** Value of the `Retry-After` response header when present (seconds or HTTP-date). */
|
|
497
|
+
retryAfter?: string;
|
|
498
|
+
/** Selected rate-limit response headers when present. */
|
|
499
|
+
rateLimit?: {
|
|
500
|
+
limit?: string;
|
|
501
|
+
remaining?: string;
|
|
502
|
+
reset?: string;
|
|
503
|
+
};
|
|
415
504
|
};
|
|
416
505
|
declare class WishlistStackApiError<TBody = unknown> extends Error {
|
|
417
506
|
readonly name = "WishlistStackApiError";
|
|
@@ -420,6 +509,8 @@ declare class WishlistStackApiError<TBody = unknown> extends Error {
|
|
|
420
509
|
readonly method: string;
|
|
421
510
|
readonly requestId?: string;
|
|
422
511
|
readonly body?: TBody;
|
|
512
|
+
readonly retryAfter?: string;
|
|
513
|
+
readonly rateLimit?: WishlistStackApiErrorDetails["rateLimit"];
|
|
423
514
|
constructor(message: string, details: WishlistStackApiErrorDetails<TBody>);
|
|
424
515
|
/**
|
|
425
516
|
* Best-effort extraction of API error items from the response body.
|
|
@@ -439,4 +530,14 @@ declare class WishlistStackApiError<TBody = unknown> extends Error {
|
|
|
439
530
|
*/
|
|
440
531
|
declare function isWishlistStackApiError(value: unknown): value is WishlistStackApiError<unknown>;
|
|
441
532
|
|
|
442
|
-
|
|
533
|
+
/**
|
|
534
|
+
* Clamp a page size to the API-allowed range (1–25) so requests
|
|
535
|
+
* do not send invalid `pageSize` values.
|
|
536
|
+
*/
|
|
537
|
+
declare function clampPageSize(n: number): number;
|
|
538
|
+
/**
|
|
539
|
+
* Clamp a 1-based page index to at least 1.
|
|
540
|
+
*/
|
|
541
|
+
declare function clampPage(n: number): number;
|
|
542
|
+
|
|
543
|
+
export { type AddItemsToListBody, type AddItemsToListDeltaResponse, type AddItemsToListResponse, type ApiErrorItem, type ApiErrorResponse, type CreateGroupBody, type CreateGroupResponse, type CreateListBody, type CreateListResponse, type CreateWishlistStackClientOptions, DEFAULT_PAGE, DEFAULT_PAGE_SIZE, type DuplicateGroupResponse, type DuplicateListBody, type DuplicateListResponse, type FeaturedItem, type GetGroupResponse, type GetGroupsResponse, type GetListResponse, type GetListsResponse, type GetSharedGroupResponse, type GetSharedListResponse, type GroupDetail, type GroupDetailList, type GroupMutationResponse, type GroupSummary, type GroupSummaryList, type HydratedProduct, type HydratedVariant, type HydratedWishlistItem, type Id, type Image, type ListDetail, type ListMutationResponse, type ListSummary, type MarkGroupSharedResponse, type MarkListSharedResponse, type Metafield, type Money, type OkResponse, type PaginatedQuery, type Pagination, type PaginationParams, type RemoveGroupResponse, type RemoveItemFromListResponse, type RemoveListResponse, type ReorderGroupBody, type ReorderGroupResponse, type ReorderGroupsBody, type ReorderGroupsResponse, type ReorderListItemsBody, type ReorderListItemsResponse, type RevokeGroupSharedResponse, type RevokeListSharedResponse, SORT_DIRECTIONS, SORT_FIELDS, type SelectedOption, type SortDirection, type SortField, type SortParams, type UpdateGroupBody, type UpdateGroupDetailsResponse, type UpdateListBody, type UpdateListDetailsResponse, type UpdateListItemBody, type UpdateListItemResponse, WishlistStackApiError, type WishlistStackClient$1 as WishlistStackClient, WishlistStackClient as WishlistStackSDK, clampPage, clampPageSize, createWishlistStackClient, isAddItemsDeltaResponse, isWishlistStackApiError, withPagination, withSort };
|
package/dist/index.js
CHANGED
|
@@ -55,11 +55,15 @@ var WishlistStackApiError = class extends Error {
|
|
|
55
55
|
__publicField(this, "method");
|
|
56
56
|
__publicField(this, "requestId");
|
|
57
57
|
__publicField(this, "body");
|
|
58
|
+
__publicField(this, "retryAfter");
|
|
59
|
+
__publicField(this, "rateLimit");
|
|
58
60
|
this.status = details.status;
|
|
59
61
|
this.url = details.url;
|
|
60
62
|
this.method = details.method;
|
|
61
63
|
this.requestId = details.requestId;
|
|
62
64
|
this.body = details.body;
|
|
65
|
+
this.retryAfter = details.retryAfter;
|
|
66
|
+
this.rateLimit = details.rateLimit;
|
|
63
67
|
}
|
|
64
68
|
/**
|
|
65
69
|
* Best-effort extraction of API error items from the response body.
|
|
@@ -91,6 +95,42 @@ async function safeParseJson(res) {
|
|
|
91
95
|
return void 0;
|
|
92
96
|
}
|
|
93
97
|
}
|
|
98
|
+
function pickRequestId(res) {
|
|
99
|
+
return res.headers.get("fly-request-id") ?? res.headers.get("x-request-id") ?? res.headers.get("x-nf-request-id") ?? res.headers.get("x-netlify-request-id") ?? void 0;
|
|
100
|
+
}
|
|
101
|
+
function pickRateLimit(res) {
|
|
102
|
+
const limit = res.headers.get("x-ratelimit-limit") ?? void 0;
|
|
103
|
+
const remaining = res.headers.get("x-ratelimit-remaining") ?? void 0;
|
|
104
|
+
const reset = res.headers.get("x-ratelimit-reset") ?? void 0;
|
|
105
|
+
if (!limit && !remaining && !reset) return void 0;
|
|
106
|
+
return { limit, remaining, reset };
|
|
107
|
+
}
|
|
108
|
+
function errorMessageFor(status, path) {
|
|
109
|
+
if (status === 503 && path.includes("/api/groups")) {
|
|
110
|
+
return "Groups API disabled for this merchant";
|
|
111
|
+
}
|
|
112
|
+
return `Wishlist Stack API request failed (${status})`;
|
|
113
|
+
}
|
|
114
|
+
function parseRetryAfterMs(retryAfter) {
|
|
115
|
+
if (!retryAfter) return 0;
|
|
116
|
+
const asSeconds = Number(retryAfter);
|
|
117
|
+
if (Number.isFinite(asSeconds) && asSeconds >= 0) {
|
|
118
|
+
return Math.min(asSeconds * 1e3, 3e4);
|
|
119
|
+
}
|
|
120
|
+
const asDate = Date.parse(retryAfter);
|
|
121
|
+
if (!Number.isNaN(asDate)) {
|
|
122
|
+
return Math.min(Math.max(0, asDate - Date.now()), 3e4);
|
|
123
|
+
}
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
function jitterDelayMs(baseMs) {
|
|
127
|
+
const base = Math.max(100, baseMs || 500);
|
|
128
|
+
const jitter = Math.floor(Math.random() * 250);
|
|
129
|
+
return Math.min(base + jitter, 3e4);
|
|
130
|
+
}
|
|
131
|
+
function sleep(ms) {
|
|
132
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
133
|
+
}
|
|
94
134
|
function createRequest(ctx) {
|
|
95
135
|
return async function request(args) {
|
|
96
136
|
const url = `${ctx.baseUrl}${args.path}${toQueryString(args.query)}`;
|
|
@@ -107,25 +147,39 @@ function createRequest(ctx) {
|
|
|
107
147
|
headers["content-type"] ?? (headers["content-type"] = "application/json");
|
|
108
148
|
body = JSON.stringify(args.body);
|
|
109
149
|
}
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
150
|
+
const timeoutMs = args.timeoutMs ?? ctx.defaultTimeoutMs;
|
|
151
|
+
const doFetch = async () => {
|
|
152
|
+
const controller = timeoutMs ? new AbortController() : void 0;
|
|
153
|
+
const timeout = timeoutMs && controller ? setTimeout(() => controller.abort(), timeoutMs) : void 0;
|
|
154
|
+
const signal = args.signal ? args.signal : controller ? controller.signal : void 0;
|
|
155
|
+
try {
|
|
156
|
+
return await ctx.fetch(url, {
|
|
157
|
+
method: args.method,
|
|
158
|
+
headers,
|
|
159
|
+
body,
|
|
160
|
+
signal
|
|
161
|
+
});
|
|
162
|
+
} finally {
|
|
163
|
+
if (timeout) clearTimeout(timeout);
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
let res = await doFetch();
|
|
167
|
+
if (!res.ok && res.status === 429 && ctx.retryOnRateLimit) {
|
|
168
|
+
const retryAfter = res.headers.get("retry-after") ?? void 0;
|
|
169
|
+
await sleep(jitterDelayMs(parseRetryAfterMs(retryAfter)));
|
|
170
|
+
res = await doFetch();
|
|
171
|
+
}
|
|
120
172
|
if (!res.ok) {
|
|
121
173
|
const errBody = await safeParseJson(res);
|
|
122
|
-
const
|
|
123
|
-
throw new WishlistStackApiError(
|
|
174
|
+
const retryAfter = res.headers.get("retry-after") ?? void 0;
|
|
175
|
+
throw new WishlistStackApiError(errorMessageFor(res.status, args.path), {
|
|
124
176
|
status: res.status,
|
|
125
177
|
url,
|
|
126
178
|
method: args.method,
|
|
127
|
-
requestId,
|
|
128
|
-
body: errBody
|
|
179
|
+
requestId: pickRequestId(res),
|
|
180
|
+
body: errBody,
|
|
181
|
+
retryAfter,
|
|
182
|
+
rateLimit: pickRateLimit(res)
|
|
129
183
|
});
|
|
130
184
|
}
|
|
131
185
|
if (res.status === 204) return void 0;
|
|
@@ -138,8 +192,38 @@ function createRequest(ctx) {
|
|
|
138
192
|
};
|
|
139
193
|
}
|
|
140
194
|
|
|
195
|
+
// src/helpers/pagination.ts
|
|
196
|
+
function clampPageSize(n) {
|
|
197
|
+
if (!Number.isFinite(n)) return MAX_PAGINATION_SIZE;
|
|
198
|
+
return Math.min(MAX_PAGINATION_SIZE, Math.max(1, Math.trunc(n)));
|
|
199
|
+
}
|
|
200
|
+
function clampPage(n) {
|
|
201
|
+
if (!Number.isFinite(n)) return 1;
|
|
202
|
+
return Math.max(1, Math.trunc(n));
|
|
203
|
+
}
|
|
204
|
+
|
|
141
205
|
// src/resources/lists/index.ts
|
|
206
|
+
function chunkItems(items, batchSize) {
|
|
207
|
+
const size = Math.max(1, Math.min(MAX_PAGINATION_SIZE, Math.trunc(batchSize) || MAX_PAGINATION_SIZE));
|
|
208
|
+
const chunks = [];
|
|
209
|
+
for (let i = 0; i < items.length; i += size) {
|
|
210
|
+
chunks.push(items.slice(i, i + size));
|
|
211
|
+
}
|
|
212
|
+
return chunks.length ? chunks : [[]];
|
|
213
|
+
}
|
|
142
214
|
function createListsResource(request) {
|
|
215
|
+
const getById = (listId, query) => request({
|
|
216
|
+
method: "GET",
|
|
217
|
+
path: `/api/lists/${encodeURIComponent(listId)}`,
|
|
218
|
+
auth: "authenticated",
|
|
219
|
+
query
|
|
220
|
+
});
|
|
221
|
+
const addItems = (listId, body) => request({
|
|
222
|
+
method: "POST",
|
|
223
|
+
path: `/api/lists/${encodeURIComponent(listId)}/add`,
|
|
224
|
+
auth: "authenticated",
|
|
225
|
+
body
|
|
226
|
+
});
|
|
143
227
|
return {
|
|
144
228
|
/** Returns all lists for the authenticated customer under the current merchant. */
|
|
145
229
|
getAll: (query) => request({
|
|
@@ -149,13 +233,47 @@ function createListsResource(request) {
|
|
|
149
233
|
query
|
|
150
234
|
}),
|
|
151
235
|
/** Fetch list details by id. */
|
|
152
|
-
getById
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
236
|
+
getById,
|
|
237
|
+
/**
|
|
238
|
+
* Fetch every item on a list by walking pages (list detail is paginated;
|
|
239
|
+
* max pageSize 25).
|
|
240
|
+
*/
|
|
241
|
+
getByIdAllItems: async (listId, opts) => {
|
|
242
|
+
const pageSize = clampPageSize(opts?.pageSize ?? MAX_PAGINATION_SIZE);
|
|
243
|
+
const sortBy = opts?.sortBy;
|
|
244
|
+
const sortDirection = opts?.sortDirection;
|
|
245
|
+
const first = await getById(listId, {
|
|
246
|
+
page: 1,
|
|
247
|
+
pageSize,
|
|
248
|
+
...sortBy ? { sortBy } : {},
|
|
249
|
+
...sortDirection ? { sortDirection } : {}
|
|
250
|
+
});
|
|
251
|
+
const totalPages = first.pagination.totalPages;
|
|
252
|
+
const allItems = [...first.items ?? []];
|
|
253
|
+
for (let page = 2; page <= totalPages; page++) {
|
|
254
|
+
const next = await getById(listId, {
|
|
255
|
+
page: clampPage(page),
|
|
256
|
+
pageSize,
|
|
257
|
+
...sortBy ? { sortBy } : {},
|
|
258
|
+
...sortDirection ? { sortDirection } : {}
|
|
259
|
+
});
|
|
260
|
+
allItems.push(...next.items ?? []);
|
|
261
|
+
}
|
|
262
|
+
const pagination = {
|
|
263
|
+
...first.pagination,
|
|
264
|
+
page: 1,
|
|
265
|
+
pageSize
|
|
266
|
+
};
|
|
267
|
+
return {
|
|
268
|
+
...first,
|
|
269
|
+
items: allItems,
|
|
270
|
+
pagination
|
|
271
|
+
};
|
|
272
|
+
},
|
|
273
|
+
/**
|
|
274
|
+
* Create a new list. Do not send `variantIds` — add items via `addItems`
|
|
275
|
+
* or `addItemsBatched` after create.
|
|
276
|
+
*/
|
|
159
277
|
create: (body) => request({
|
|
160
278
|
method: "POST",
|
|
161
279
|
path: "/api/lists",
|
|
@@ -187,13 +305,33 @@ function createListsResource(request) {
|
|
|
187
305
|
auth: "authenticated",
|
|
188
306
|
body
|
|
189
307
|
}),
|
|
190
|
-
/**
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Add one or more items to a list.
|
|
310
|
+
* Returns `{ listId, addedItems, addedCount }`. Prefer `addItemsBatched`
|
|
311
|
+
* when sending more than 25 items.
|
|
312
|
+
*/
|
|
313
|
+
addItems,
|
|
314
|
+
/**
|
|
315
|
+
* Add items in sequential batches of ≤25 (API hard max).
|
|
316
|
+
* Merges `{ addedItems, addedCount }` across batches.
|
|
317
|
+
*/
|
|
318
|
+
addItemsBatched: async (listId, body, opts) => {
|
|
319
|
+
const items = body.items ?? [];
|
|
320
|
+
const batchSize = opts?.batchSize ?? MAX_PAGINATION_SIZE;
|
|
321
|
+
const chunks = chunkItems(items, batchSize);
|
|
322
|
+
const merged = {
|
|
323
|
+
listId,
|
|
324
|
+
addedItems: [],
|
|
325
|
+
addedCount: 0
|
|
326
|
+
};
|
|
327
|
+
for (const chunk of chunks) {
|
|
328
|
+
const res = await addItems(listId, { items: chunk });
|
|
329
|
+
merged.listId = res.listId;
|
|
330
|
+
merged.addedItems.push(...res.addedItems);
|
|
331
|
+
merged.addedCount += res.addedCount;
|
|
332
|
+
}
|
|
333
|
+
return merged;
|
|
334
|
+
},
|
|
197
335
|
/** Update an item on a list. */
|
|
198
336
|
updateItem: (listId, itemId, body) => request({
|
|
199
337
|
method: "PATCH",
|
|
@@ -250,14 +388,20 @@ function normalizeGetGroupsQuery(query) {
|
|
|
250
388
|
}
|
|
251
389
|
function createGroupsResource(request) {
|
|
252
390
|
return {
|
|
253
|
-
/**
|
|
391
|
+
/**
|
|
392
|
+
* Fetch groups for the authenticated customer.
|
|
393
|
+
* May return **503** when the Groups API is disabled for this merchant.
|
|
394
|
+
*/
|
|
254
395
|
getAll: (query) => request({
|
|
255
396
|
method: "GET",
|
|
256
397
|
path: "/api/groups",
|
|
257
398
|
auth: "authenticated",
|
|
258
399
|
query: normalizeGetGroupsQuery(query)
|
|
259
400
|
}),
|
|
260
|
-
/**
|
|
401
|
+
/**
|
|
402
|
+
* Fetch group details by id.
|
|
403
|
+
* May return **503** when the Groups API is disabled for this merchant.
|
|
404
|
+
*/
|
|
261
405
|
getById: (groupId, query) => request({
|
|
262
406
|
method: "GET",
|
|
263
407
|
path: `/api/groups/${encodeURIComponent(groupId)}`,
|
|
@@ -293,10 +437,14 @@ function createGroupsResource(request) {
|
|
|
293
437
|
path: `/api/groups/${encodeURIComponent(groupId)}/duplicate`,
|
|
294
438
|
auth: "authenticated"
|
|
295
439
|
}),
|
|
296
|
-
/**
|
|
297
|
-
|
|
440
|
+
/**
|
|
441
|
+
* Reorder the customer's groups (`POST /api/groups/reorder`).
|
|
442
|
+
* Body must include `groupIds` in the desired order — not list IDs, and
|
|
443
|
+
* not a per-group path.
|
|
444
|
+
*/
|
|
445
|
+
reorder: (body) => request({
|
|
298
446
|
method: "POST",
|
|
299
|
-
path:
|
|
447
|
+
path: "/api/groups/reorder",
|
|
300
448
|
auth: "authenticated",
|
|
301
449
|
body
|
|
302
450
|
}),
|
|
@@ -352,7 +500,9 @@ function createWishlistStackClient(opts) {
|
|
|
352
500
|
baseUrl: opts.baseUrl ?? BASE_URL,
|
|
353
501
|
apiKey: opts.apiKey,
|
|
354
502
|
customerAccessToken: opts.customerAccessToken,
|
|
355
|
-
fetch: fetchImpl
|
|
503
|
+
fetch: fetchImpl,
|
|
504
|
+
defaultTimeoutMs: opts.defaultTimeoutMs,
|
|
505
|
+
retryOnRateLimit: opts.retryOnRateLimit
|
|
356
506
|
});
|
|
357
507
|
return {
|
|
358
508
|
lists: createListsResource(request),
|
|
@@ -402,6 +552,15 @@ function withSort(query, sort) {
|
|
|
402
552
|
return { ...query ?? {}, ...sort ?? {} };
|
|
403
553
|
}
|
|
404
554
|
|
|
405
|
-
|
|
555
|
+
// src/types/guards.ts
|
|
556
|
+
function isRecord2(value) {
|
|
557
|
+
return typeof value === "object" && value !== null;
|
|
558
|
+
}
|
|
559
|
+
function isAddItemsDeltaResponse(value) {
|
|
560
|
+
if (!isRecord2(value)) return false;
|
|
561
|
+
return typeof value.listId === "string" && Array.isArray(value.addedItems) && typeof value.addedCount === "number";
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export { DEFAULT_PAGE, DEFAULT_PAGE_SIZE, SORT_DIRECTIONS, SORT_FIELDS, WishlistStackApiError, WishlistStackClient as WishlistStackSDK, clampPage, clampPageSize, createWishlistStackClient, isAddItemsDeltaResponse, isWishlistStackApiError, withPagination, withSort };
|
|
406
565
|
//# sourceMappingURL=index.js.map
|
|
407
566
|
//# sourceMappingURL=index.js.map
|