@01.software/sdk 0.29.0 → 0.31.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 (78) hide show
  1. package/README.md +331 -77
  2. package/dist/analytics/react.cjs +4 -1
  3. package/dist/analytics/react.cjs.map +1 -1
  4. package/dist/analytics/react.js +4 -1
  5. package/dist/analytics/react.js.map +1 -1
  6. package/dist/analytics.cjs +4 -1
  7. package/dist/analytics.cjs.map +1 -1
  8. package/dist/analytics.js +4 -1
  9. package/dist/analytics.js.map +1 -1
  10. package/dist/client.cjs +1541 -0
  11. package/dist/client.cjs.map +1 -0
  12. package/dist/client.d.cts +28 -0
  13. package/dist/client.d.ts +28 -0
  14. package/dist/client.js +1518 -0
  15. package/dist/client.js.map +1 -0
  16. package/dist/collection-client-ByzY3hWK.d.ts +218 -0
  17. package/dist/collection-client-DFXXz0vk.d.cts +218 -0
  18. package/dist/{const-DAjQYNuM.d.ts → const-AytzliEu.d.cts} +5 -7
  19. package/dist/{const-Dsixdi6z.d.cts → const-BGCP-OJL.d.ts} +5 -7
  20. package/dist/index-BGEhoDUs.d.cts +106 -0
  21. package/dist/index-BGEhoDUs.d.ts +106 -0
  22. package/dist/index.cjs +1006 -1615
  23. package/dist/index.cjs.map +1 -1
  24. package/dist/index.d.cts +11 -115
  25. package/dist/index.d.ts +11 -115
  26. package/dist/index.js +932 -1559
  27. package/dist/index.js.map +1 -1
  28. package/dist/metadata.cjs +91 -0
  29. package/dist/metadata.cjs.map +1 -0
  30. package/dist/metadata.d.cts +58 -0
  31. package/dist/metadata.d.ts +58 -0
  32. package/dist/metadata.js +68 -0
  33. package/dist/metadata.js.map +1 -0
  34. package/dist/{payload-types-Ci-ZA7aM.d.cts → payload-types-Wa4-eC6x.d.cts} +794 -532
  35. package/dist/{payload-types-Ci-ZA7aM.d.ts → payload-types-Wa4-eC6x.d.ts} +794 -532
  36. package/dist/query.cjs +1841 -0
  37. package/dist/query.cjs.map +1 -0
  38. package/dist/query.d.cts +244 -0
  39. package/dist/query.d.ts +244 -0
  40. package/dist/query.js +1836 -0
  41. package/dist/query.js.map +1 -0
  42. package/dist/realtime.cjs +4 -1
  43. package/dist/realtime.cjs.map +1 -1
  44. package/dist/realtime.d.cts +2 -2
  45. package/dist/realtime.d.ts +2 -2
  46. package/dist/realtime.js +4 -1
  47. package/dist/realtime.js.map +1 -1
  48. package/dist/{server-BINWywT8.d.cts → server-CrsPyqEc.d.cts} +14 -31
  49. package/dist/{server-BINWywT8.d.ts → server-CrsPyqEc.d.ts} +14 -31
  50. package/dist/server.cjs +430 -846
  51. package/dist/server.cjs.map +1 -1
  52. package/dist/server.d.cts +137 -7
  53. package/dist/server.d.ts +137 -7
  54. package/dist/server.js +430 -864
  55. package/dist/server.js.map +1 -1
  56. package/dist/{server-Cv0Q4dPQ.d.ts → types-BX2mqDf6.d.ts} +270 -743
  57. package/dist/{types-BWq_WlbB.d.ts → types-CVA10VC-.d.ts} +6 -2
  58. package/dist/{types-zKjATmDK.d.cts → types-CmLG-7RL.d.cts} +6 -2
  59. package/dist/{server-C0C8dtms.d.cts → types-DChFjQGz.d.cts} +270 -743
  60. package/dist/ui/canvas/server.cjs +7 -6
  61. package/dist/ui/canvas/server.cjs.map +1 -1
  62. package/dist/ui/canvas/server.d.cts +1 -3
  63. package/dist/ui/canvas/server.d.ts +1 -3
  64. package/dist/ui/canvas/server.js +7 -6
  65. package/dist/ui/canvas/server.js.map +1 -1
  66. package/dist/ui/canvas.cjs +11 -10
  67. package/dist/ui/canvas.cjs.map +1 -1
  68. package/dist/ui/canvas.d.cts +29 -6
  69. package/dist/ui/canvas.d.ts +29 -6
  70. package/dist/ui/canvas.js +11 -10
  71. package/dist/ui/canvas.js.map +1 -1
  72. package/dist/ui/form.d.cts +1 -1
  73. package/dist/ui/form.d.ts +1 -1
  74. package/dist/ui/video.d.cts +1 -1
  75. package/dist/ui/video.d.ts +1 -1
  76. package/dist/webhook.d.cts +3 -3
  77. package/dist/webhook.d.ts +3 -3
  78. package/package.json +84 -15
package/dist/index.js CHANGED
@@ -1,242 +1,3 @@
1
- // src/utils/types.ts
2
- var resolveRelation = (ref) => {
3
- if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
4
- return null;
5
- return ref;
6
- };
7
-
8
- // src/core/metadata/index.ts
9
- function extractSeo(doc) {
10
- const seo = doc.seo ?? {};
11
- const og = seo.openGraph ?? {};
12
- return {
13
- title: seo.title ?? doc.title ?? null,
14
- description: seo.description ?? null,
15
- noIndex: seo.noIndex ?? null,
16
- canonical: seo.canonical ?? null,
17
- openGraph: {
18
- title: og.title ?? null,
19
- description: og.description ?? null,
20
- image: og.image ?? null
21
- }
22
- };
23
- }
24
- function generateMetadata(input, options) {
25
- const title = input.title ?? void 0;
26
- const description = input.description ?? void 0;
27
- const ogTitle = input.openGraph?.title ?? title;
28
- const ogDescription = input.openGraph?.description ?? description;
29
- const image = resolveMetaImage(input.openGraph?.image);
30
- return {
31
- title,
32
- description,
33
- ...input.noIndex && { robots: { index: false, follow: false } },
34
- ...input.canonical && { alternates: { canonical: input.canonical } },
35
- openGraph: {
36
- ...ogTitle && { title: ogTitle },
37
- ...ogDescription && { description: ogDescription },
38
- ...options?.siteName && { siteName: options.siteName },
39
- ...image && { images: [image] }
40
- },
41
- twitter: {
42
- card: image ? "summary_large_image" : "summary",
43
- ...ogTitle && { title: ogTitle },
44
- ...ogDescription && { description: ogDescription },
45
- ...image && { images: [image.url] }
46
- }
47
- };
48
- }
49
- function resolveMetaImage(ref) {
50
- const image = resolveRelation(ref);
51
- if (!image) return null;
52
- const sized = image.sizes?.["1536"];
53
- const url = sized?.url || image.url;
54
- if (!url) return null;
55
- const width = sized?.url ? sized.width : image.width;
56
- const height = sized?.url ? sized.height : image.height;
57
- return {
58
- url,
59
- ...width && { width },
60
- ...height && { height },
61
- ...image.alt && { alt: image.alt }
62
- };
63
- }
64
-
65
- // src/core/collection/query-builder.ts
66
- var ReadOnlyCollectionQueryBuilder = class {
67
- constructor(api, collection) {
68
- this.api = api;
69
- this.collection = collection;
70
- }
71
- async find(options) {
72
- return this.api.requestFind(
73
- `/api/${String(this.collection)}`,
74
- options
75
- );
76
- }
77
- async findById(id, options) {
78
- return this.api.requestFindById(
79
- `/api/${String(this.collection)}/${String(id)}`,
80
- options
81
- );
82
- }
83
- async count(options) {
84
- return this.api.requestCount(
85
- `/api/${String(this.collection)}/count`,
86
- options
87
- );
88
- }
89
- async findMetadata(options, metadataOptions) {
90
- const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
91
- const doc = docs[0];
92
- if (!doc) return null;
93
- return generateMetadata(
94
- extractSeo(doc),
95
- metadataOptions
96
- );
97
- }
98
- async findMetadataById(id, metadataOptions) {
99
- const doc = await this.findById(id, { depth: 1 });
100
- return generateMetadata(
101
- extractSeo(doc),
102
- metadataOptions
103
- );
104
- }
105
- };
106
- var CollectionQueryBuilder = class {
107
- constructor(api, collection) {
108
- this.api = api;
109
- this.collection = collection;
110
- }
111
- /**
112
- * Find documents (list query)
113
- * GET /api/{collection}
114
- * @returns Payload CMS find response with docs array and pagination
115
- */
116
- async find(options) {
117
- return this.api.requestFind(
118
- `/api/${String(this.collection)}`,
119
- options
120
- );
121
- }
122
- /**
123
- * Find document by ID
124
- * GET /api/{collection}/{id}
125
- * @returns Document object directly (no wrapper)
126
- */
127
- async findById(id, options) {
128
- return this.api.requestFindById(
129
- `/api/${String(this.collection)}/${String(id)}`,
130
- options
131
- );
132
- }
133
- /**
134
- * Create a new document
135
- * POST /api/{collection}
136
- * @returns Payload CMS mutation response with doc and message
137
- */
138
- async create(data, options) {
139
- const endpoint = `/api/${String(this.collection)}`;
140
- if (options?.file) {
141
- return this.api.requestCreateWithFile(
142
- endpoint,
143
- data,
144
- options.file,
145
- options.filename
146
- );
147
- }
148
- return this.api.requestCreate(endpoint, data);
149
- }
150
- /**
151
- * Update a document by ID
152
- * PATCH /api/{collection}/{id}
153
- * @returns Payload CMS mutation response with doc and message
154
- */
155
- async update(id, data, options) {
156
- const endpoint = `/api/${String(this.collection)}/${String(id)}`;
157
- if (options?.file) {
158
- return this.api.requestUpdateWithFile(
159
- endpoint,
160
- data,
161
- options.file,
162
- options.filename
163
- );
164
- }
165
- return this.api.requestUpdate(endpoint, data);
166
- }
167
- /**
168
- * Count documents
169
- * GET /api/{collection}/count
170
- * @returns Count response with totalDocs
171
- */
172
- async count(options) {
173
- return this.api.requestCount(
174
- `/api/${String(this.collection)}/count`,
175
- options
176
- );
177
- }
178
- /**
179
- * Find first matching document and return its Next.js Metadata.
180
- * Applies depth: 1 (SEO image populate) and limit: 1 automatically.
181
- * @returns Metadata or null if no document matches
182
- */
183
- async findMetadata(options, metadataOptions) {
184
- const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
185
- const doc = docs[0];
186
- if (!doc) return null;
187
- return generateMetadata(
188
- extractSeo(doc),
189
- metadataOptions
190
- );
191
- }
192
- /**
193
- * Find document by ID and return its Next.js Metadata.
194
- * Applies depth: 1 (SEO image populate) automatically.
195
- * @returns Metadata (throws on 404)
196
- */
197
- async findMetadataById(id, metadataOptions) {
198
- const doc = await this.findById(id, { depth: 1 });
199
- return generateMetadata(
200
- extractSeo(doc),
201
- metadataOptions
202
- );
203
- }
204
- /**
205
- * Update multiple documents (bulk update)
206
- * PATCH /api/{collection}
207
- * @returns Payload CMS find response with updated docs
208
- */
209
- async updateMany(where, data) {
210
- return this.api.requestUpdateMany(
211
- `/api/${String(this.collection)}`,
212
- { where, data }
213
- );
214
- }
215
- /**
216
- * Delete a document by ID
217
- * DELETE /api/{collection}/{id}
218
- * @returns Deleted document object directly (no wrapper)
219
- */
220
- async remove(id) {
221
- return this.api.requestDelete(
222
- `/api/${String(this.collection)}/${String(id)}`
223
- );
224
- }
225
- /**
226
- * Delete multiple documents (bulk delete)
227
- * DELETE /api/{collection}
228
- * @returns Payload CMS find response with deleted docs
229
- */
230
- async removeMany(where) {
231
- return this.api.requestDeleteMany(
232
- `/api/${String(this.collection)}`,
233
- { where }
234
- );
235
- }
236
- };
237
- var ServerCollectionQueryBuilder = class extends CollectionQueryBuilder {
238
- };
239
-
240
1
  // src/core/collection/http-client.ts
241
2
  import { stringify } from "qs-esm";
242
3
 
@@ -284,8 +45,16 @@ var ValidationError = class extends SDKError {
284
45
  }
285
46
  };
286
47
  var ApiError = class extends SDKError {
287
- constructor(message, status, details, userMessage, suggestion) {
288
- super("API_ERROR", message, status, details, userMessage, suggestion);
48
+ constructor(message, status, details, userMessage, suggestion, requestId) {
49
+ super(
50
+ "API_ERROR",
51
+ message,
52
+ status,
53
+ details,
54
+ userMessage,
55
+ suggestion,
56
+ requestId
57
+ );
289
58
  this.name = "ApiError";
290
59
  }
291
60
  };
@@ -336,19 +105,43 @@ var UsageLimitError = class extends SDKError {
336
105
  };
337
106
  var AuthError = class extends SDKError {
338
107
  constructor(message, details, userMessage, suggestion, requestId) {
339
- super("auth_error", message, 401, details, userMessage, suggestion, requestId);
108
+ super(
109
+ "auth_error",
110
+ message,
111
+ 401,
112
+ details,
113
+ userMessage,
114
+ suggestion,
115
+ requestId
116
+ );
340
117
  this.name = "AuthError";
341
118
  }
342
119
  };
343
120
  var PermissionError = class extends SDKError {
344
121
  constructor(message, details, userMessage, suggestion, requestId) {
345
- super("permission_error", message, 403, details, userMessage, suggestion, requestId);
122
+ super(
123
+ "permission_error",
124
+ message,
125
+ 403,
126
+ details,
127
+ userMessage,
128
+ suggestion,
129
+ requestId
130
+ );
346
131
  this.name = "PermissionError";
347
132
  }
348
133
  };
349
134
  var NotFoundError = class extends SDKError {
350
135
  constructor(message, details, userMessage, suggestion, requestId) {
351
- super("not_found", message, 404, details, userMessage, suggestion, requestId);
136
+ super(
137
+ "not_found",
138
+ message,
139
+ 404,
140
+ details,
141
+ userMessage,
142
+ suggestion,
143
+ requestId
144
+ );
352
145
  this.name = "NotFoundError";
353
146
  }
354
147
  };
@@ -360,7 +153,15 @@ var ConflictError = class extends SDKError {
360
153
  };
361
154
  var RateLimitError = class extends SDKError {
362
155
  constructor(message, retryAfter, details, userMessage, suggestion, requestId) {
363
- super("rate_limit_exceeded", message, 429, details, userMessage, suggestion, requestId);
156
+ super(
157
+ "rate_limit_exceeded",
158
+ message,
159
+ 429,
160
+ details,
161
+ userMessage,
162
+ suggestion,
163
+ requestId
164
+ );
364
165
  this.name = "RateLimitError";
365
166
  this.retryAfter = retryAfter;
366
167
  }
@@ -409,7 +210,7 @@ function isRateLimitError(error) {
409
210
  }
410
211
  var createNetworkError = (message, status, details, userMessage, suggestion) => new NetworkError(message, status, details, userMessage, suggestion);
411
212
  var createValidationError = (message, details, userMessage, suggestion, status) => new ValidationError(message, details, userMessage, suggestion, status);
412
- var createApiError = (message, status, details, userMessage, suggestion) => new ApiError(message, status, details, userMessage, suggestion);
213
+ var createApiError = (message, status, details, userMessage, suggestion, requestId) => new ApiError(message, status, details, userMessage, suggestion, requestId);
413
214
  var createConfigError = (message, details, userMessage, suggestion) => new ConfigError(message, details, userMessage, suggestion);
414
215
  var createTimeoutError = (message, details, userMessage, suggestion) => new TimeoutError(message, details, userMessage, suggestion);
415
216
  var createUsageLimitError = (message, usage, details, userMessage, suggestion) => new UsageLimitError(message, usage, details, userMessage, suggestion);
@@ -417,7 +218,14 @@ var createAuthError = (message, details, userMessage, suggestion, requestId) =>
417
218
  var createPermissionError = (message, details, userMessage, suggestion, requestId) => new PermissionError(message, details, userMessage, suggestion, requestId);
418
219
  var createNotFoundError = (message, details, userMessage, suggestion, requestId) => new NotFoundError(message, details, userMessage, suggestion, requestId);
419
220
  var createConflictError = (message, details, userMessage, suggestion, requestId) => new ConflictError(message, details, userMessage, suggestion, requestId);
420
- var createRateLimitError = (message, retryAfter, details, userMessage, suggestion, requestId) => new RateLimitError(message, retryAfter, details, userMessage, suggestion, requestId);
221
+ var createRateLimitError = (message, retryAfter, details, userMessage, suggestion, requestId) => new RateLimitError(
222
+ message,
223
+ retryAfter,
224
+ details,
225
+ userMessage,
226
+ suggestion,
227
+ requestId
228
+ );
421
229
 
422
230
  // src/core/internal/utils/credentials.ts
423
231
  function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
@@ -430,7 +238,10 @@ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
430
238
  }
431
239
 
432
240
  // src/core/client/types.ts
433
- function resolveApiUrl() {
241
+ function resolveApiUrl(apiUrl) {
242
+ if (apiUrl) {
243
+ return apiUrl.replace(/\/$/, "");
244
+ }
434
245
  if (typeof process !== "undefined" && process.env) {
435
246
  const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
436
247
  if (envUrl) {
@@ -454,6 +265,22 @@ function debugLog(debug, type, message, data) {
454
265
  console.groupEnd();
455
266
  }
456
267
  }
268
+ function redactSensitiveHeader(value) {
269
+ const prefix = value.toLowerCase().startsWith("bearer ") ? "Bearer " : "";
270
+ return value.length > 20 ? `${prefix}...****${value.slice(-8)}` : "****";
271
+ }
272
+ function redactSensitiveHeaders(headers) {
273
+ const redacted = Object.fromEntries(headers.entries());
274
+ if (redacted.authorization) {
275
+ redacted.authorization = redactSensitiveHeader(redacted.authorization);
276
+ }
277
+ if (redacted["x-preview-token"]) {
278
+ redacted["x-preview-token"] = redactSensitiveHeader(
279
+ redacted["x-preview-token"]
280
+ );
281
+ }
282
+ return redacted;
283
+ }
457
284
  function getErrorSuggestion(status) {
458
285
  if (status === 400)
459
286
  return "The request data failed validation. Check field values and types.";
@@ -528,6 +355,12 @@ async function parseErrorBody(response) {
528
355
  return fallback;
529
356
  }
530
357
  }
358
+ function getParsedErrorSuggestion(status, parsed) {
359
+ if (status === 403 && parsed.reason === "origin_not_allowed") {
360
+ return "Add the request origin to the tenant Browser API origins, then retry the browser request.";
361
+ }
362
+ return getErrorSuggestion(status);
363
+ }
531
364
  async function delay(ms) {
532
365
  return new Promise((resolve) => setTimeout(resolve, ms));
533
366
  }
@@ -541,7 +374,7 @@ function createHttpStatusError(status, parsed, details, requestId) {
541
374
  ...parsed.errors && { errors: parsed.errors },
542
375
  ...parsed.body && { body: parsed.body }
543
376
  };
544
- const suggestion = getErrorSuggestion(status);
377
+ const suggestion = getParsedErrorSuggestion(status, parsed);
545
378
  if (status === 400 || status === 422) {
546
379
  return attachRequestId(
547
380
  createValidationError(
@@ -611,6 +444,7 @@ function createHttpStatusError(status, parsed, details, requestId) {
611
444
  }
612
445
  async function httpFetch(url, options) {
613
446
  const {
447
+ apiUrl,
614
448
  publishableKey,
615
449
  secretKey,
616
450
  customerToken,
@@ -620,7 +454,7 @@ async function httpFetch(url, options) {
620
454
  onUnauthorized,
621
455
  ...requestInit
622
456
  } = options || {};
623
- const baseUrl = resolveApiUrl();
457
+ const baseUrl = resolveApiUrl(apiUrl);
624
458
  const retryConfig = {
625
459
  maxRetries: retry?.maxRetries ?? 3,
626
460
  retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
@@ -646,11 +480,7 @@ async function httpFetch(url, options) {
646
480
  if (!headers.has("Content-Type") && requestInit.body && !(requestInit.body instanceof FormData)) {
647
481
  headers.set("Content-Type", "application/json");
648
482
  }
649
- const redactedHeaders = Object.fromEntries(headers.entries());
650
- if (redactedHeaders["authorization"]) {
651
- const token = redactedHeaders["authorization"];
652
- redactedHeaders["authorization"] = token.length > 20 ? `Bearer ...****${token.slice(-8)}` : "****";
653
- }
483
+ const redactedHeaders = redactSensitiveHeaders(headers);
654
484
  debugLog(debug, "request", url, {
655
485
  method: requestInit.method || "GET",
656
486
  headers: redactedHeaders,
@@ -668,7 +498,7 @@ async function httpFetch(url, options) {
668
498
  debugLog(debug, "response", url, {
669
499
  status: response.status,
670
500
  statusText: response.statusText,
671
- headers: Object.fromEntries(response.headers.entries())
501
+ headers: redactSensitiveHeaders(response.headers)
672
502
  });
673
503
  if (!response.ok) {
674
504
  if (isUsageLimitExceededResponse(response)) {
@@ -809,7 +639,7 @@ async function httpFetch(url, options) {
809
639
 
810
640
  // src/core/collection/http-client.ts
811
641
  var HttpClient = class {
812
- constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
642
+ constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
813
643
  this.publishableKey = requirePublishableKeyForSecret(
814
644
  "CollectionClient",
815
645
  publishableKey,
@@ -819,9 +649,11 @@ var HttpClient = class {
819
649
  this.getCustomerToken = getCustomerToken;
820
650
  this.onUnauthorized = onUnauthorized;
821
651
  this.onRequestId = onRequestId;
652
+ this.apiUrl = apiUrl;
822
653
  }
823
654
  get defaultOptions() {
824
655
  const opts = {
656
+ apiUrl: this.apiUrl,
825
657
  publishableKey: this.publishableKey,
826
658
  secretKey: this.secretKey
827
659
  };
@@ -939,159 +771,113 @@ var HttpClient = class {
939
771
  }
940
772
  };
941
773
 
942
- // src/core/collection/collection-client.ts
943
- function buildPayloadFormData(data, file, filename) {
944
- const formData = new FormData();
945
- formData.append("file", file, filename);
946
- if (data != null) {
947
- formData.append("_payload", JSON.stringify(data));
948
- }
949
- return formData;
774
+ // src/utils/types.ts
775
+ var resolveRelation = (ref) => {
776
+ if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
777
+ return null;
778
+ return ref;
779
+ };
780
+
781
+ // src/core/metadata/index.ts
782
+ function extractSeo(doc) {
783
+ const seo = doc.seo ?? {};
784
+ const og = seo.openGraph ?? {};
785
+ return {
786
+ title: seo.title ?? doc.title ?? null,
787
+ description: seo.description ?? null,
788
+ noIndex: seo.noIndex ?? null,
789
+ canonical: seo.canonical ?? null,
790
+ openGraph: {
791
+ title: og.title ?? null,
792
+ description: og.description ?? null,
793
+ image: og.image ?? null
794
+ }
795
+ };
950
796
  }
951
- var CollectionClient = class extends HttpClient {
952
- from(collection) {
953
- return new CollectionQueryBuilder(this, collection);
954
- }
955
- // ============================================================================
956
- // Payload-native methods
957
- // ============================================================================
958
- /**
959
- * Find documents (list query)
960
- * GET /api/{collection}
961
- */
962
- async requestFind(endpoint, options) {
963
- const url = this.buildUrl(endpoint, options);
964
- const response = await this.fetchWithTracking(url, {
965
- ...this.defaultOptions,
966
- method: "GET"
967
- });
968
- return this.parseFindResponse(response);
969
- }
970
- /**
971
- * Find-like response from a custom endpoint
972
- * POST /api/...custom-endpoint
973
- */
974
- async requestFindEndpoint(endpoint, data) {
975
- const response = await this.fetchWithTracking(endpoint, {
976
- ...this.defaultOptions,
977
- method: "POST",
978
- body: data ? JSON.stringify(data) : void 0
979
- });
980
- return this.parseFindResponse(response);
981
- }
982
- /**
983
- * Find document by ID
984
- * GET /api/{collection}/{id}
985
- */
986
- async requestFindById(endpoint, options) {
987
- const url = this.buildUrl(endpoint, options);
988
- const response = await this.fetchWithTracking(url, {
989
- ...this.defaultOptions,
990
- method: "GET"
991
- });
992
- return this.parseDocumentResponse(response);
993
- }
994
- /**
995
- * Create document
996
- * POST /api/{collection}
997
- */
998
- async requestCreate(endpoint, data) {
999
- const response = await this.fetchWithTracking(endpoint, {
1000
- ...this.defaultOptions,
1001
- method: "POST",
1002
- body: data ? JSON.stringify(data) : void 0
1003
- });
1004
- return this.parseMutationResponse(response);
1005
- }
1006
- /**
1007
- * Update document
1008
- * PATCH /api/{collection}/{id}
1009
- */
1010
- async requestUpdate(endpoint, data) {
1011
- const response = await this.fetchWithTracking(endpoint, {
1012
- ...this.defaultOptions,
1013
- method: "PATCH",
1014
- body: data ? JSON.stringify(data) : void 0
1015
- });
1016
- return this.parseMutationResponse(response);
1017
- }
1018
- /**
1019
- * Count documents
1020
- * GET /api/{collection}/count
1021
- */
1022
- async requestCount(endpoint, options) {
1023
- const url = this.buildUrl(endpoint, options);
1024
- const response = await this.fetchWithTracking(url, {
1025
- ...this.defaultOptions,
1026
- method: "GET"
1027
- });
1028
- return this.parseDocumentResponse(response);
1029
- }
1030
- /**
1031
- * Update multiple documents (bulk update)
1032
- * PATCH /api/{collection}
1033
- */
1034
- async requestUpdateMany(endpoint, data) {
1035
- const response = await this.fetchWithTracking(endpoint, {
1036
- ...this.defaultOptions,
1037
- method: "PATCH",
1038
- body: JSON.stringify(data)
1039
- });
1040
- return this.parseFindResponse(response);
797
+ function generateMetadata(input, options) {
798
+ const title = input.title ?? void 0;
799
+ const description = input.description ?? void 0;
800
+ const ogTitle = input.openGraph?.title ?? title;
801
+ const ogDescription = input.openGraph?.description ?? description;
802
+ const image = resolveMetaImage(input.openGraph?.image);
803
+ return {
804
+ title,
805
+ description,
806
+ ...input.noIndex && { robots: { index: false, follow: false } },
807
+ ...input.canonical && { alternates: { canonical: input.canonical } },
808
+ openGraph: {
809
+ ...ogTitle && { title: ogTitle },
810
+ ...ogDescription && { description: ogDescription },
811
+ ...options?.siteName && { siteName: options.siteName },
812
+ ...image && { images: [image] }
813
+ },
814
+ twitter: {
815
+ card: image ? "summary_large_image" : "summary",
816
+ ...ogTitle && { title: ogTitle },
817
+ ...ogDescription && { description: ogDescription },
818
+ ...image && { images: [image.url] }
819
+ }
820
+ };
821
+ }
822
+ function resolveMetaImage(ref) {
823
+ const image = resolveRelation(ref);
824
+ if (!image) return null;
825
+ const sized = image.sizes?.["1536"];
826
+ const url = sized?.url || image.url;
827
+ if (!url) return null;
828
+ const width = sized?.url ? sized.width : image.width;
829
+ const height = sized?.url ? sized.height : image.height;
830
+ return {
831
+ url,
832
+ ...width && { width },
833
+ ...height && { height },
834
+ ...image.alt && { alt: image.alt }
835
+ };
836
+ }
837
+
838
+ // src/core/collection/query-builder.ts
839
+ var ReadOnlyCollectionQueryBuilder = class {
840
+ constructor(api, collection) {
841
+ this.api = api;
842
+ this.collection = collection;
1041
843
  }
1042
- /**
1043
- * Delete document
1044
- * DELETE /api/{collection}/{id}
1045
- */
1046
- async requestDelete(endpoint) {
1047
- const response = await this.fetchWithTracking(endpoint, {
1048
- ...this.defaultOptions,
1049
- method: "DELETE"
1050
- });
1051
- return this.parseDocumentResponse(response);
844
+ async find(options) {
845
+ return this.api.requestFind(
846
+ `/api/${String(this.collection)}`,
847
+ options
848
+ );
1052
849
  }
1053
- /**
1054
- * Delete multiple documents (bulk delete)
1055
- * DELETE /api/{collection}
1056
- */
1057
- async requestDeleteMany(endpoint, data) {
1058
- const response = await this.fetchWithTracking(endpoint, {
1059
- ...this.defaultOptions,
1060
- method: "DELETE",
1061
- body: JSON.stringify(data)
1062
- });
1063
- return this.parseFindResponse(response);
850
+ async findById(id, options) {
851
+ return this.api.requestFindById(
852
+ `/api/${String(this.collection)}/${String(id)}`,
853
+ options
854
+ );
1064
855
  }
1065
- /**
1066
- * Create document with file upload
1067
- * POST /api/{collection} (multipart/form-data)
1068
- */
1069
- async requestCreateWithFile(endpoint, data, file, filename) {
1070
- const response = await this.fetchWithTracking(endpoint, {
1071
- ...this.defaultOptions,
1072
- method: "POST",
1073
- body: buildPayloadFormData(data, file, filename)
1074
- });
1075
- return this.parseMutationResponse(response);
856
+ async count(options) {
857
+ return this.api.requestCount(
858
+ `/api/${String(this.collection)}/count`,
859
+ options
860
+ );
1076
861
  }
1077
- /**
1078
- * Update document with file upload
1079
- * PATCH /api/{collection}/{id} (multipart/form-data)
1080
- */
1081
- async requestUpdateWithFile(endpoint, data, file, filename) {
1082
- const response = await this.fetchWithTracking(endpoint, {
1083
- ...this.defaultOptions,
1084
- method: "PATCH",
1085
- body: buildPayloadFormData(data, file, filename)
1086
- });
1087
- return this.parseMutationResponse(response);
862
+ async findMetadata(options, metadataOptions) {
863
+ const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
864
+ const doc = docs[0];
865
+ if (!doc) return null;
866
+ return generateMetadata(
867
+ extractSeo(doc),
868
+ metadataOptions
869
+ );
1088
870
  }
1089
- };
1090
- var ServerCollectionClient = class extends CollectionClient {
1091
- from(collection) {
1092
- return new ServerCollectionQueryBuilder(this, collection);
871
+ async findMetadataById(id, metadataOptions) {
872
+ const doc = await this.findById(id, { depth: 1 });
873
+ return generateMetadata(
874
+ extractSeo(doc),
875
+ metadataOptions
876
+ );
1093
877
  }
1094
878
  };
879
+
880
+ // src/core/collection/collection-client.ts
1095
881
  var ReadOnlyCollectionClient = class extends HttpClient {
1096
882
  from(collection) {
1097
883
  return new ReadOnlyCollectionQueryBuilder(this, collection);
@@ -1122,126 +908,6 @@ var ReadOnlyCollectionClient = class extends HttpClient {
1122
908
  }
1123
909
  };
1124
910
 
1125
- // src/core/collection/const.ts
1126
- var INTERNAL_COLLECTIONS = [
1127
- "users",
1128
- "payload-kv",
1129
- "payload-locked-documents",
1130
- "payload-preferences",
1131
- "payload-migrations",
1132
- "payload-folders",
1133
- "field-configs",
1134
- "system-media",
1135
- "track-assets",
1136
- "audiences",
1137
- "email-logs",
1138
- "api-usage",
1139
- "tenant-analytics-daily",
1140
- "tenant-web-analytics-config",
1141
- "analytics-event-schemas",
1142
- "subscriptions",
1143
- "billing-history",
1144
- "inventory-reservations",
1145
- "order-status-logs",
1146
- "api-keys",
1147
- "personal-access-tokens",
1148
- "tenant-entitlements",
1149
- "tenant-purge-jobs",
1150
- "direct-upload-sessions",
1151
- "webhook-events",
1152
- "webhook-deliveries",
1153
- "audit-logs",
1154
- "plans",
1155
- "webhooks",
1156
- "event-registrations"
1157
- ];
1158
- var COLLECTIONS = [
1159
- "tenants",
1160
- "tenant-metadata",
1161
- "tenant-logos",
1162
- "products",
1163
- "product-variants",
1164
- "product-options",
1165
- "product-option-values",
1166
- "product-categories",
1167
- "product-tags",
1168
- "product-collections",
1169
- "brands",
1170
- "brand-logos",
1171
- "orders",
1172
- "order-items",
1173
- "returns",
1174
- "return-items",
1175
- "fulfillments",
1176
- "fulfillment-items",
1177
- "transactions",
1178
- "customers",
1179
- "customer-profiles",
1180
- "customer-profile-lists",
1181
- "customer-addresses",
1182
- "carts",
1183
- "cart-items",
1184
- "discounts",
1185
- "shipping-policies",
1186
- "shipping-zones",
1187
- "documents",
1188
- "document-categories",
1189
- "document-types",
1190
- "articles",
1191
- "article-authors",
1192
- "article-categories",
1193
- "article-tags",
1194
- "playlists",
1195
- "playlist-categories",
1196
- "playlist-tags",
1197
- "tracks",
1198
- "track-categories",
1199
- "track-tags",
1200
- "galleries",
1201
- "gallery-categories",
1202
- "gallery-tags",
1203
- "gallery-items",
1204
- "links",
1205
- "link-categories",
1206
- "link-tags",
1207
- "canvases",
1208
- "canvas-node-types",
1209
- "canvas-edge-types",
1210
- "canvas-categories",
1211
- "canvas-tags",
1212
- "canvas-nodes",
1213
- "canvas-edges",
1214
- "videos",
1215
- "video-categories",
1216
- "video-tags",
1217
- "live-streams",
1218
- "images",
1219
- "forms",
1220
- "form-submissions",
1221
- // Community
1222
- "posts",
1223
- "comments",
1224
- "reactions",
1225
- "reaction-types",
1226
- "bookmarks",
1227
- "post-categories",
1228
- // Events
1229
- "event-calendars",
1230
- "events",
1231
- "event-categories",
1232
- "event-occurrences",
1233
- "event-tags"
1234
- ];
1235
- var SERVER_ONLY_COLLECTIONS = [
1236
- "customer-groups",
1237
- "reports",
1238
- "community-bans"
1239
- ];
1240
- var SERVER_COLLECTIONS = [
1241
- ...COLLECTIONS,
1242
- ...SERVER_ONLY_COLLECTIONS
1243
- ];
1244
-
1245
911
  // src/core/api/parse-response.ts
1246
912
  async function parseApiResponse(response, endpoint) {
1247
913
  let data;
@@ -1265,7 +931,7 @@ async function parseApiResponse(response, endpoint) {
1265
931
  if (reason === "validation_failed") {
1266
932
  throw attachRequestId(createValidationError(errorMessage, data, errorMessage), requestId);
1267
933
  }
1268
- if (reason === "token_expired" || reason === "token_invalid" || reason === "key_invalid" || reason === "key_revoked") {
934
+ if (reason === "token_expired" || reason === "token_invalid" || reason === "preview_token_invalid" || reason === "preview_token_required" || reason === "key_invalid" || reason === "key_revoked") {
1269
935
  throw attachRequestId(createAuthError(errorMessage, data, errorMessage), requestId);
1270
936
  }
1271
937
  if (reason === "forbidden") {
@@ -1297,6 +963,7 @@ var CommunityClient = class {
1297
963
  options.secretKey
1298
964
  );
1299
965
  this.secretKey = options.secretKey;
966
+ this.apiUrl = options.apiUrl;
1300
967
  this.customerToken = options.customerToken;
1301
968
  this.onUnauthorized = options.onUnauthorized;
1302
969
  this.onRequestId = options.onRequestId;
@@ -1311,6 +978,7 @@ var CommunityClient = class {
1311
978
  try {
1312
979
  const response = await httpFetch(endpoint, {
1313
980
  method,
981
+ apiUrl: this.apiUrl,
1314
982
  publishableKey: this.publishableKey,
1315
983
  secretKey: this.secretKey,
1316
984
  customerToken: token ?? void 0,
@@ -1459,53 +1127,6 @@ var CommunityClient = class {
1459
1127
  }
1460
1128
  };
1461
1129
 
1462
- // src/core/api/base-api.ts
1463
- var BaseApi = class {
1464
- constructor(apiName, options) {
1465
- if (!options.secretKey) {
1466
- throw createConfigError(`secretKey is required for ${apiName}.`);
1467
- }
1468
- this.publishableKey = requirePublishableKeyForSecret(
1469
- apiName,
1470
- options.publishableKey,
1471
- options.secretKey
1472
- );
1473
- this.secretKey = options.secretKey;
1474
- this.onRequestId = options.onRequestId;
1475
- }
1476
- async request(endpoint, body, options) {
1477
- const method = options?.method ?? "POST";
1478
- try {
1479
- const response = await httpFetch(endpoint, {
1480
- method,
1481
- publishableKey: this.publishableKey,
1482
- secretKey: this.secretKey,
1483
- ...body !== void 0 && { body: JSON.stringify(body) },
1484
- ...options?.headers && { headers: options.headers }
1485
- });
1486
- this.onRequestId?.(response.headers.get("x-request-id") ?? null);
1487
- return parseApiResponse(response, endpoint);
1488
- } catch (err) {
1489
- const id = err instanceof SDKError ? err.requestId ?? null : null;
1490
- this.onRequestId?.(id);
1491
- throw err;
1492
- }
1493
- }
1494
- };
1495
-
1496
- // src/core/community/moderation-api.ts
1497
- var ModerationApi = class extends BaseApi {
1498
- constructor(options) {
1499
- super("ModerationApi", options);
1500
- }
1501
- banCustomer(params) {
1502
- return this.request("/api/community-bans/ban", params);
1503
- }
1504
- unbanCustomer(params) {
1505
- return this.request("/api/community-bans/unban", params);
1506
- }
1507
- };
1508
-
1509
1130
  // src/core/customer/customer-auth.ts
1510
1131
  var DEFAULT_TIMEOUT2 = 15e3;
1511
1132
  function safeGetItem(key) {
@@ -1516,10 +1137,10 @@ function safeGetItem(key) {
1516
1137
  }
1517
1138
  }
1518
1139
  var CustomerAuth = class {
1519
- constructor(publishableKey, options) {
1140
+ constructor(publishableKey, options, apiUrl) {
1520
1141
  this.refreshPromise = null;
1521
1142
  this.publishableKey = publishableKey;
1522
- this.baseUrl = resolveApiUrl();
1143
+ this.baseUrl = resolveApiUrl(apiUrl);
1523
1144
  const persist = options?.persist ?? true;
1524
1145
  if (persist) {
1525
1146
  const key = typeof persist === "string" ? persist : "customer-token";
@@ -1750,8 +1371,8 @@ var CustomerAuth = class {
1750
1371
 
1751
1372
  // src/core/customer/customer-namespace.ts
1752
1373
  var CustomerNamespace = class {
1753
- constructor(publishableKey, options) {
1754
- this.auth = new CustomerAuth(publishableKey, options);
1374
+ constructor(publishableKey, options, apiUrl) {
1375
+ this.auth = new CustomerAuth(publishableKey, options, apiUrl);
1755
1376
  }
1756
1377
  };
1757
1378
 
@@ -1769,6 +1390,7 @@ var CartApi = class {
1769
1390
  options.secretKey
1770
1391
  );
1771
1392
  this.secretKey = options.secretKey;
1393
+ this.apiUrl = options.apiUrl;
1772
1394
  this.customerToken = options.customerToken;
1773
1395
  this.onUnauthorized = options.onUnauthorized;
1774
1396
  this.onRequestId = options.onRequestId;
@@ -1778,6 +1400,7 @@ var CartApi = class {
1778
1400
  try {
1779
1401
  const response = await httpFetch(endpoint, {
1780
1402
  method,
1403
+ apiUrl: this.apiUrl,
1781
1404
  publishableKey: this.publishableKey,
1782
1405
  secretKey: this.secretKey,
1783
1406
  customerToken: token ?? void 0,
@@ -1828,6 +1451,7 @@ var CommerceClient = class {
1828
1451
  constructor(options) {
1829
1452
  const cartApi = new CartApi({
1830
1453
  publishableKey: options.publishableKey,
1454
+ apiUrl: options.apiUrl,
1831
1455
  customerToken: options.customerToken,
1832
1456
  onUnauthorized: options.onUnauthorized,
1833
1457
  onRequestId: options.onRequestId
@@ -1837,6 +1461,7 @@ var CommerceClient = class {
1837
1461
  try {
1838
1462
  const response = await httpFetch(endpoint, {
1839
1463
  method: "POST",
1464
+ apiUrl: options.apiUrl,
1840
1465
  publishableKey: options.publishableKey,
1841
1466
  customerToken: token ?? void 0,
1842
1467
  ...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
@@ -1884,68 +1509,105 @@ var CommerceClient = class {
1884
1509
  }
1885
1510
  };
1886
1511
 
1887
- // src/core/api/product-api.ts
1888
- var ProductApi = class extends BaseApi {
1512
+ // src/core/client/client.ts
1513
+ var Client = class {
1889
1514
  constructor(options) {
1890
- super("ProductApi", options);
1515
+ this.lastRequestId = null;
1516
+ const publishableKey = options.publishableKey;
1517
+ if (!publishableKey) {
1518
+ throw createConfigError("publishableKey is required.");
1519
+ }
1520
+ this.config = { ...options, publishableKey };
1521
+ const metadata = {
1522
+ timestamp: Date.now(),
1523
+ userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
1524
+ };
1525
+ this.state = { metadata };
1526
+ this.customer = new CustomerNamespace(
1527
+ this.config.publishableKey,
1528
+ options.customer,
1529
+ this.config.apiUrl
1530
+ );
1531
+ const onUnauthorized = async () => {
1532
+ try {
1533
+ const result = await this.customer.auth.refreshToken();
1534
+ return result.token ?? null;
1535
+ } catch {
1536
+ return null;
1537
+ }
1538
+ };
1539
+ const onRequestId = (id) => {
1540
+ this.lastRequestId = id;
1541
+ };
1542
+ this.commerce = new CommerceClient({
1543
+ publishableKey: this.config.publishableKey,
1544
+ apiUrl: this.config.apiUrl,
1545
+ customerToken: () => this.customer.auth.getToken(),
1546
+ onUnauthorized,
1547
+ onRequestId,
1548
+ customerAuth: this.customer.auth
1549
+ });
1550
+ this.community = new CommunityClient({
1551
+ publishableKey: this.config.publishableKey,
1552
+ apiUrl: this.config.apiUrl,
1553
+ customerToken: () => this.customer.auth.getToken(),
1554
+ onUnauthorized,
1555
+ onRequestId
1556
+ });
1557
+ this.collections = new ReadOnlyCollectionClient(
1558
+ this.config.publishableKey,
1559
+ void 0,
1560
+ () => this.customer.auth.getToken(),
1561
+ onUnauthorized,
1562
+ onRequestId,
1563
+ this.config.apiUrl
1564
+ );
1891
1565
  }
1892
- /**
1893
- * Check point-in-time stock availability for one or more product variants.
1894
- * Results reflect available stock at the moment of the call and are not guaranteed
1895
- * to remain available by the time an order is placed.
1896
- */
1897
- stockCheck(params) {
1898
- return this.request("/api/products/stock-check", params);
1566
+ getState() {
1567
+ return { ...this.state };
1899
1568
  }
1900
- listingGroups(params) {
1901
- return this.request(
1902
- "/api/products/listing-groups",
1903
- params
1569
+ getConfig() {
1570
+ return { ...this.config };
1571
+ }
1572
+ };
1573
+ function createClient(options) {
1574
+ return new Client(options);
1575
+ }
1576
+
1577
+ // src/core/api/base-api.ts
1578
+ var BaseApi = class {
1579
+ constructor(apiName, options) {
1580
+ if (!options.secretKey) {
1581
+ throw createConfigError(`secretKey is required for ${apiName}.`);
1582
+ }
1583
+ this.publishableKey = requirePublishableKeyForSecret(
1584
+ apiName,
1585
+ options.publishableKey,
1586
+ options.secretKey
1904
1587
  );
1588
+ this.secretKey = options.secretKey;
1589
+ this.apiUrl = options.apiUrl;
1590
+ this.onRequestId = options.onRequestId;
1905
1591
  }
1906
- /**
1907
- * Fetch full product detail by slug or id.
1908
- * Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
1909
- * `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
1910
- * inspect `client.lastRequestId` against backend logs.
1911
- */
1912
- async detail(params) {
1592
+ async request(endpoint, body, options) {
1593
+ const method = options?.method ?? "POST";
1913
1594
  try {
1914
- return await this.request("/api/products/detail", params);
1595
+ const response = await httpFetch(endpoint, {
1596
+ method,
1597
+ apiUrl: this.apiUrl,
1598
+ publishableKey: this.publishableKey,
1599
+ secretKey: this.secretKey,
1600
+ ...body !== void 0 && { body: JSON.stringify(body) },
1601
+ ...options?.headers && { headers: options.headers }
1602
+ });
1603
+ this.onRequestId?.(response.headers.get("x-request-id") ?? null);
1604
+ return parseApiResponse(response, endpoint);
1915
1605
  } catch (err) {
1916
- if (err instanceof NotFoundError) return null;
1606
+ const id = err instanceof SDKError ? err.requestId ?? null : null;
1607
+ this.onRequestId?.(id);
1917
1608
  throw err;
1918
1609
  }
1919
1610
  }
1920
- /**
1921
- * Atomically create or update a product together with its options,
1922
- * option-values, and variants in a single transaction. Mirrors Shopify's
1923
- * `productSet` shape and is the canonical write path for the MCP
1924
- * `product-upsert` tool.
1925
- */
1926
- upsert(params) {
1927
- return this.request("/api/products/upsert", params);
1928
- }
1929
- };
1930
-
1931
- // src/core/api/discount-api.ts
1932
- var DiscountApi = class extends BaseApi {
1933
- constructor(options) {
1934
- super("DiscountApi", options);
1935
- }
1936
- validate(params) {
1937
- return this.request("/api/discounts/validate", params);
1938
- }
1939
- };
1940
-
1941
- // src/core/api/shipping-api.ts
1942
- var ShippingApi = class extends BaseApi {
1943
- constructor(options) {
1944
- super("ShippingApi", options);
1945
- }
1946
- calculate(params) {
1947
- return this.request("/api/shipping-policies/calculate", params);
1948
- }
1949
1611
  };
1950
1612
 
1951
1613
  // src/core/api/order-api.ts
@@ -1998,770 +1660,308 @@ var OrderApi = class extends BaseApi {
1998
1660
  }
1999
1661
  };
2000
1662
 
2001
- // src/core/commerce/server-commerce-client.ts
2002
- var ServerCommerceClient = class {
1663
+ // src/core/api/discount-api.ts
1664
+ var DiscountApi = class extends BaseApi {
2003
1665
  constructor(options) {
2004
- const publishableKey = requirePublishableKeyForSecret(
2005
- "ServerCommerceClient",
2006
- options.publishableKey,
2007
- options.secretKey
2008
- );
2009
- const serverOptions = {
2010
- publishableKey,
2011
- secretKey: options.secretKey,
2012
- onRequestId: options.onRequestId
2013
- };
2014
- const productApi = new ProductApi(serverOptions);
2015
- const cartApi = new CartApi(serverOptions);
2016
- const discountApi = new DiscountApi(serverOptions);
2017
- const shippingApi = new ShippingApi(serverOptions);
2018
- const orderApi = new OrderApi(serverOptions);
2019
- this.product = {
2020
- stockCheck: productApi.stockCheck.bind(productApi),
2021
- listingGroups: productApi.listingGroups.bind(productApi),
2022
- detail: productApi.detail.bind(productApi),
2023
- upsert: productApi.upsert.bind(productApi)
2024
- };
2025
- this.cart = {
2026
- get: cartApi.getCart.bind(cartApi),
2027
- addItem: cartApi.addItem.bind(cartApi),
2028
- updateItem: cartApi.updateItem.bind(cartApi),
2029
- removeItem: cartApi.removeItem.bind(cartApi),
2030
- applyDiscount: cartApi.applyDiscount.bind(cartApi),
2031
- removeDiscount: cartApi.removeDiscount.bind(cartApi),
2032
- clear: cartApi.clearCart.bind(cartApi)
2033
- };
2034
- this.orders = {
2035
- checkout: orderApi.checkout.bind(orderApi),
2036
- create: orderApi.createOrder.bind(orderApi),
2037
- update: orderApi.updateOrder.bind(orderApi),
2038
- updateTransaction: orderApi.updateTransaction.bind(orderApi),
2039
- confirmPayment: orderApi.confirmPayment.bind(orderApi),
2040
- createFulfillment: orderApi.createFulfillment.bind(orderApi),
2041
- updateFulfillment: orderApi.updateFulfillment.bind(orderApi),
2042
- bulkImportFulfillments: orderApi.bulkImportFulfillments.bind(orderApi),
2043
- createReturn: orderApi.createReturn.bind(orderApi),
2044
- updateReturn: orderApi.updateReturn.bind(orderApi),
2045
- returnWithRefund: orderApi.returnWithRefund.bind(orderApi)
2046
- };
2047
- this.discounts = {
2048
- validate: discountApi.validate.bind(discountApi)
2049
- };
2050
- this.shipping = {
2051
- calculate: shippingApi.calculate.bind(shippingApi)
2052
- };
1666
+ super("DiscountApi", options);
1667
+ }
1668
+ validate(params) {
1669
+ return this.request("/api/discounts/validate", params);
2053
1670
  }
2054
1671
  };
2055
1672
 
2056
- // src/core/query/get-query-client.ts
2057
- import {
2058
- isServer,
2059
- QueryClient,
2060
- defaultShouldDehydrateQuery
2061
- } from "@tanstack/react-query";
2062
- function makeQueryClient() {
2063
- return new QueryClient({
2064
- defaultOptions: {
2065
- queries: {
2066
- // Infinite staleTime: server-fetched data persists until explicitly invalidated.
2067
- // For browser clients needing fresher data, override per-query:
2068
- // useQuery({ ..., staleTime: 5 * 60 * 1000 })
2069
- staleTime: Number.POSITIVE_INFINITY,
2070
- refetchOnWindowFocus: false
2071
- },
2072
- dehydrate: {
2073
- shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending",
2074
- shouldRedactErrors: () => false
2075
- }
2076
- }
2077
- });
2078
- }
2079
- var browserQueryClient;
2080
- function getQueryClient() {
2081
- if (isServer) {
2082
- return makeQueryClient();
1673
+ // src/core/api/shipping-api.ts
1674
+ var ShippingApi = class extends BaseApi {
1675
+ constructor(options) {
1676
+ super("ShippingApi", options);
2083
1677
  }
2084
- if (!browserQueryClient) {
2085
- browserQueryClient = makeQueryClient();
1678
+ calculate(params) {
1679
+ return this.request("/api/shipping-policies/calculate", params);
2086
1680
  }
2087
- return browserQueryClient;
2088
- }
2089
-
2090
- // src/core/query/query-hooks.ts
2091
- import {
2092
- useInfiniteQuery as useInfiniteQueryOriginal2,
2093
- useQuery as useQueryOriginal3,
2094
- useSuspenseInfiniteQuery as useSuspenseInfiniteQueryOriginal2,
2095
- useSuspenseQuery as useSuspenseQueryOriginal2
2096
- } from "@tanstack/react-query";
2097
-
2098
- // src/core/query/collection-hooks.ts
2099
- import {
2100
- useQuery as useQueryOriginal,
2101
- useSuspenseQuery as useSuspenseQueryOriginal,
2102
- useInfiniteQuery as useInfiniteQueryOriginal,
2103
- useSuspenseInfiniteQuery as useSuspenseInfiniteQueryOriginal,
2104
- useMutation as useMutationOriginal
2105
- } from "@tanstack/react-query";
2106
-
2107
- // src/core/query/query-keys.ts
2108
- function collectionKeys(collection) {
2109
- return {
2110
- all: [collection],
2111
- lists: () => [collection, "list"],
2112
- list: (options) => [collection, "list", options],
2113
- details: () => [collection, "detail"],
2114
- detail: (id, options) => [collection, "detail", id, options],
2115
- infinites: () => [collection, "infinite"],
2116
- infinite: (options) => [collection, "infinite", options]
2117
- };
2118
- }
2119
- var customerKeys = {
2120
- all: ["customer"],
2121
- me: () => ["customer", "me"]
2122
- };
2123
- var productKeys = {
2124
- listingGroups: (options) => ["products", "listing-groups", "list", options],
2125
- listingGroupsInfinite: (options) => ["products", "listing-groups", "infinite", options],
2126
- detail: (params) => ["products", "detail", params],
2127
- detailAll: () => ["products", "detail"]
2128
1681
  };
2129
1682
 
2130
- // src/core/query/collection-hooks.ts
2131
- var PRODUCT_DETAIL_INVALIDATING_COLLECTIONS = /* @__PURE__ */ new Set([
2132
- "products",
2133
- "product-variants",
2134
- "product-options",
2135
- "product-option-values",
2136
- "product-categories",
2137
- "product-tags",
2138
- "product-collections",
2139
- "brands",
2140
- "brand-logos",
2141
- "images"
2142
- ]);
2143
- var DEFAULT_PAGE_SIZE = 20;
2144
- var CollectionHooks = class {
2145
- constructor(queryClient, collectionClient) {
2146
- this.queryClient = queryClient;
2147
- this.collectionClient = collectionClient;
2148
- }
2149
- // ===== useQuery =====
2150
- useQuery(params, options) {
2151
- const { collection, options: queryOptions } = params;
2152
- const { placeholderData, ...restOptions } = options ?? {};
2153
- return useQueryOriginal({
2154
- queryKey: collectionKeys(collection).list(queryOptions),
2155
- queryFn: async () => {
2156
- return await this.collectionClient.from(collection).find(queryOptions);
2157
- },
2158
- ...restOptions,
2159
- // NonFunctionGuard<T> incompatible with generic union types — safe cast
2160
- ...placeholderData !== void 0 && {
2161
- placeholderData
2162
- }
2163
- });
1683
+ // src/core/api/product-api.ts
1684
+ var ProductApi = class extends BaseApi {
1685
+ constructor(options) {
1686
+ super("ProductApi", options);
2164
1687
  }
2165
- // ===== useSuspenseQuery =====
2166
- useSuspenseQuery(params, options) {
2167
- const { collection, options: queryOptions } = params;
2168
- return useSuspenseQueryOriginal({
2169
- queryKey: collectionKeys(collection).list(queryOptions),
2170
- queryFn: async () => {
2171
- return await this.collectionClient.from(collection).find(queryOptions);
2172
- },
2173
- ...options
2174
- });
1688
+ /**
1689
+ * Check point-in-time stock availability for one or more product variants.
1690
+ * Results reflect available stock at the moment of the call and are not guaranteed
1691
+ * to remain available by the time an order is placed.
1692
+ */
1693
+ stockCheck(params) {
1694
+ return this.request("/api/products/stock-check", params);
2175
1695
  }
2176
- // ===== useQueryById =====
2177
- useQueryById(params, options) {
2178
- const { collection, id, options: queryOptions } = params;
2179
- const { placeholderData, ...restOptions } = options ?? {};
2180
- return useQueryOriginal({
2181
- queryKey: collectionKeys(collection).detail(id, queryOptions),
2182
- queryFn: async () => {
2183
- return await this.collectionClient.from(collection).findById(id, queryOptions);
2184
- },
2185
- ...restOptions,
2186
- // NonFunctionGuard<T> incompatible with generic union types — safe cast
2187
- ...placeholderData !== void 0 && {
2188
- placeholderData
2189
- }
2190
- });
2191
- }
2192
- // ===== useSuspenseQueryById =====
2193
- useSuspenseQueryById(params, options) {
2194
- const { collection, id, options: queryOptions } = params;
2195
- return useSuspenseQueryOriginal({
2196
- queryKey: collectionKeys(collection).detail(id, queryOptions),
2197
- queryFn: async () => {
2198
- return await this.collectionClient.from(collection).findById(id, queryOptions);
2199
- },
2200
- ...options
2201
- });
2202
- }
2203
- // ===== useInfiniteQuery =====
2204
- useInfiniteQuery(params, options) {
2205
- const {
2206
- collection,
2207
- options: queryOptions,
2208
- pageSize = DEFAULT_PAGE_SIZE
2209
- } = params;
2210
- return useInfiniteQueryOriginal({
2211
- queryKey: collectionKeys(collection).infinite(queryOptions),
2212
- queryFn: async ({ pageParam }) => {
2213
- const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
2214
- return response;
2215
- },
2216
- initialPageParam: 1,
2217
- getNextPageParam: (lastPage) => {
2218
- return lastPage.hasNextPage ? lastPage.nextPage : void 0;
2219
- },
2220
- ...options
2221
- });
2222
- }
2223
- // ===== useSuspenseInfiniteQuery =====
2224
- useSuspenseInfiniteQuery(params, options) {
2225
- const {
2226
- collection,
2227
- options: queryOptions,
2228
- pageSize = DEFAULT_PAGE_SIZE
2229
- } = params;
2230
- return useSuspenseInfiniteQueryOriginal({
2231
- queryKey: collectionKeys(collection).infinite(queryOptions),
2232
- queryFn: async ({ pageParam }) => {
2233
- const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
2234
- return response;
2235
- },
2236
- initialPageParam: 1,
2237
- getNextPageParam: (lastPage) => {
2238
- return lastPage.hasNextPage ? lastPage.nextPage : void 0;
2239
- },
2240
- ...options
2241
- });
2242
- }
2243
- // ===== prefetchQuery =====
2244
- async prefetchQuery(params, options) {
2245
- const { collection, options: queryOptions } = params;
2246
- return this.queryClient.prefetchQuery({
2247
- queryKey: collectionKeys(collection).list(queryOptions),
2248
- queryFn: async () => {
2249
- return await this.collectionClient.from(collection).find(queryOptions);
2250
- },
2251
- ...options
2252
- });
2253
- }
2254
- // ===== prefetchQueryById =====
2255
- async prefetchQueryById(params, options) {
2256
- const { collection, id, options: queryOptions } = params;
2257
- return this.queryClient.prefetchQuery({
2258
- queryKey: collectionKeys(collection).detail(id, queryOptions),
2259
- queryFn: async () => {
2260
- return await this.collectionClient.from(collection).findById(id, queryOptions);
2261
- },
2262
- ...options
2263
- });
2264
- }
2265
- // ===== prefetchInfiniteQuery =====
2266
- async prefetchInfiniteQuery(params, options) {
2267
- const {
2268
- collection,
2269
- options: queryOptions,
2270
- pageSize = DEFAULT_PAGE_SIZE
2271
- } = params;
2272
- return this.queryClient.prefetchInfiniteQuery({
2273
- queryKey: collectionKeys(collection).infinite(queryOptions),
2274
- queryFn: async ({ pageParam }) => {
2275
- const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
2276
- return response;
2277
- },
2278
- initialPageParam: 1,
2279
- getNextPageParam: (lastPage) => {
2280
- return lastPage.hasNextPage ? lastPage.nextPage : void 0;
2281
- },
2282
- pages: options?.pages ?? 1,
2283
- staleTime: options?.staleTime
2284
- });
2285
- }
2286
- // ===== Mutation Hooks =====
2287
- useCreate(params, options) {
2288
- const { collection } = params;
2289
- return useMutationOriginal({
2290
- mutationFn: async (variables) => {
2291
- return await this.collectionClient.from(collection).create(
2292
- variables.data,
2293
- variables.file ? { file: variables.file, filename: variables.filename } : void 0
2294
- );
2295
- },
2296
- onSuccess: (data) => {
2297
- this.queryClient.invalidateQueries({
2298
- queryKey: collectionKeys(collection).all
2299
- });
2300
- if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
2301
- this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
2302
- }
2303
- options?.onSuccess?.(data);
2304
- },
2305
- onError: options?.onError,
2306
- onSettled: options?.onSettled
2307
- });
2308
- }
2309
- useUpdate(params, options) {
2310
- const { collection } = params;
2311
- return useMutationOriginal({
2312
- mutationFn: async (variables) => {
2313
- return await this.collectionClient.from(collection).update(
2314
- variables.id,
2315
- variables.data,
2316
- variables.file ? { file: variables.file, filename: variables.filename } : void 0
2317
- );
2318
- },
2319
- onSuccess: (data) => {
2320
- this.queryClient.invalidateQueries({
2321
- queryKey: collectionKeys(collection).all
2322
- });
2323
- if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
2324
- this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
2325
- }
2326
- options?.onSuccess?.(data);
2327
- },
2328
- onError: options?.onError,
2329
- onSettled: options?.onSettled
2330
- });
2331
- }
2332
- useRemove(params, options) {
2333
- const { collection } = params;
2334
- return useMutationOriginal({
2335
- mutationFn: async (id) => {
2336
- return await this.collectionClient.from(collection).remove(id);
2337
- },
2338
- onSuccess: (data) => {
2339
- this.queryClient.invalidateQueries({
2340
- queryKey: collectionKeys(collection).all
2341
- });
2342
- if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
2343
- this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
2344
- }
2345
- options?.onSuccess?.(data);
2346
- },
2347
- onError: options?.onError,
2348
- onSettled: options?.onSettled
2349
- });
2350
- }
2351
- // ===== Cache Utilities =====
2352
- invalidateQueries(collection, type) {
2353
- const queryKey = type ? [collection, type] : [collection];
2354
- return this.queryClient.invalidateQueries({ queryKey });
2355
- }
2356
- getQueryData(collection, type, idOrOptions, options) {
2357
- if (type === "list") {
2358
- return this.queryClient.getQueryData(
2359
- collectionKeys(collection).list(idOrOptions)
2360
- );
2361
- }
2362
- return this.queryClient.getQueryData(
2363
- collectionKeys(collection).detail(idOrOptions, options)
1696
+ listingGroups(params) {
1697
+ return this.request(
1698
+ "/api/products/listing-groups",
1699
+ params
2364
1700
  );
2365
1701
  }
2366
- setQueryData(collection, type, dataOrId, dataOrOptions, options) {
2367
- if (type === "list") {
2368
- this.queryClient.setQueryData(
2369
- collectionKeys(collection).list(dataOrOptions),
2370
- dataOrId
2371
- );
2372
- } else {
2373
- this.queryClient.setQueryData(
2374
- collectionKeys(collection).detail(dataOrId, options),
2375
- dataOrOptions
2376
- );
1702
+ /**
1703
+ * Fetch full product detail by slug or id.
1704
+ * Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
1705
+ * `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
1706
+ * inspect `client.lastRequestId` against backend logs.
1707
+ */
1708
+ async detail(params) {
1709
+ try {
1710
+ return await this.request("/api/products/detail", params);
1711
+ } catch (err) {
1712
+ if (err instanceof NotFoundError) return null;
1713
+ throw err;
2377
1714
  }
2378
1715
  }
1716
+ /**
1717
+ * Atomically create or update a product together with its options,
1718
+ * option-values, and variants in a single transaction. Mirrors Shopify's
1719
+ * `productSet` shape and is the canonical write path for the MCP
1720
+ * `product-upsert` tool.
1721
+ */
1722
+ upsert(params) {
1723
+ return this.request("/api/products/upsert", params);
1724
+ }
2379
1725
  };
2380
1726
 
2381
- // src/core/query/customer-hooks.ts
2382
- import {
2383
- useQuery as useQueryOriginal2,
2384
- useMutation as useMutationOriginal2
2385
- } from "@tanstack/react-query";
2386
- function createMutation(mutationFn, callbacks, onSuccessExtra) {
2387
- return useMutationOriginal2({
2388
- mutationFn,
2389
- onSuccess: (data) => {
2390
- onSuccessExtra?.(data);
2391
- callbacks?.onSuccess?.(data);
2392
- },
2393
- onError: callbacks?.onError,
2394
- onSettled: callbacks?.onSettled
2395
- });
1727
+ // src/core/webhook/index.ts
1728
+ function isValidWebhookEvent(data) {
1729
+ if (typeof data !== "object" || data === null) return false;
1730
+ const obj = data;
1731
+ return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
2396
1732
  }
2397
- var CustomerHooks = class {
2398
- constructor(queryClient, customerAuth) {
2399
- this.invalidateMe = () => {
2400
- this.queryClient.invalidateQueries({ queryKey: customerKeys.me() });
2401
- };
2402
- this.queryClient = queryClient;
2403
- this.customerAuth = customerAuth;
1733
+ var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
1734
+ function isRecord(value) {
1735
+ return typeof value === "object" && value !== null;
1736
+ }
1737
+ function hasString(value, key) {
1738
+ return typeof value[key] === "string";
1739
+ }
1740
+ function hasStringOrNumber(value, key) {
1741
+ return typeof value[key] === "string" || typeof value[key] === "number";
1742
+ }
1743
+ function isCustomerPasswordResetWebhookEvent(event) {
1744
+ if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
1745
+ return false;
2404
1746
  }
2405
- ensureCustomerAuth() {
2406
- if (!this.customerAuth) {
2407
- throw createConfigError(
2408
- "Customer hooks require Client. Use createClient() instead of createServerClient()."
2409
- );
1747
+ return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
1748
+ }
1749
+ function createCustomerAuthWebhookHandler(handlers) {
1750
+ return async (event) => {
1751
+ if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
1752
+ await handlers.passwordReset(event.data, event);
1753
+ return;
2410
1754
  }
2411
- return this.customerAuth;
2412
- }
2413
- // ===== useCustomerMe =====
2414
- useCustomerMe(options) {
2415
- return useQueryOriginal2({
2416
- queryKey: customerKeys.me(),
2417
- queryFn: async () => {
2418
- return await this.ensureCustomerAuth().me();
2419
- },
2420
- ...options,
2421
- enabled: (options?.enabled ?? true) && !!this.customerAuth?.isAuthenticated()
2422
- });
2423
- }
2424
- // ===== Mutations =====
2425
- useCustomerLogin(options) {
2426
- return createMutation(
2427
- (data) => this.ensureCustomerAuth().login(data),
2428
- options,
2429
- this.invalidateMe
2430
- );
2431
- }
2432
- useCustomerRegister(options) {
2433
- return createMutation(
2434
- (data) => this.ensureCustomerAuth().register(data),
2435
- options
2436
- );
2437
- }
2438
- useCustomerLogout(options) {
2439
- return useMutationOriginal2({
2440
- mutationFn: async () => {
2441
- this.ensureCustomerAuth().logout();
2442
- },
2443
- onSuccess: () => {
2444
- this.queryClient.removeQueries({ queryKey: customerKeys.all });
2445
- options?.onSuccess?.();
2446
- },
2447
- onError: options?.onError,
2448
- onSettled: options?.onSettled
2449
- });
2450
- }
2451
- useCustomerForgotPassword(options) {
2452
- return createMutation(
2453
- (email) => this.ensureCustomerAuth().forgotPassword(email).then(() => {
2454
- }),
2455
- options
2456
- );
2457
- }
2458
- useCustomerResetPassword(options) {
2459
- return createMutation(
2460
- (data) => this.ensureCustomerAuth().resetPassword(data.token, data.password).then(() => {
2461
- }),
2462
- options
2463
- );
2464
- }
2465
- useCustomerRefreshToken(options) {
2466
- return createMutation(
2467
- () => this.ensureCustomerAuth().refreshToken(),
2468
- options,
2469
- this.invalidateMe
2470
- );
2471
- }
2472
- useCustomerUpdateProfile(options) {
2473
- return createMutation(
2474
- (data) => this.ensureCustomerAuth().updateProfile(data),
2475
- options,
2476
- this.invalidateMe
2477
- );
2478
- }
2479
- useCustomerChangePassword(options) {
2480
- return createMutation(
2481
- (data) => this.ensureCustomerAuth().changePassword(data.currentPassword, data.newPassword).then(() => {
2482
- }),
2483
- options
2484
- );
2485
- }
2486
- // ===== Customer Cache Utilities =====
2487
- invalidateCustomerQueries() {
2488
- return this.queryClient.invalidateQueries({ queryKey: customerKeys.all });
2489
- }
2490
- getCustomerData() {
2491
- return this.queryClient.getQueryData(customerKeys.me());
2492
- }
2493
- setCustomerData(data) {
2494
- this.queryClient.setQueryData(customerKeys.me(), data);
1755
+ await handlers.unhandled?.(event);
1756
+ };
1757
+ }
1758
+ async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
1759
+ const encoder = new TextEncoder();
1760
+ const key = await crypto.subtle.importKey(
1761
+ "raw",
1762
+ encoder.encode(secret),
1763
+ { name: "HMAC", hash: "SHA-256" },
1764
+ false,
1765
+ ["verify"]
1766
+ );
1767
+ if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
1768
+ return false;
2495
1769
  }
2496
- };
2497
-
2498
- // src/core/query/query-hooks.ts
2499
- var QueryHooks = class extends CollectionHooks {
2500
- constructor(queryClient, collectionClient, customerAuth, commerceClient) {
2501
- super(queryClient, collectionClient);
2502
- // --- Customer hooks delegation ---
2503
- this.useCustomerMe = (...args) => this._customer.useCustomerMe(...args);
2504
- this.useCustomerLogin = (...args) => this._customer.useCustomerLogin(...args);
2505
- this.useCustomerRegister = (...args) => this._customer.useCustomerRegister(...args);
2506
- this.useCustomerLogout = (...args) => this._customer.useCustomerLogout(...args);
2507
- this.useCustomerForgotPassword = (...args) => this._customer.useCustomerForgotPassword(...args);
2508
- this.useCustomerResetPassword = (...args) => this._customer.useCustomerResetPassword(...args);
2509
- this.useCustomerRefreshToken = (...args) => this._customer.useCustomerRefreshToken(...args);
2510
- this.useCustomerUpdateProfile = (...args) => this._customer.useCustomerUpdateProfile(...args);
2511
- this.useCustomerChangePassword = (...args) => this._customer.useCustomerChangePassword(...args);
2512
- // --- Customer cache delegation ---
2513
- this.invalidateCustomerQueries = () => this._customer.invalidateCustomerQueries();
2514
- this.getCustomerData = () => this._customer.getCustomerData();
2515
- this.setCustomerData = (data) => this._customer.setCustomerData(data);
2516
- this._customer = new CustomerHooks(queryClient, customerAuth);
2517
- this._commerce = commerceClient;
2518
- }
2519
- useProductListingGroupsQuery(params, options) {
2520
- const queryOptions = params.options;
2521
- const { placeholderData, ...restOptions } = options ?? {};
2522
- return useQueryOriginal3({
2523
- queryKey: productKeys.listingGroups(queryOptions),
2524
- queryFn: async () => this.collectionClient.requestFindEndpoint(
2525
- "/api/products/listing-groups/query",
2526
- { options: queryOptions }
2527
- ),
2528
- ...restOptions,
2529
- ...placeholderData !== void 0 && {
2530
- placeholderData
1770
+ const sigBytes = new Uint8Array(
1771
+ (signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
1772
+ );
1773
+ return crypto.subtle.verify(
1774
+ "HMAC",
1775
+ key,
1776
+ sigBytes,
1777
+ encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
1778
+ );
1779
+ }
1780
+ function timestampIsFresh(timestamp, toleranceSeconds) {
1781
+ if (!/^\d+$/.test(timestamp)) return false;
1782
+ const timestampMs = Number(timestamp);
1783
+ if (!Number.isFinite(timestampMs)) return false;
1784
+ const skewMs = Math.abs(Date.now() - timestampMs);
1785
+ return skewMs <= toleranceSeconds * 1e3;
1786
+ }
1787
+ async function handleWebhook(request, handler, options) {
1788
+ try {
1789
+ const rawBody = await request.text();
1790
+ if (options?.secret) {
1791
+ const signature = request.headers.get("x-webhook-signature") || "";
1792
+ const timestamp = request.headers.get("x-webhook-timestamp") || "";
1793
+ const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
1794
+ const toleranceSeconds = options.toleranceSeconds ?? 300;
1795
+ const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
1796
+ rawBody,
1797
+ options.secret,
1798
+ signature,
1799
+ timestamp,
1800
+ deliveryId
1801
+ );
1802
+ if (!valid) {
1803
+ return new Response(
1804
+ JSON.stringify({ error: "Invalid webhook signature" }),
1805
+ { status: 401, headers: { "Content-Type": "application/json" } }
1806
+ );
2531
1807
  }
2532
- });
2533
- }
2534
- useSuspenseProductListingGroupsQuery(params, options) {
2535
- const queryOptions = params.options;
2536
- return useSuspenseQueryOriginal2({
2537
- queryKey: productKeys.listingGroups(queryOptions),
2538
- queryFn: async () => this.collectionClient.requestFindEndpoint(
2539
- "/api/products/listing-groups/query",
2540
- { options: queryOptions }
2541
- ),
2542
- ...options
2543
- });
2544
- }
2545
- useInfiniteProductListingGroupsQuery(params, options) {
2546
- const {
2547
- options: queryOptions,
2548
- pageSize = 20
2549
- } = params;
2550
- return useInfiniteQueryOriginal2({
2551
- queryKey: productKeys.listingGroupsInfinite(queryOptions),
2552
- queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
2553
- "/api/products/listing-groups/query",
2554
- {
2555
- options: { ...queryOptions, page: pageParam, limit: pageSize }
2556
- }
2557
- ),
2558
- initialPageParam: 1,
2559
- getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
2560
- ...options
2561
- });
2562
- }
2563
- useSuspenseInfiniteProductListingGroupsQuery(params, options) {
2564
- const {
2565
- options: queryOptions,
2566
- pageSize = 20
2567
- } = params;
2568
- return useSuspenseInfiniteQueryOriginal2({
2569
- queryKey: productKeys.listingGroupsInfinite(queryOptions),
2570
- queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
2571
- "/api/products/listing-groups/query",
2572
- {
2573
- options: { ...queryOptions, page: pageParam, limit: pageSize }
2574
- }
2575
- ),
2576
- initialPageParam: 1,
2577
- getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
2578
- ...options
2579
- });
2580
- }
2581
- async prefetchProductListingGroupsQuery(params, options) {
2582
- const queryOptions = params.options;
2583
- return this.queryClient.prefetchQuery({
2584
- queryKey: productKeys.listingGroups(queryOptions),
2585
- queryFn: async () => this.collectionClient.requestFindEndpoint(
2586
- "/api/products/listing-groups/query",
2587
- { options: queryOptions }
2588
- ),
2589
- ...options
2590
- });
2591
- }
2592
- async prefetchInfiniteProductListingGroupsQuery(params, options) {
2593
- const {
2594
- options: queryOptions,
2595
- pageSize = 20
2596
- } = params;
2597
- return this.queryClient.prefetchInfiniteQuery({
2598
- queryKey: productKeys.listingGroupsInfinite(queryOptions),
2599
- queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
2600
- "/api/products/listing-groups/query",
2601
- {
2602
- options: { ...queryOptions, page: pageParam, limit: pageSize }
2603
- }
2604
- ),
2605
- initialPageParam: 1,
2606
- getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
2607
- pages: options?.pages ?? 1,
2608
- staleTime: options?.staleTime
2609
- });
2610
- }
2611
- useProductDetail(params, options) {
2612
- const discriminator = "slug" in params ? params.slug : params.id;
2613
- const enabled = options?.enabled !== false && Boolean(discriminator);
2614
- return useQueryOriginal3({
2615
- queryKey: productKeys.detail(params),
2616
- queryFn: () => this._commerce.product.detail(params),
2617
- enabled
2618
- });
2619
- }
2620
- useProductDetailBySlug(slug, options) {
2621
- return this.useProductDetail({ slug }, options);
2622
- }
2623
- useProductDetailById(id, options) {
2624
- return this.useProductDetail({ id }, options);
2625
- }
2626
- };
2627
-
2628
- // src/core/client/client.ts
2629
- var Client = class {
2630
- constructor(options) {
2631
- this.lastRequestId = null;
2632
- const publishableKey = options.publishableKey;
2633
- if (!publishableKey) {
2634
- throw createConfigError("publishableKey is required.");
1808
+ } else {
1809
+ console.warn(
1810
+ "[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
1811
+ );
2635
1812
  }
2636
- this.config = { ...options, publishableKey };
2637
- const metadata = {
2638
- timestamp: Date.now(),
2639
- userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
2640
- };
2641
- this.state = { metadata };
2642
- this.queryClient = getQueryClient();
2643
- this.customer = new CustomerNamespace(
2644
- this.config.publishableKey,
2645
- options.customer
1813
+ const body = JSON.parse(rawBody);
1814
+ if (!isValidWebhookEvent(body)) {
1815
+ return new Response(
1816
+ JSON.stringify({ error: "Invalid webhook event format" }),
1817
+ { status: 400, headers: { "Content-Type": "application/json" } }
1818
+ );
1819
+ }
1820
+ await handler(body);
1821
+ return new Response(
1822
+ JSON.stringify({ success: true, message: "Webhook processed" }),
1823
+ { status: 200, headers: { "Content-Type": "application/json" } }
2646
1824
  );
2647
- const onUnauthorized = async () => {
2648
- try {
2649
- const result = await this.customer.auth.refreshToken();
2650
- return result.token ?? null;
2651
- } catch {
2652
- return null;
2653
- }
2654
- };
2655
- const onRequestId = (id) => {
2656
- this.lastRequestId = id;
2657
- };
2658
- this.commerce = new CommerceClient({
2659
- publishableKey: this.config.publishableKey,
2660
- customerToken: () => this.customer.auth.getToken(),
2661
- onUnauthorized,
2662
- onRequestId,
2663
- customerAuth: this.customer.auth
2664
- });
2665
- this.community = new CommunityClient({
2666
- publishableKey: this.config.publishableKey,
2667
- customerToken: () => this.customer.auth.getToken(),
2668
- onUnauthorized,
2669
- onRequestId
1825
+ } catch (error) {
1826
+ console.error("Webhook processing error:", error);
1827
+ return new Response(JSON.stringify({ error: "Internal server error" }), {
1828
+ status: 500,
1829
+ headers: { "Content-Type": "application/json" }
2670
1830
  });
2671
- const collectionClient = new CollectionClient(
2672
- this.config.publishableKey,
2673
- void 0,
2674
- () => this.customer.auth.getToken(),
2675
- onUnauthorized,
2676
- onRequestId
2677
- );
2678
- this.collections = new ReadOnlyCollectionClient(
2679
- this.config.publishableKey,
2680
- void 0,
2681
- () => this.customer.auth.getToken(),
2682
- onUnauthorized,
2683
- onRequestId
2684
- );
2685
- this.query = new QueryHooks(
2686
- this.queryClient,
2687
- collectionClient,
2688
- this.customer.auth,
2689
- this.commerce
2690
- );
2691
- }
2692
- getState() {
2693
- return { ...this.state };
2694
- }
2695
- getConfig() {
2696
- return { ...this.config };
2697
1831
  }
2698
- };
2699
- function createClient(options) {
2700
- return new Client(options);
2701
1832
  }
2702
-
2703
- // src/core/client/client.server.ts
2704
- var ServerClient = class {
2705
- constructor(options) {
2706
- this.lastRequestId = null;
2707
- if (typeof window !== "undefined") {
2708
- throw createConfigError(
2709
- "ServerClient must not be used in a browser environment. This risks exposing your secretKey in client bundles. Use createClient() for browser code instead."
2710
- );
2711
- }
2712
- if (!options.secretKey) {
2713
- throw createConfigError("secretKey is required.");
2714
- }
2715
- if (!options.publishableKey) {
2716
- throw createConfigError(
2717
- "publishableKey is required. It is used for rate limiting and monthly quota enforcement via the X-Publishable-Key header. Get it from Console > Settings > API Keys."
1833
+ function createTypedWebhookHandler(collection, handler) {
1834
+ return async (event) => {
1835
+ if (event.collection !== collection) {
1836
+ throw new Error(
1837
+ `Expected collection "${collection}", got "${event.collection}"`
2718
1838
  );
2719
1839
  }
2720
- this.config = { ...options, publishableKey: options.publishableKey };
2721
- const metadata = {
2722
- timestamp: Date.now(),
2723
- userAgent: "Node.js"
2724
- };
2725
- this.state = { metadata };
2726
- const onRequestId = (id) => {
2727
- this.lastRequestId = id;
2728
- };
2729
- const serverOptions = {
2730
- publishableKey: this.config.publishableKey,
2731
- secretKey: this.config.secretKey,
2732
- onRequestId
2733
- };
2734
- this.commerce = new ServerCommerceClient(serverOptions);
2735
- const communityClient = new CommunityClient(serverOptions);
2736
- const moderationApi = new ModerationApi(serverOptions);
2737
- this.community = Object.assign(communityClient, {
2738
- moderation: {
2739
- banCustomer: moderationApi.banCustomer.bind(moderationApi),
2740
- unbanCustomer: moderationApi.unbanCustomer.bind(moderationApi)
2741
- }
2742
- });
2743
- this.collections = new ServerCollectionClient(
2744
- this.config.publishableKey,
2745
- this.config.secretKey,
2746
- void 0,
2747
- void 0,
2748
- onRequestId
2749
- );
2750
- this.queryClient = getQueryClient();
2751
- this.query = new QueryHooks(this.queryClient, this.collections, void 0, this.commerce);
2752
- }
2753
- getState() {
2754
- return { ...this.state };
2755
- }
2756
- getConfig() {
2757
- const { secretKey: _, ...safeConfig } = this.config;
2758
- return safeConfig;
2759
- }
2760
- };
2761
- function createServerClient(options) {
2762
- return new ServerClient(options);
1840
+ return handler(event);
1841
+ };
2763
1842
  }
2764
1843
 
1844
+ // src/core/collection/const.ts
1845
+ var INTERNAL_COLLECTIONS = [
1846
+ "users",
1847
+ "payload-kv",
1848
+ "payload-locked-documents",
1849
+ "payload-preferences",
1850
+ "payload-migrations",
1851
+ "payload-folders",
1852
+ "field-configs",
1853
+ "system-media",
1854
+ "track-assets",
1855
+ "audiences",
1856
+ "email-logs",
1857
+ "api-usage",
1858
+ "tenant-analytics-daily",
1859
+ "tenant-web-analytics-config",
1860
+ "analytics-event-schemas",
1861
+ "subscriptions",
1862
+ "billing-history",
1863
+ "inventory-reservations",
1864
+ "product-collection-items",
1865
+ "order-status-logs",
1866
+ "api-keys",
1867
+ "personal-access-tokens",
1868
+ "tenant-entitlements",
1869
+ "tenant-purge-jobs",
1870
+ "direct-upload-sessions",
1871
+ "webhook-events",
1872
+ "webhook-deliveries",
1873
+ "audit-logs",
1874
+ "plans",
1875
+ "webhooks",
1876
+ "event-registrations"
1877
+ ];
1878
+ var COLLECTIONS = [
1879
+ "tenants",
1880
+ "tenant-metadata",
1881
+ "tenant-logos",
1882
+ "products",
1883
+ "product-variants",
1884
+ "product-options",
1885
+ "product-option-values",
1886
+ "product-categories",
1887
+ "product-tags",
1888
+ "product-collections",
1889
+ "brands",
1890
+ "brand-logos",
1891
+ "orders",
1892
+ "order-items",
1893
+ "returns",
1894
+ "return-items",
1895
+ "fulfillments",
1896
+ "fulfillment-items",
1897
+ "transactions",
1898
+ "customers",
1899
+ "customer-profiles",
1900
+ "customer-addresses",
1901
+ "carts",
1902
+ "cart-items",
1903
+ "discounts",
1904
+ "shipping-policies",
1905
+ "shipping-zones",
1906
+ "documents",
1907
+ "document-categories",
1908
+ "document-types",
1909
+ "articles",
1910
+ "article-authors",
1911
+ "article-categories",
1912
+ "article-tags",
1913
+ "playlists",
1914
+ "playlist-categories",
1915
+ "playlist-tags",
1916
+ "tracks",
1917
+ "track-categories",
1918
+ "track-tags",
1919
+ "galleries",
1920
+ "gallery-categories",
1921
+ "gallery-tags",
1922
+ "gallery-items",
1923
+ "links",
1924
+ "link-categories",
1925
+ "link-tags",
1926
+ "canvases",
1927
+ "canvas-node-types",
1928
+ "canvas-edge-types",
1929
+ "canvas-categories",
1930
+ "canvas-tags",
1931
+ "canvas-nodes",
1932
+ "canvas-edges",
1933
+ "videos",
1934
+ "video-categories",
1935
+ "video-tags",
1936
+ "live-streams",
1937
+ "images",
1938
+ "forms",
1939
+ "form-submissions",
1940
+ // Community
1941
+ "posts",
1942
+ "comments",
1943
+ "reactions",
1944
+ "reaction-types",
1945
+ "bookmarks",
1946
+ "post-categories",
1947
+ "customer-profile-lists",
1948
+ // Events
1949
+ "event-calendars",
1950
+ "events",
1951
+ "event-categories",
1952
+ "event-occurrences",
1953
+ "event-tags"
1954
+ ];
1955
+ var SERVER_ONLY_COLLECTIONS = [
1956
+ "customer-groups",
1957
+ "reports",
1958
+ "community-bans"
1959
+ ];
1960
+ var SERVER_COLLECTIONS = [
1961
+ ...COLLECTIONS,
1962
+ ...SERVER_ONLY_COLLECTIONS
1963
+ ];
1964
+
2765
1965
  // src/core/query/realtime.ts
2766
1966
  var INITIAL_RECONNECT_DELAY = 1e3;
2767
1967
  var MAX_RECONNECT_DELAY = 3e4;
@@ -2907,123 +2107,6 @@ var RealtimeConnection = class {
2907
2107
  }
2908
2108
  };
2909
2109
 
2910
- // src/core/webhook/index.ts
2911
- function isValidWebhookEvent(data) {
2912
- if (typeof data !== "object" || data === null) return false;
2913
- const obj = data;
2914
- return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
2915
- }
2916
- var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
2917
- function isRecord(value) {
2918
- return typeof value === "object" && value !== null;
2919
- }
2920
- function hasString(value, key) {
2921
- return typeof value[key] === "string";
2922
- }
2923
- function hasStringOrNumber(value, key) {
2924
- return typeof value[key] === "string" || typeof value[key] === "number";
2925
- }
2926
- function isCustomerPasswordResetWebhookEvent(event) {
2927
- if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
2928
- return false;
2929
- }
2930
- return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
2931
- }
2932
- function createCustomerAuthWebhookHandler(handlers) {
2933
- return async (event) => {
2934
- if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
2935
- await handlers.passwordReset(event.data, event);
2936
- return;
2937
- }
2938
- await handlers.unhandled?.(event);
2939
- };
2940
- }
2941
- async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
2942
- const encoder = new TextEncoder();
2943
- const key = await crypto.subtle.importKey(
2944
- "raw",
2945
- encoder.encode(secret),
2946
- { name: "HMAC", hash: "SHA-256" },
2947
- false,
2948
- ["verify"]
2949
- );
2950
- if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
2951
- return false;
2952
- }
2953
- const sigBytes = new Uint8Array(
2954
- (signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
2955
- );
2956
- return crypto.subtle.verify(
2957
- "HMAC",
2958
- key,
2959
- sigBytes,
2960
- encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
2961
- );
2962
- }
2963
- function timestampIsFresh(timestamp, toleranceSeconds) {
2964
- if (!/^\d+$/.test(timestamp)) return false;
2965
- const timestampMs = Number(timestamp);
2966
- if (!Number.isFinite(timestampMs)) return false;
2967
- const skewMs = Math.abs(Date.now() - timestampMs);
2968
- return skewMs <= toleranceSeconds * 1e3;
2969
- }
2970
- async function handleWebhook(request, handler, options) {
2971
- try {
2972
- const rawBody = await request.text();
2973
- if (options?.secret) {
2974
- const signature = request.headers.get("x-webhook-signature") || "";
2975
- const timestamp = request.headers.get("x-webhook-timestamp") || "";
2976
- const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
2977
- const toleranceSeconds = options.toleranceSeconds ?? 300;
2978
- const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
2979
- rawBody,
2980
- options.secret,
2981
- signature,
2982
- timestamp,
2983
- deliveryId
2984
- );
2985
- if (!valid) {
2986
- return new Response(
2987
- JSON.stringify({ error: "Invalid webhook signature" }),
2988
- { status: 401, headers: { "Content-Type": "application/json" } }
2989
- );
2990
- }
2991
- } else {
2992
- console.warn(
2993
- "[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
2994
- );
2995
- }
2996
- const body = JSON.parse(rawBody);
2997
- if (!isValidWebhookEvent(body)) {
2998
- return new Response(
2999
- JSON.stringify({ error: "Invalid webhook event format" }),
3000
- { status: 400, headers: { "Content-Type": "application/json" } }
3001
- );
3002
- }
3003
- await handler(body);
3004
- return new Response(
3005
- JSON.stringify({ success: true, message: "Webhook processed" }),
3006
- { status: 200, headers: { "Content-Type": "application/json" } }
3007
- );
3008
- } catch (error) {
3009
- console.error("Webhook processing error:", error);
3010
- return new Response(JSON.stringify({ error: "Internal server error" }), {
3011
- status: 500,
3012
- headers: { "Content-Type": "application/json" }
3013
- });
3014
- }
3015
- }
3016
- function createTypedWebhookHandler(collection, handler) {
3017
- return async (event) => {
3018
- if (event.collection !== collection) {
3019
- throw new Error(
3020
- `Expected collection "${collection}", got "${event.collection}"`
3021
- );
3022
- }
3023
- return handler(event);
3024
- };
3025
- }
3026
-
3027
2110
  // src/utils/ecommerce.ts
3028
2111
  var ProductSelectionCodecError = class extends Error {
3029
2112
  constructor(message) {
@@ -3308,38 +2391,45 @@ function hasExplicitSelection(selection) {
3308
2391
  );
3309
2392
  }
3310
2393
  function assignSelectedValue(matrix, selectedByOptionId, optionId, valueId) {
3311
- if (valueId == null) return;
2394
+ if (valueId == null) return false;
3312
2395
  const normalizedValueId = String(valueId);
3313
- if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return;
2396
+ if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return false;
3314
2397
  selectedByOptionId.set(optionId, normalizedValueId);
2398
+ return true;
3315
2399
  }
3316
2400
  function assignSelectedValueSlugByOptionId(matrix, selectedByOptionId, optionId, valueSlug) {
3317
- if (!valueSlug) return;
2401
+ if (!valueSlug) return false;
3318
2402
  const option = matrix.optionById.get(optionId);
3319
- if (!option) return;
3320
- const value = option.values.find((candidate) => candidate.slug === valueSlug);
3321
- if (!value) return;
2403
+ if (!option) return false;
2404
+ const values = option.values.filter(
2405
+ (candidate) => candidate.slug === valueSlug
2406
+ );
2407
+ if (values.length > 1) {
2408
+ throw new ProductSelectionCodecError(
2409
+ `Ambiguous product selection value slug "${valueSlug}" for option "${optionId}". Use opt.<optionId>=<valueId>.`
2410
+ );
2411
+ }
2412
+ const value = values[0];
2413
+ if (!value) return false;
3322
2414
  selectedByOptionId.set(optionId, value.id);
2415
+ return true;
3323
2416
  }
3324
2417
  function assignSelectedValueSlugByOptionSlug(matrix, selectedByOptionId, optionSlug, valueSlug) {
3325
- if (!valueSlug) return;
2418
+ if (!valueSlug) return false;
3326
2419
  const option = matrix.optionBySlug.get(optionSlug);
3327
- if (!option) return;
3328
- const value = option.values.find((candidate) => candidate.slug === valueSlug);
3329
- if (!value) return;
3330
- selectedByOptionId.set(option.id, value.id);
3331
- }
3332
- function requireValueSlug(value) {
3333
- if (value.slug) return value.slug;
3334
- throw new ProductSelectionCodecError(
3335
- `Option value "${value.id}" does not have a slug and cannot be used in product selection URLs.`
3336
- );
3337
- }
3338
- function requireOptionSlug(option) {
3339
- if (option.slug) return option.slug;
3340
- throw new ProductSelectionCodecError(
3341
- `Option "${option.id}" does not have a slug and cannot be used in product selection URLs.`
2420
+ if (!option) return false;
2421
+ const values = option.values.filter(
2422
+ (candidate) => candidate.slug === valueSlug
3342
2423
  );
2424
+ if (values.length > 1) {
2425
+ throw new ProductSelectionCodecError(
2426
+ `Ambiguous product selection value slug "${valueSlug}" for option "${optionSlug}". Use opt.<optionId>=<valueId>.`
2427
+ );
2428
+ }
2429
+ const value = values[0];
2430
+ if (!value) return false;
2431
+ selectedByOptionId.set(option.id, value.id);
2432
+ return true;
3343
2433
  }
3344
2434
  function toSearchParams(search) {
3345
2435
  if (!search) return new URLSearchParams();
@@ -3363,6 +2453,15 @@ function slugLike(value) {
3363
2453
  }
3364
2454
  function assertNoAmbiguousSelectionParams(matrix, params) {
3365
2455
  const knownSelectionKeys = /* @__PURE__ */ new Set();
2456
+ const hasVariantParam = params.has("variant");
2457
+ const hasOptionParams = Array.from(params.keys()).some(
2458
+ (key) => key.startsWith("opt.")
2459
+ );
2460
+ if (hasVariantParam && hasOptionParams) {
2461
+ throw new ProductSelectionCodecError(
2462
+ "Product selection URL cannot mix variant=<variantId> with opt.<optionId>=<valueId> params."
2463
+ );
2464
+ }
3366
2465
  for (const option of matrix.options) {
3367
2466
  knownSelectionKeys.add(slugLike(option.slug));
3368
2467
  knownSelectionKeys.add(slugLike(option.title));
@@ -3375,12 +2474,25 @@ function assertNoAmbiguousSelectionParams(matrix, params) {
3375
2474
  const optionToken = key.slice(4);
3376
2475
  if (!optionToken || !matrix.optionBySlug.has(optionToken) && !matrix.optionById.has(optionToken)) {
3377
2476
  throw new ProductSelectionCodecError(
3378
- `Unknown product selection query parameter "${key}". Use opt.<optionSlug>=<valueSlug>.`
2477
+ `Unknown product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
2478
+ );
2479
+ }
2480
+ if (!value) {
2481
+ throw new ProductSelectionCodecError(
2482
+ `Product selection query parameter "${key}" requires a value ID or compatibility value slug.`
3379
2483
  );
3380
2484
  }
2485
+ continue;
2486
+ }
2487
+ if (key === "variant") {
3381
2488
  if (!value) {
3382
2489
  throw new ProductSelectionCodecError(
3383
- `Product selection query parameter "${key}" requires a value slug.`
2490
+ 'Product selection query parameter "variant" requires a variant ID.'
2491
+ );
2492
+ }
2493
+ if (!getVariantSelection(matrix, value)) {
2494
+ throw new ProductSelectionCodecError(
2495
+ `Unknown product selection variant "${value}".`
3384
2496
  );
3385
2497
  }
3386
2498
  continue;
@@ -3388,55 +2500,97 @@ function assertNoAmbiguousSelectionParams(matrix, params) {
3388
2500
  const keyToken = slugLike(key);
3389
2501
  if (knownSelectionKeys.has(keyToken) || value === "" && knownSelectionKeys.has(keyToken)) {
3390
2502
  throw new ProductSelectionCodecError(
3391
- `Ambiguous product selection query parameter "${key}". Use opt.<optionSlug>=<valueSlug>.`
2503
+ `Ambiguous product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
3392
2504
  );
3393
2505
  }
3394
2506
  }
3395
2507
  }
3396
- function emitLegacyOptionIdParam(options, event) {
2508
+ function emitCompatibilityOptionIdParam(options, event) {
3397
2509
  try {
2510
+ if (options?.onCompatibilityOptionIdParam) {
2511
+ options.onCompatibilityOptionIdParam(event);
2512
+ return;
2513
+ }
3398
2514
  options?.onLegacyOptionIdParam?.(event);
3399
2515
  } catch {
3400
2516
  }
3401
2517
  }
3402
2518
  function assignSearchSelection(matrix, selectedByOptionId, search, options) {
3403
- if (!search) return;
2519
+ if (!search) return null;
3404
2520
  const params = toSearchParams(search);
3405
2521
  assertNoAmbiguousSelectionParams(matrix, params);
3406
- for (const [key, valueSlug] of params.entries()) {
2522
+ const variantParam = params.get("variant");
2523
+ if (variantParam != null) {
2524
+ const variantSelection = getVariantSelection(matrix, variantParam);
2525
+ if (!variantSelection) {
2526
+ throw new ProductSelectionCodecError(
2527
+ `Unknown product selection variant "${variantParam}".`
2528
+ );
2529
+ }
2530
+ for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
2531
+ selectedByOptionId.set(optionId, valueId);
2532
+ }
2533
+ return variantSelection.id;
2534
+ }
2535
+ for (const [key, valueToken] of params.entries()) {
3407
2536
  if (!key.startsWith("opt.")) continue;
3408
2537
  const optionToken = key.slice(4);
2538
+ const optionById = matrix.optionById.get(optionToken);
2539
+ if (optionById) {
2540
+ if (assignSelectedValue(
2541
+ matrix,
2542
+ selectedByOptionId,
2543
+ optionById.id,
2544
+ valueToken
2545
+ )) {
2546
+ continue;
2547
+ }
2548
+ const before = selectedByOptionId.get(optionById.id);
2549
+ if (assignSelectedValueSlugByOptionId(
2550
+ matrix,
2551
+ selectedByOptionId,
2552
+ optionById.id,
2553
+ valueToken
2554
+ ) && selectedByOptionId.get(optionById.id) !== before) {
2555
+ emitCompatibilityOptionIdParam(options, {
2556
+ optionId: optionById.id,
2557
+ optionSlug: optionById.slug,
2558
+ valueSlug: valueToken,
2559
+ searchParam: key
2560
+ });
2561
+ continue;
2562
+ }
2563
+ throw new ProductSelectionCodecError(
2564
+ `Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
2565
+ );
2566
+ }
3409
2567
  const optionBySlug = matrix.optionBySlug.get(optionToken);
3410
2568
  if (optionBySlug) {
3411
- assignSelectedValueSlugByOptionSlug(
2569
+ if (assignSelectedValueSlugByOptionSlug(
3412
2570
  matrix,
3413
2571
  selectedByOptionId,
3414
2572
  optionBySlug.slug,
3415
- valueSlug
2573
+ valueToken
2574
+ )) {
2575
+ continue;
2576
+ }
2577
+ if (matrix.valueById.has(valueToken)) {
2578
+ throw new ProductSelectionCodecError(
2579
+ `Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
2580
+ );
2581
+ }
2582
+ throw new ProductSelectionCodecError(
2583
+ `Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionSlug>=<valueSlug> for compatibility URLs.`
3416
2584
  );
3417
- continue;
3418
- }
3419
- const legacyOption = matrix.optionById.get(optionToken);
3420
- if (!legacyOption) continue;
3421
- const before = selectedByOptionId.get(legacyOption.id);
3422
- assignSelectedValueSlugByOptionId(
3423
- matrix,
3424
- selectedByOptionId,
3425
- legacyOption.id,
3426
- valueSlug
3427
- );
3428
- if (selectedByOptionId.get(legacyOption.id) !== before) {
3429
- emitLegacyOptionIdParam(options, {
3430
- optionId: legacyOption.id,
3431
- optionSlug: legacyOption.slug,
3432
- valueSlug,
3433
- searchParam: key
3434
- });
3435
2585
  }
3436
2586
  }
2587
+ return null;
3437
2588
  }
3438
2589
  function normalizeProductSelection(detail, selection = {}, options) {
3439
2590
  const matrix = buildProductOptionMatrixFromDetail(detail);
2591
+ return normalizeProductSelectionFromMatrix(matrix, selection, options);
2592
+ }
2593
+ function normalizeProductSelectionFromMatrix(matrix, selection = {}, options) {
3440
2594
  const selectedByOptionId = /* @__PURE__ */ new Map();
3441
2595
  const variantSelection = getVariantSelection(matrix, selection.variantId);
3442
2596
  const variantId = variantSelection?.id ?? null;
@@ -3452,7 +2606,12 @@ function normalizeProductSelection(detail, selection = {}, options) {
3452
2606
  if (!optionId) continue;
3453
2607
  selectedByOptionId.set(optionId, valueId);
3454
2608
  }
3455
- assignSearchSelection(matrix, selectedByOptionId, selection.search, options);
2609
+ const searchVariantId = assignSearchSelection(
2610
+ matrix,
2611
+ selectedByOptionId,
2612
+ selection.search,
2613
+ options
2614
+ );
3456
2615
  for (const [rawOptionId, rawSelection] of Object.entries(
3457
2616
  selection.byOptionId ?? {}
3458
2617
  )) {
@@ -3529,7 +2688,7 @@ function normalizeProductSelection(detail, selection = {}, options) {
3529
2688
  byOptionSlug,
3530
2689
  byOptionId,
3531
2690
  valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
3532
- variantId
2691
+ variantId: searchVariantId ?? variantId
3533
2692
  };
3534
2693
  }
3535
2694
  function parseProductSelection(detail, search, options) {
@@ -3537,15 +2696,31 @@ function parseProductSelection(detail, search, options) {
3537
2696
  }
3538
2697
  function stringifyProductSelection(detail, selection = {}, options) {
3539
2698
  const matrix = buildProductOptionMatrixFromDetail(detail);
3540
- const normalized = normalizeProductSelection(detail, selection, options);
2699
+ const normalized = normalizeProductSelectionFromMatrix(
2700
+ matrix,
2701
+ selection,
2702
+ options
2703
+ );
3541
2704
  const params = new URLSearchParams();
2705
+ if (hasExplicitSelection(selection)) {
2706
+ const matchingVariants = getMatchingVariantEntries(matrix, normalized);
2707
+ const exactVariant = getExactSelectedVariantEntry(
2708
+ matrix,
2709
+ normalized,
2710
+ matchingVariants
2711
+ );
2712
+ if (exactVariant) {
2713
+ params.set("variant", exactVariant.id);
2714
+ return params.toString();
2715
+ }
2716
+ }
3542
2717
  for (const optionId of matrix.optionIds) {
3543
2718
  const valueId = normalized.byOptionId[optionId];
3544
2719
  if (!valueId) continue;
3545
- const option = matrix.optionById.get(optionId);
3546
- const value = matrix.valueById.get(valueId);
3547
- if (!option || !value) continue;
3548
- params.append(`opt.${requireOptionSlug(option)}`, requireValueSlug(value));
2720
+ if (!matrix.optionById.has(optionId) || !matrix.valueById.has(valueId)) {
2721
+ continue;
2722
+ }
2723
+ params.append(`opt.${optionId}`, valueId);
3549
2724
  }
3550
2725
  return params.toString();
3551
2726
  }
@@ -3610,17 +2785,21 @@ function mediaArray(values) {
3610
2785
  return values.filter(isPresentMedia);
3611
2786
  }
3612
2787
  function buildSelectionMedia(detail, selectedVariant, matchingVariants, selectedValues) {
2788
+ const selectedVariantImages = mediaArray(selectedVariant?.images);
3613
2789
  const selectedValueImages = selectedValues.flatMap(
3614
2790
  (value) => mediaArray(value.images)
3615
2791
  );
2792
+ const matchingVariantImages = matchingVariants.flatMap(
2793
+ (variant) => mediaArray(variant.images)
2794
+ );
3616
2795
  const selectedValuePrimary = selectedValues.map((value) => firstMedia(value.thumbnail) ?? firstMedia(value.images)).find((value) => value != null) ?? null;
3617
2796
  const selectedVariantPrimary = firstMedia(selectedVariant?.thumbnail) ?? firstMedia(selectedVariant?.images);
3618
2797
  const matchingVariantPrimary = matchingVariants.map(
3619
2798
  (variant) => firstMedia(variant.thumbnail) ?? firstMedia(variant.images)
3620
2799
  ).find((value) => value != null) ?? null;
3621
2800
  const detailImages = mediaArray(detail.images);
3622
- const primaryImage = selectedVariantPrimary ?? selectedValuePrimary ?? firstMedia(detail.listing.primaryImage) ?? matchingVariantPrimary ?? firstMedia(detailImages);
3623
- const images = mediaArray(selectedVariant?.images).length > 0 ? mediaArray(selectedVariant?.images) : selectedValueImages.length > 0 ? selectedValueImages : detailImages;
2801
+ const primaryImage = selectedVariantPrimary ?? selectedValuePrimary ?? matchingVariantPrimary ?? firstMedia(detail.listing.primaryImage) ?? firstMedia(detailImages);
2802
+ const images = selectedVariantImages.length > 0 ? selectedVariantImages : selectedValueImages.length > 0 ? selectedValueImages : matchingVariantImages.length > 0 ? matchingVariantImages : detailImages;
3624
2803
  return {
3625
2804
  primaryImage,
3626
2805
  images
@@ -3646,11 +2825,39 @@ function buildSelectionStock(selectedVariant, matchingVariants) {
3646
2825
  availableStock: null
3647
2826
  };
3648
2827
  }
3649
- function resolveProductSelection(detail, selection = {}, options) {
3650
- const matrix = buildProductOptionMatrixFromDetail(detail);
3651
- const effectiveSelection = hasExplicitSelection(selection) || detail.listing.selectionHintVariant == null ? selection : { ...selection, variantId: detail.listing.selectionHintVariant };
3652
- const normalizedSelection = normalizeProductSelection(
3653
- detail,
2828
+ function buildAvailableValueStock(variants) {
2829
+ const activeVariants = variants.filter((variant) => variant.isActive !== false);
2830
+ const isUnlimited = activeVariants.some((variant) => variant.isUnlimited);
2831
+ const availableStock = isUnlimited ? null : activeVariants.reduce(
2832
+ (sum, variant) => sum + Math.max(0, variant.stock - variant.reservedStock),
2833
+ 0
2834
+ );
2835
+ return {
2836
+ availableForSale: activeVariants.some(isVariantAvailableForSale),
2837
+ isUnlimited,
2838
+ availableStock
2839
+ };
2840
+ }
2841
+ function getCandidateVariantsForValue(matrix, optionId, valueId, selectedValueIds) {
2842
+ const selectedByOption = getSelectedValueByOptionId(matrix, selectedValueIds);
2843
+ selectedByOption.set(optionId, valueId);
2844
+ return matrix.variants.filter(
2845
+ (variant) => Array.from(selectedByOption.entries()).every(
2846
+ ([selectedOptionId, selectedValueId]) => variant.optionValueByOptionId.get(selectedOptionId) === selectedValueId
2847
+ )
2848
+ ).map((variant) => variant.source);
2849
+ }
2850
+ function getResolutionContext(context) {
2851
+ return {
2852
+ images: context?.images ?? context?.detail?.images ?? [],
2853
+ listing: context?.listing ?? context?.detail?.listing ?? {}
2854
+ };
2855
+ }
2856
+ function resolveProductSelectionFromMatrix(matrix, selection = {}, options, context) {
2857
+ const { images, listing } = getResolutionContext(context);
2858
+ const effectiveSelection = hasExplicitSelection(selection) || listing.selectionHintVariant == null ? selection : { ...selection, variantId: listing.selectionHintVariant };
2859
+ const normalizedSelection = normalizeProductSelectionFromMatrix(
2860
+ matrix,
3654
2861
  effectiveSelection,
3655
2862
  options
3656
2863
  );
@@ -3685,9 +2892,17 @@ function resolveProductSelection(detail, selection = {}, options) {
3685
2892
  option.values.map((value) => ({
3686
2893
  valueId: value.id,
3687
2894
  value: value.label,
3688
- slug: requireValueSlug(value),
2895
+ slug: value.slug ?? "",
3689
2896
  selected: normalizedSelection.byOptionId[option.id] === value.id,
3690
2897
  available: availableValueIds.has(value.id),
2898
+ ...buildAvailableValueStock(
2899
+ getCandidateVariantsForValue(
2900
+ matrix,
2901
+ option.id,
2902
+ value.id,
2903
+ normalizedSelection.valueIds
2904
+ )
2905
+ ),
3691
2906
  swatchColor: value.swatchColor ?? null,
3692
2907
  thumbnail: value.thumbnail ?? null,
3693
2908
  images: value.images ?? null
@@ -3715,7 +2930,12 @@ function resolveProductSelection(detail, selection = {}, options) {
3715
2930
  allOptionsSelected,
3716
2931
  price: buildSelectionPrice(priceVariants),
3717
2932
  media: buildSelectionMedia(
3718
- detail,
2933
+ {
2934
+ images,
2935
+ listing: {
2936
+ primaryImage: listing.primaryImage ?? null
2937
+ }
2938
+ },
3719
2939
  selectedVariant,
3720
2940
  matchingVariants,
3721
2941
  selectedValues
@@ -3723,6 +2943,100 @@ function resolveProductSelection(detail, selection = {}, options) {
3723
2943
  stock: buildSelectionStock(selectedVariant, matchingVariants)
3724
2944
  };
3725
2945
  }
2946
+ function resolveProductSelection(detail, selection = {}, options) {
2947
+ const matrix = buildProductOptionMatrixFromDetail(detail);
2948
+ return resolveProductSelectionFromMatrix(matrix, selection, options, {
2949
+ detail
2950
+ });
2951
+ }
2952
+ function isProductDetailImageMedia(value) {
2953
+ return typeof value === "object" && value !== null;
2954
+ }
2955
+ function mediaDedupeKey(value) {
2956
+ if (value.id != null) return `id:${String(value.id)}`;
2957
+ if (value.url) return `url:${value.url}`;
2958
+ return null;
2959
+ }
2960
+ function getProductSelectionImages(resolution) {
2961
+ const seen = /* @__PURE__ */ new Set();
2962
+ const images = [];
2963
+ const candidates = [
2964
+ resolution.media.primaryImage,
2965
+ ...resolution.media.images
2966
+ ].filter(isProductDetailImageMedia);
2967
+ for (const candidate of candidates) {
2968
+ const key = mediaDedupeKey(candidate);
2969
+ if (key && seen.has(key)) continue;
2970
+ if (key) seen.add(key);
2971
+ images.push(candidate);
2972
+ }
2973
+ return images;
2974
+ }
2975
+ function getProductHrefSlug(product) {
2976
+ if ("product" in product && product.product?.slug) {
2977
+ return product.product.slug;
2978
+ }
2979
+ if ("slug" in product && product.slug) return product.slug;
2980
+ throw new ProductSelectionCodecError(
2981
+ "Product slug is required to build a product href."
2982
+ );
2983
+ }
2984
+ function joinProductPath(basePath, slug, trailingSlash) {
2985
+ const base = basePath.replace(/\/+$/, "");
2986
+ const encodedSlug = encodeURIComponent(slug);
2987
+ return `${base}/${encodedSlug}${trailingSlash ? "/" : ""}`;
2988
+ }
2989
+ function getProductHrefGroupSelection(group, matrix) {
2990
+ if (!group) return null;
2991
+ if (group.variantId != null) return { variantId: group.variantId };
2992
+ const listingVariantId = group.listing?.selectionHintVariant;
2993
+ const optionId = group.optionId != null ? String(group.optionId) : group.optionSlug ? matrix?.optionBySlug.get(group.optionSlug)?.id : void 0;
2994
+ if (!optionId) {
2995
+ return listingVariantId != null ? { variantId: listingVariantId } : null;
2996
+ }
2997
+ const option = matrix?.optionById.get(optionId);
2998
+ const optionValueId = group.optionValueId != null ? String(group.optionValueId) : group.optionValueSlug && option ? option.values.find((value) => value.slug === group.optionValueSlug)?.id : void 0;
2999
+ if (!optionValueId) {
3000
+ return listingVariantId != null ? { variantId: listingVariantId } : null;
3001
+ }
3002
+ return { byOptionId: { [optionId]: optionValueId } };
3003
+ }
3004
+ function buildProductHref(product, group, options = {}) {
3005
+ const path = joinProductPath(
3006
+ options.basePath ?? "/products",
3007
+ getProductHrefSlug(product),
3008
+ options.trailingSlash ?? false
3009
+ );
3010
+ const params = new URLSearchParams();
3011
+ if (options.detail && options.selection) {
3012
+ const selection = stringifyProductSelection(options.detail, options.selection);
3013
+ return selection ? `${path}?${selection}` : path;
3014
+ }
3015
+ const groupSelection = getProductHrefGroupSelection(group, options.matrix);
3016
+ if (groupSelection) {
3017
+ if (options.detail) {
3018
+ const selection = stringifyProductSelection(options.detail, groupSelection);
3019
+ return selection ? `${path}?${selection}` : path;
3020
+ }
3021
+ if (groupSelection.variantId != null) {
3022
+ params.set("variant", String(groupSelection.variantId));
3023
+ return `${path}?${params.toString()}`;
3024
+ }
3025
+ const [selectionEntry] = Object.entries(groupSelection.byOptionId ?? {});
3026
+ if (selectionEntry) {
3027
+ const [optionId, valueId] = selectionEntry;
3028
+ params.set(`opt.${optionId}`, String(valueId));
3029
+ return `${path}?${params.toString()}`;
3030
+ }
3031
+ }
3032
+ if (group?.optionValueSlug) {
3033
+ const optionSlug = group.optionSlug ?? (group.optionId != null ? options.matrix?.optionById.get(String(group.optionId))?.slug : void 0);
3034
+ if (optionSlug) {
3035
+ params.set(`opt.${optionSlug}`, group.optionValueSlug);
3036
+ }
3037
+ }
3038
+ return params.size > 0 ? `${path}?${params.toString()}` : path;
3039
+ }
3726
3040
  function compareVariantOrder(a, b) {
3727
3041
  const aOrder = Number(a._order ?? Number.MAX_SAFE_INTEGER);
3728
3042
  const bOrder = Number(b._order ?? Number.MAX_SAFE_INTEGER);
@@ -3776,6 +3090,72 @@ function buildProductListingProjection(product, variants) {
3776
3090
  availableForSale: availableVariants.length > 0
3777
3091
  };
3778
3092
  }
3093
+ function buildProductListingCard(item, options = {}) {
3094
+ const product = item.product;
3095
+ const groups = item.groups;
3096
+ const primaryImage = firstMedia(product.thumbnail) ?? firstMedia(product.images ?? null) ?? null;
3097
+ const priceRange = aggregateListingPriceRange(groups);
3098
+ const availableForSale = groups.some(
3099
+ (group) => group.listing.availableForSale
3100
+ );
3101
+ const swatches = groups.length > 1 ? groups.map((group) => buildListingSwatch(product, group, options)) : [];
3102
+ return {
3103
+ id: String(product.id),
3104
+ href: buildProductHref({ slug: product.slug }, void 0, options),
3105
+ title: product.title,
3106
+ primaryImage,
3107
+ priceRange,
3108
+ availableForSale,
3109
+ swatches
3110
+ };
3111
+ }
3112
+ function aggregateListingPriceRange(groups) {
3113
+ const minPrice = minOfNullable(groups.map((g) => g.listing.minPrice));
3114
+ const maxPrice = maxOfNullable(groups.map((g) => g.listing.maxPrice));
3115
+ const minCompareAtPrice = minOfNullable(
3116
+ groups.map((g) => g.listing.minCompareAtPrice)
3117
+ );
3118
+ const maxCompareAtPrice = maxOfNullable(
3119
+ groups.map((g) => g.listing.maxCompareAtPrice)
3120
+ );
3121
+ const isPriceRange = minPrice !== null && maxPrice !== null && minPrice !== maxPrice;
3122
+ return {
3123
+ minPrice,
3124
+ maxPrice,
3125
+ minCompareAtPrice,
3126
+ maxCompareAtPrice,
3127
+ isPriceRange
3128
+ };
3129
+ }
3130
+ function buildListingSwatch(product, group, options) {
3131
+ const thumbnail = firstMedia(group.optionValueThumbnail) ?? firstMedia(group.optionValueImages) ?? null;
3132
+ return {
3133
+ optionId: group.optionId,
3134
+ optionValueId: group.optionValueId,
3135
+ label: group.optionValueLabel,
3136
+ swatchColor: group.optionValueSwatchColor ?? null,
3137
+ thumbnail,
3138
+ href: buildProductHref(
3139
+ { slug: product.slug },
3140
+ {
3141
+ optionId: group.optionId,
3142
+ optionValueId: group.optionValueId,
3143
+ optionValueSlug: group.optionValueSlug,
3144
+ listing: group.listing
3145
+ },
3146
+ options
3147
+ ),
3148
+ availableForSale: group.listing.availableForSale
3149
+ };
3150
+ }
3151
+ function minOfNullable(values) {
3152
+ const numbers = values.filter((v) => v !== null);
3153
+ return numbers.length === 0 ? null : Math.min(...numbers);
3154
+ }
3155
+ function maxOfNullable(values) {
3156
+ const numbers = values.filter((v) => v !== null);
3157
+ return numbers.length === 0 ? null : Math.max(...numbers);
3158
+ }
3779
3159
  function buildProductListingGroupsByOption(args) {
3780
3160
  const primaryOptionId = args.primaryOptionId ?? void 0;
3781
3161
  if (!primaryOptionId) return [];
@@ -4102,6 +3482,11 @@ function createAnalytics(config) {
4102
3482
  }
4103
3483
  };
4104
3484
  }
3485
+
3486
+ // src/index.ts
3487
+ function createClient2(options) {
3488
+ return createClient(options);
3489
+ }
4105
3490
  export {
4106
3491
  ApiError,
4107
3492
  AuthError,
@@ -4109,61 +3494,48 @@ export {
4109
3494
  COLLECTIONS,
4110
3495
  CUSTOMER_PASSWORD_RESET_OPERATION,
4111
3496
  CartApi,
4112
- Client,
4113
- CollectionClient,
4114
- CollectionHooks,
4115
- CollectionQueryBuilder,
4116
3497
  CommerceClient,
4117
3498
  CommunityClient,
4118
3499
  ConfigError,
4119
3500
  ConflictError,
4120
3501
  CustomerAuth,
4121
- CustomerHooks,
4122
3502
  CustomerNamespace,
4123
3503
  DiscountApi,
4124
3504
  GoneError,
4125
3505
  IMAGE_SIZES,
4126
3506
  INTERNAL_COLLECTIONS,
4127
- ModerationApi,
4128
3507
  NetworkError,
4129
3508
  NotFoundError,
4130
3509
  OrderApi,
4131
3510
  PermissionError,
4132
3511
  ProductApi,
4133
3512
  ProductSelectionCodecError,
4134
- QueryHooks,
4135
3513
  RateLimitError,
4136
- ReadOnlyCollectionClient,
4137
3514
  RealtimeConnection,
4138
3515
  SDKError,
4139
3516
  SERVER_COLLECTIONS,
4140
3517
  SERVER_ONLY_COLLECTIONS,
4141
- ServerClient,
4142
- ServerCollectionClient,
4143
- ServerCollectionQueryBuilder,
4144
- ServerCommerceClient,
4145
3518
  ServiceUnavailableError,
4146
3519
  ShippingApi,
4147
3520
  TimeoutError,
4148
3521
  UsageLimitError,
4149
3522
  ValidationError,
3523
+ buildProductHref,
3524
+ buildProductListingCard,
4150
3525
  buildProductListingGroupsByOption,
4151
3526
  buildProductListingProjection,
4152
3527
  buildProductOptionMatrix,
4153
3528
  buildProductOptionMatrixFromDetail,
4154
- collectionKeys,
4155
3529
  createAnalytics,
4156
3530
  createAuthError,
4157
- createClient,
3531
+ createClient2 as createClient,
4158
3532
  createConflictError,
4159
3533
  createCustomerAuthWebhookHandler,
4160
3534
  createNotFoundError,
4161
3535
  createPermissionError,
4162
3536
  createProductSelectionCodec,
4163
3537
  createRateLimitError,
4164
- createServerClient,
4165
3538
  createTypedWebhookHandler,
4166
- customerKeys,
4167
3539
  formatOrderName,
4168
3540
  generateOrderNumber,
4169
3541
  getAvailableOptionValues,
@@ -4172,7 +3544,7 @@ export {
4172
3544
  getImagePlaceholderStyle,
4173
3545
  getImageSrcSet,
4174
3546
  getImageUrl,
4175
- getQueryClient,
3547
+ getProductSelectionImages,
4176
3548
  getSelectedValueByOptionId,
4177
3549
  getVideoGif,
4178
3550
  getVideoMp4Url,
@@ -4197,10 +3569,11 @@ export {
4197
3569
  isValidWebhookEvent,
4198
3570
  isValidationError,
4199
3571
  normalizeProductSelection,
3572
+ normalizeProductSelectionFromMatrix,
4200
3573
  normalizeSelectedValueIds,
4201
3574
  parseProductSelection,
4202
- productKeys,
4203
3575
  resolveProductSelection,
3576
+ resolveProductSelectionFromMatrix,
4204
3577
  resolveRelation,
4205
3578
  resolveVariantForSelection,
4206
3579
  stringifyProductSelection