@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/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
- type AddItemsToListResponse = unknown;
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<ListDetail>;
204
- /** Create a new list. */
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
- /** Add one or more items to a list. */
218
- addItems: (listId: string, body: AddItemsToListBody) => Promise<unknown>;
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<unknown>;
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
- type ReorderGroupBody = {
245
- listIds?: string[];
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 fully hydrated (same shape as `lists.getById`).
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
- /** Empty unless `includeLists=1` was requested. */
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 ReorderGroupResponse = unknown;
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 its full `lists`
328
- * array with hydrated items. Sent to the API as `includeLists=1`.
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
- /** Fetch groups for the authenticated customer. */
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
- /** Fetch group details by id. */
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
- /** Reorder a group (spec uses listIds payload). */
350
- reorder: (groupId: string, body: ReorderGroupBody) => Promise<unknown>;
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<ListDetail>;
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
- export { type AddItemsToListBody, 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 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, createWishlistStackClient, isWishlistStackApiError, withPagination, withSort };
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 controller = args.timeoutMs ? new AbortController() : void 0;
111
- const timeout = args.timeoutMs && controller ? setTimeout(() => controller.abort(), args.timeoutMs) : void 0;
112
- const signal = args.signal ? args.signal : controller ? controller.signal : void 0;
113
- const res = await ctx.fetch(url, {
114
- method: args.method,
115
- headers,
116
- body,
117
- signal
118
- });
119
- if (timeout) clearTimeout(timeout);
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 requestId = res.headers.get("fly-request-id") ?? res.headers.get("x-request-id") ?? void 0;
123
- throw new WishlistStackApiError(`Wishlist Stack API request failed (${res.status})`, {
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: (listId, query) => request({
153
- method: "GET",
154
- path: `/api/lists/${encodeURIComponent(listId)}`,
155
- auth: "authenticated",
156
- query
157
- }),
158
- /** Create a new list. */
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
- /** Add one or more items to a list. */
191
- addItems: (listId, body) => request({
192
- method: "POST",
193
- path: `/api/lists/${encodeURIComponent(listId)}/add`,
194
- auth: "authenticated",
195
- body
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
- /** Fetch groups for the authenticated customer. */
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
- /** Fetch group details by id. */
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
- /** Reorder a group (spec uses listIds payload). */
297
- reorder: (groupId, body) => request({
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: `/api/groups/${encodeURIComponent(groupId)}/reorder`,
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
- export { DEFAULT_PAGE, DEFAULT_PAGE_SIZE, SORT_DIRECTIONS, SORT_FIELDS, WishlistStackApiError, WishlistStackClient as WishlistStackSDK, createWishlistStackClient, isWishlistStackApiError, withPagination, withSort };
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