@01.software/sdk 0.28.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +273 -73
- package/dist/analytics/react.cjs +4 -1
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js +4 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs +4 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js +4 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +1476 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +28 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +1453 -0
- package/dist/client.js.map +1 -0
- package/dist/collection-client-B9d9kr1d.d.ts +218 -0
- package/dist/collection-client-QPbwimkU.d.cts +218 -0
- package/dist/{const-mdQQtIOz.d.ts → const-B75IFDRi.d.ts} +2 -4
- package/dist/{const-Cz9Ki_I7.d.cts → const-VZuk2tWc.d.cts} +2 -4
- package/dist/index-B2WbhEgT.d.cts +106 -0
- package/dist/index-B2WbhEgT.d.ts +106 -0
- package/dist/index.cjs +1291 -1501
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -115
- package/dist/index.d.ts +11 -115
- package/dist/index.js +1292 -1520
- package/dist/index.js.map +1 -1
- package/dist/metadata.cjs +91 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +58 -0
- package/dist/metadata.d.ts +58 -0
- package/dist/metadata.js +68 -0
- package/dist/metadata.js.map +1 -0
- package/dist/{payload-types-BrSYb-sh.d.cts → payload-types-DPjO_IbQ.d.cts} +17 -6
- package/dist/{payload-types-BrSYb-sh.d.ts → payload-types-DPjO_IbQ.d.ts} +17 -6
- package/dist/query.cjs +1791 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +244 -0
- package/dist/query.d.ts +244 -0
- package/dist/query.js +1786 -0
- package/dist/query.js.map +1 -0
- package/dist/realtime.cjs +4 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +4 -1
- package/dist/realtime.js.map +1 -1
- package/dist/{server-BINWywT8.d.cts → server-CrsPyqEc.d.cts} +14 -31
- package/dist/{server-BINWywT8.d.ts → server-CrsPyqEc.d.ts} +14 -31
- package/dist/server.cjs +300 -844
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +112 -7
- package/dist/server.d.ts +112 -7
- package/dist/server.js +300 -862
- package/dist/server.js.map +1 -1
- package/dist/{types-BLUb4cYq.d.ts → types-1fBLrYU7.d.ts} +1 -1
- package/dist/{types-CW4PaIL7.d.cts → types-BwT0eeaz.d.cts} +1 -1
- package/dist/types-Dlb2mwpX.d.cts +1249 -0
- package/dist/types-DuSKPiY5.d.ts +1249 -0
- package/dist/ui/canvas/server.cjs +7 -6
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +1 -3
- package/dist/ui/canvas/server.d.ts +1 -3
- package/dist/ui/canvas/server.js +7 -6
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +11 -10
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +29 -6
- package/dist/ui/canvas.d.ts +29 -6
- package/dist/ui/canvas.js +11 -10
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +82 -13
- package/dist/server-C2Q9R-Lu.d.ts +0 -1662
- package/dist/server-D369bCVJ.d.cts +0 -1662
package/dist/index.cjs
CHANGED
|
@@ -26,58 +26,47 @@ __export(src_exports, {
|
|
|
26
26
|
COLLECTIONS: () => COLLECTIONS,
|
|
27
27
|
CUSTOMER_PASSWORD_RESET_OPERATION: () => CUSTOMER_PASSWORD_RESET_OPERATION,
|
|
28
28
|
CartApi: () => CartApi,
|
|
29
|
-
Client: () => Client,
|
|
30
|
-
CollectionClient: () => CollectionClient,
|
|
31
|
-
CollectionHooks: () => CollectionHooks,
|
|
32
|
-
CollectionQueryBuilder: () => CollectionQueryBuilder,
|
|
33
29
|
CommerceClient: () => CommerceClient,
|
|
34
30
|
CommunityClient: () => CommunityClient,
|
|
35
31
|
ConfigError: () => ConfigError,
|
|
36
32
|
ConflictError: () => ConflictError,
|
|
37
33
|
CustomerAuth: () => CustomerAuth,
|
|
38
|
-
CustomerHooks: () => CustomerHooks,
|
|
39
34
|
CustomerNamespace: () => CustomerNamespace,
|
|
40
35
|
DiscountApi: () => DiscountApi,
|
|
41
36
|
GoneError: () => GoneError,
|
|
42
37
|
IMAGE_SIZES: () => IMAGE_SIZES,
|
|
43
38
|
INTERNAL_COLLECTIONS: () => INTERNAL_COLLECTIONS,
|
|
44
|
-
ModerationApi: () => ModerationApi,
|
|
45
39
|
NetworkError: () => NetworkError,
|
|
46
40
|
NotFoundError: () => NotFoundError,
|
|
47
41
|
OrderApi: () => OrderApi,
|
|
48
42
|
PermissionError: () => PermissionError,
|
|
49
43
|
ProductApi: () => ProductApi,
|
|
50
|
-
|
|
44
|
+
ProductSelectionCodecError: () => ProductSelectionCodecError,
|
|
51
45
|
RateLimitError: () => RateLimitError,
|
|
52
|
-
ReadOnlyCollectionClient: () => ReadOnlyCollectionClient,
|
|
53
46
|
RealtimeConnection: () => RealtimeConnection,
|
|
54
47
|
SDKError: () => SDKError,
|
|
55
48
|
SERVER_COLLECTIONS: () => SERVER_COLLECTIONS,
|
|
56
49
|
SERVER_ONLY_COLLECTIONS: () => SERVER_ONLY_COLLECTIONS,
|
|
57
|
-
ServerClient: () => ServerClient,
|
|
58
|
-
ServerCollectionClient: () => ServerCollectionClient,
|
|
59
|
-
ServerCollectionQueryBuilder: () => ServerCollectionQueryBuilder,
|
|
60
|
-
ServerCommerceClient: () => ServerCommerceClient,
|
|
61
50
|
ServiceUnavailableError: () => ServiceUnavailableError,
|
|
62
51
|
ShippingApi: () => ShippingApi,
|
|
63
52
|
TimeoutError: () => TimeoutError,
|
|
64
53
|
UsageLimitError: () => UsageLimitError,
|
|
65
54
|
ValidationError: () => ValidationError,
|
|
55
|
+
buildProductHref: () => buildProductHref,
|
|
66
56
|
buildProductListingGroupsByOption: () => buildProductListingGroupsByOption,
|
|
67
57
|
buildProductListingProjection: () => buildProductListingProjection,
|
|
68
58
|
buildProductOptionMatrix: () => buildProductOptionMatrix,
|
|
69
|
-
|
|
59
|
+
buildProductOptionMatrixFromDetail: () => buildProductOptionMatrixFromDetail,
|
|
70
60
|
createAnalytics: () => createAnalytics,
|
|
71
61
|
createAuthError: () => createAuthError,
|
|
72
|
-
createClient: () =>
|
|
62
|
+
createClient: () => createClient2,
|
|
73
63
|
createConflictError: () => createConflictError,
|
|
74
64
|
createCustomerAuthWebhookHandler: () => createCustomerAuthWebhookHandler,
|
|
75
65
|
createNotFoundError: () => createNotFoundError,
|
|
76
66
|
createPermissionError: () => createPermissionError,
|
|
67
|
+
createProductSelectionCodec: () => createProductSelectionCodec,
|
|
77
68
|
createRateLimitError: () => createRateLimitError,
|
|
78
|
-
createServerClient: () => createServerClient,
|
|
79
69
|
createTypedWebhookHandler: () => createTypedWebhookHandler,
|
|
80
|
-
customerKeys: () => customerKeys,
|
|
81
70
|
formatOrderName: () => formatOrderName,
|
|
82
71
|
generateOrderNumber: () => generateOrderNumber,
|
|
83
72
|
getAvailableOptionValues: () => getAvailableOptionValues,
|
|
@@ -86,7 +75,7 @@ __export(src_exports, {
|
|
|
86
75
|
getImagePlaceholderStyle: () => getImagePlaceholderStyle,
|
|
87
76
|
getImageSrcSet: () => getImageSrcSet,
|
|
88
77
|
getImageUrl: () => getImageUrl,
|
|
89
|
-
|
|
78
|
+
getProductSelectionImages: () => getProductSelectionImages,
|
|
90
79
|
getSelectedValueByOptionId: () => getSelectedValueByOptionId,
|
|
91
80
|
getVideoGif: () => getVideoGif,
|
|
92
81
|
getVideoMp4Url: () => getVideoMp4Url,
|
|
@@ -110,252 +99,18 @@ __export(src_exports, {
|
|
|
110
99
|
isUsageLimitError: () => isUsageLimitError,
|
|
111
100
|
isValidWebhookEvent: () => isValidWebhookEvent,
|
|
112
101
|
isValidationError: () => isValidationError,
|
|
102
|
+
normalizeProductSelection: () => normalizeProductSelection,
|
|
103
|
+
normalizeProductSelectionFromMatrix: () => normalizeProductSelectionFromMatrix,
|
|
113
104
|
normalizeSelectedValueIds: () => normalizeSelectedValueIds,
|
|
114
|
-
|
|
105
|
+
parseProductSelection: () => parseProductSelection,
|
|
106
|
+
resolveProductSelection: () => resolveProductSelection,
|
|
107
|
+
resolveProductSelectionFromMatrix: () => resolveProductSelectionFromMatrix,
|
|
115
108
|
resolveRelation: () => resolveRelation,
|
|
116
|
-
resolveVariantForSelection: () => resolveVariantForSelection
|
|
109
|
+
resolveVariantForSelection: () => resolveVariantForSelection,
|
|
110
|
+
stringifyProductSelection: () => stringifyProductSelection
|
|
117
111
|
});
|
|
118
112
|
module.exports = __toCommonJS(src_exports);
|
|
119
113
|
|
|
120
|
-
// src/utils/types.ts
|
|
121
|
-
var resolveRelation = (ref) => {
|
|
122
|
-
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
123
|
-
return null;
|
|
124
|
-
return ref;
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
// src/core/metadata/index.ts
|
|
128
|
-
function extractSeo(doc) {
|
|
129
|
-
const seo = doc.seo ?? {};
|
|
130
|
-
const og = seo.openGraph ?? {};
|
|
131
|
-
return {
|
|
132
|
-
title: seo.title ?? doc.title ?? null,
|
|
133
|
-
description: seo.description ?? null,
|
|
134
|
-
noIndex: seo.noIndex ?? null,
|
|
135
|
-
canonical: seo.canonical ?? null,
|
|
136
|
-
openGraph: {
|
|
137
|
-
title: og.title ?? null,
|
|
138
|
-
description: og.description ?? null,
|
|
139
|
-
image: og.image ?? null
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function generateMetadata(input, options) {
|
|
144
|
-
const title = input.title ?? void 0;
|
|
145
|
-
const description = input.description ?? void 0;
|
|
146
|
-
const ogTitle = input.openGraph?.title ?? title;
|
|
147
|
-
const ogDescription = input.openGraph?.description ?? description;
|
|
148
|
-
const image = resolveMetaImage(input.openGraph?.image);
|
|
149
|
-
return {
|
|
150
|
-
title,
|
|
151
|
-
description,
|
|
152
|
-
...input.noIndex && { robots: { index: false, follow: false } },
|
|
153
|
-
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
154
|
-
openGraph: {
|
|
155
|
-
...ogTitle && { title: ogTitle },
|
|
156
|
-
...ogDescription && { description: ogDescription },
|
|
157
|
-
...options?.siteName && { siteName: options.siteName },
|
|
158
|
-
...image && { images: [image] }
|
|
159
|
-
},
|
|
160
|
-
twitter: {
|
|
161
|
-
card: image ? "summary_large_image" : "summary",
|
|
162
|
-
...ogTitle && { title: ogTitle },
|
|
163
|
-
...ogDescription && { description: ogDescription },
|
|
164
|
-
...image && { images: [image.url] }
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
function resolveMetaImage(ref) {
|
|
169
|
-
const image = resolveRelation(ref);
|
|
170
|
-
if (!image) return null;
|
|
171
|
-
const sized = image.sizes?.["1536"];
|
|
172
|
-
const url = sized?.url || image.url;
|
|
173
|
-
if (!url) return null;
|
|
174
|
-
const width = sized?.url ? sized.width : image.width;
|
|
175
|
-
const height = sized?.url ? sized.height : image.height;
|
|
176
|
-
return {
|
|
177
|
-
url,
|
|
178
|
-
...width && { width },
|
|
179
|
-
...height && { height },
|
|
180
|
-
...image.alt && { alt: image.alt }
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// src/core/collection/query-builder.ts
|
|
185
|
-
var ReadOnlyCollectionQueryBuilder = class {
|
|
186
|
-
constructor(api, collection) {
|
|
187
|
-
this.api = api;
|
|
188
|
-
this.collection = collection;
|
|
189
|
-
}
|
|
190
|
-
async find(options) {
|
|
191
|
-
return this.api.requestFind(
|
|
192
|
-
`/api/${String(this.collection)}`,
|
|
193
|
-
options
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
async findById(id, options) {
|
|
197
|
-
return this.api.requestFindById(
|
|
198
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
199
|
-
options
|
|
200
|
-
);
|
|
201
|
-
}
|
|
202
|
-
async count(options) {
|
|
203
|
-
return this.api.requestCount(
|
|
204
|
-
`/api/${String(this.collection)}/count`,
|
|
205
|
-
options
|
|
206
|
-
);
|
|
207
|
-
}
|
|
208
|
-
async findMetadata(options, metadataOptions) {
|
|
209
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
210
|
-
const doc = docs[0];
|
|
211
|
-
if (!doc) return null;
|
|
212
|
-
return generateMetadata(
|
|
213
|
-
extractSeo(doc),
|
|
214
|
-
metadataOptions
|
|
215
|
-
);
|
|
216
|
-
}
|
|
217
|
-
async findMetadataById(id, metadataOptions) {
|
|
218
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
219
|
-
return generateMetadata(
|
|
220
|
-
extractSeo(doc),
|
|
221
|
-
metadataOptions
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
var CollectionQueryBuilder = class {
|
|
226
|
-
constructor(api, collection) {
|
|
227
|
-
this.api = api;
|
|
228
|
-
this.collection = collection;
|
|
229
|
-
}
|
|
230
|
-
/**
|
|
231
|
-
* Find documents (list query)
|
|
232
|
-
* GET /api/{collection}
|
|
233
|
-
* @returns Payload CMS find response with docs array and pagination
|
|
234
|
-
*/
|
|
235
|
-
async find(options) {
|
|
236
|
-
return this.api.requestFind(
|
|
237
|
-
`/api/${String(this.collection)}`,
|
|
238
|
-
options
|
|
239
|
-
);
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Find document by ID
|
|
243
|
-
* GET /api/{collection}/{id}
|
|
244
|
-
* @returns Document object directly (no wrapper)
|
|
245
|
-
*/
|
|
246
|
-
async findById(id, options) {
|
|
247
|
-
return this.api.requestFindById(
|
|
248
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
249
|
-
options
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
/**
|
|
253
|
-
* Create a new document
|
|
254
|
-
* POST /api/{collection}
|
|
255
|
-
* @returns Payload CMS mutation response with doc and message
|
|
256
|
-
*/
|
|
257
|
-
async create(data, options) {
|
|
258
|
-
const endpoint = `/api/${String(this.collection)}`;
|
|
259
|
-
if (options?.file) {
|
|
260
|
-
return this.api.requestCreateWithFile(
|
|
261
|
-
endpoint,
|
|
262
|
-
data,
|
|
263
|
-
options.file,
|
|
264
|
-
options.filename
|
|
265
|
-
);
|
|
266
|
-
}
|
|
267
|
-
return this.api.requestCreate(endpoint, data);
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Update a document by ID
|
|
271
|
-
* PATCH /api/{collection}/{id}
|
|
272
|
-
* @returns Payload CMS mutation response with doc and message
|
|
273
|
-
*/
|
|
274
|
-
async update(id, data, options) {
|
|
275
|
-
const endpoint = `/api/${String(this.collection)}/${String(id)}`;
|
|
276
|
-
if (options?.file) {
|
|
277
|
-
return this.api.requestUpdateWithFile(
|
|
278
|
-
endpoint,
|
|
279
|
-
data,
|
|
280
|
-
options.file,
|
|
281
|
-
options.filename
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
return this.api.requestUpdate(endpoint, data);
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Count documents
|
|
288
|
-
* GET /api/{collection}/count
|
|
289
|
-
* @returns Count response with totalDocs
|
|
290
|
-
*/
|
|
291
|
-
async count(options) {
|
|
292
|
-
return this.api.requestCount(
|
|
293
|
-
`/api/${String(this.collection)}/count`,
|
|
294
|
-
options
|
|
295
|
-
);
|
|
296
|
-
}
|
|
297
|
-
/**
|
|
298
|
-
* Find first matching document and return its Next.js Metadata.
|
|
299
|
-
* Applies depth: 1 (SEO image populate) and limit: 1 automatically.
|
|
300
|
-
* @returns Metadata or null if no document matches
|
|
301
|
-
*/
|
|
302
|
-
async findMetadata(options, metadataOptions) {
|
|
303
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
304
|
-
const doc = docs[0];
|
|
305
|
-
if (!doc) return null;
|
|
306
|
-
return generateMetadata(
|
|
307
|
-
extractSeo(doc),
|
|
308
|
-
metadataOptions
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
/**
|
|
312
|
-
* Find document by ID and return its Next.js Metadata.
|
|
313
|
-
* Applies depth: 1 (SEO image populate) automatically.
|
|
314
|
-
* @returns Metadata (throws on 404)
|
|
315
|
-
*/
|
|
316
|
-
async findMetadataById(id, metadataOptions) {
|
|
317
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
318
|
-
return generateMetadata(
|
|
319
|
-
extractSeo(doc),
|
|
320
|
-
metadataOptions
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Update multiple documents (bulk update)
|
|
325
|
-
* PATCH /api/{collection}
|
|
326
|
-
* @returns Payload CMS find response with updated docs
|
|
327
|
-
*/
|
|
328
|
-
async updateMany(where, data) {
|
|
329
|
-
return this.api.requestUpdateMany(
|
|
330
|
-
`/api/${String(this.collection)}`,
|
|
331
|
-
{ where, data }
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
/**
|
|
335
|
-
* Delete a document by ID
|
|
336
|
-
* DELETE /api/{collection}/{id}
|
|
337
|
-
* @returns Deleted document object directly (no wrapper)
|
|
338
|
-
*/
|
|
339
|
-
async remove(id) {
|
|
340
|
-
return this.api.requestDelete(
|
|
341
|
-
`/api/${String(this.collection)}/${String(id)}`
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Delete multiple documents (bulk delete)
|
|
346
|
-
* DELETE /api/{collection}
|
|
347
|
-
* @returns Payload CMS find response with deleted docs
|
|
348
|
-
*/
|
|
349
|
-
async removeMany(where) {
|
|
350
|
-
return this.api.requestDeleteMany(
|
|
351
|
-
`/api/${String(this.collection)}`,
|
|
352
|
-
{ where }
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
var ServerCollectionQueryBuilder = class extends CollectionQueryBuilder {
|
|
357
|
-
};
|
|
358
|
-
|
|
359
114
|
// src/core/collection/http-client.ts
|
|
360
115
|
var import_qs_esm = require("qs-esm");
|
|
361
116
|
|
|
@@ -549,7 +304,10 @@ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
|
|
|
549
304
|
}
|
|
550
305
|
|
|
551
306
|
// src/core/client/types.ts
|
|
552
|
-
function resolveApiUrl() {
|
|
307
|
+
function resolveApiUrl(apiUrl) {
|
|
308
|
+
if (apiUrl) {
|
|
309
|
+
return apiUrl.replace(/\/$/, "");
|
|
310
|
+
}
|
|
553
311
|
if (typeof process !== "undefined" && process.env) {
|
|
554
312
|
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
555
313
|
if (envUrl) {
|
|
@@ -730,6 +488,7 @@ function createHttpStatusError(status, parsed, details, requestId) {
|
|
|
730
488
|
}
|
|
731
489
|
async function httpFetch(url, options) {
|
|
732
490
|
const {
|
|
491
|
+
apiUrl,
|
|
733
492
|
publishableKey,
|
|
734
493
|
secretKey,
|
|
735
494
|
customerToken,
|
|
@@ -739,7 +498,7 @@ async function httpFetch(url, options) {
|
|
|
739
498
|
onUnauthorized,
|
|
740
499
|
...requestInit
|
|
741
500
|
} = options || {};
|
|
742
|
-
const baseUrl = resolveApiUrl();
|
|
501
|
+
const baseUrl = resolveApiUrl(apiUrl);
|
|
743
502
|
const retryConfig = {
|
|
744
503
|
maxRetries: retry?.maxRetries ?? 3,
|
|
745
504
|
retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
|
|
@@ -928,7 +687,7 @@ async function httpFetch(url, options) {
|
|
|
928
687
|
|
|
929
688
|
// src/core/collection/http-client.ts
|
|
930
689
|
var HttpClient = class {
|
|
931
|
-
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
|
|
690
|
+
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
932
691
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
933
692
|
"CollectionClient",
|
|
934
693
|
publishableKey,
|
|
@@ -938,9 +697,11 @@ var HttpClient = class {
|
|
|
938
697
|
this.getCustomerToken = getCustomerToken;
|
|
939
698
|
this.onUnauthorized = onUnauthorized;
|
|
940
699
|
this.onRequestId = onRequestId;
|
|
700
|
+
this.apiUrl = apiUrl;
|
|
941
701
|
}
|
|
942
702
|
get defaultOptions() {
|
|
943
703
|
const opts = {
|
|
704
|
+
apiUrl: this.apiUrl,
|
|
944
705
|
publishableKey: this.publishableKey,
|
|
945
706
|
secretKey: this.secretKey
|
|
946
707
|
};
|
|
@@ -1058,159 +819,113 @@ var HttpClient = class {
|
|
|
1058
819
|
}
|
|
1059
820
|
};
|
|
1060
821
|
|
|
1061
|
-
// src/
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
822
|
+
// src/utils/types.ts
|
|
823
|
+
var resolveRelation = (ref) => {
|
|
824
|
+
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
825
|
+
return null;
|
|
826
|
+
return ref;
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
// src/core/metadata/index.ts
|
|
830
|
+
function extractSeo(doc) {
|
|
831
|
+
const seo = doc.seo ?? {};
|
|
832
|
+
const og = seo.openGraph ?? {};
|
|
833
|
+
return {
|
|
834
|
+
title: seo.title ?? doc.title ?? null,
|
|
835
|
+
description: seo.description ?? null,
|
|
836
|
+
noIndex: seo.noIndex ?? null,
|
|
837
|
+
canonical: seo.canonical ?? null,
|
|
838
|
+
openGraph: {
|
|
839
|
+
title: og.title ?? null,
|
|
840
|
+
description: og.description ?? null,
|
|
841
|
+
image: og.image ?? null
|
|
842
|
+
}
|
|
843
|
+
};
|
|
1069
844
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
845
|
+
function generateMetadata(input, options) {
|
|
846
|
+
const title = input.title ?? void 0;
|
|
847
|
+
const description = input.description ?? void 0;
|
|
848
|
+
const ogTitle = input.openGraph?.title ?? title;
|
|
849
|
+
const ogDescription = input.openGraph?.description ?? description;
|
|
850
|
+
const image = resolveMetaImage(input.openGraph?.image);
|
|
851
|
+
return {
|
|
852
|
+
title,
|
|
853
|
+
description,
|
|
854
|
+
...input.noIndex && { robots: { index: false, follow: false } },
|
|
855
|
+
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
856
|
+
openGraph: {
|
|
857
|
+
...ogTitle && { title: ogTitle },
|
|
858
|
+
...ogDescription && { description: ogDescription },
|
|
859
|
+
...options?.siteName && { siteName: options.siteName },
|
|
860
|
+
...image && { images: [image] }
|
|
861
|
+
},
|
|
862
|
+
twitter: {
|
|
863
|
+
card: image ? "summary_large_image" : "summary",
|
|
864
|
+
...ogTitle && { title: ogTitle },
|
|
865
|
+
...ogDescription && { description: ogDescription },
|
|
866
|
+
...image && { images: [image.url] }
|
|
867
|
+
}
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function resolveMetaImage(ref) {
|
|
871
|
+
const image = resolveRelation(ref);
|
|
872
|
+
if (!image) return null;
|
|
873
|
+
const sized = image.sizes?.["1536"];
|
|
874
|
+
const url = sized?.url || image.url;
|
|
875
|
+
if (!url) return null;
|
|
876
|
+
const width = sized?.url ? sized.width : image.width;
|
|
877
|
+
const height = sized?.url ? sized.height : image.height;
|
|
878
|
+
return {
|
|
879
|
+
url,
|
|
880
|
+
...width && { width },
|
|
881
|
+
...height && { height },
|
|
882
|
+
...image.alt && { alt: image.alt }
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/core/collection/query-builder.ts
|
|
887
|
+
var ReadOnlyCollectionQueryBuilder = class {
|
|
888
|
+
constructor(api, collection) {
|
|
889
|
+
this.api = api;
|
|
890
|
+
this.collection = collection;
|
|
1073
891
|
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
* GET /api/{collection}
|
|
1080
|
-
*/
|
|
1081
|
-
async requestFind(endpoint, options) {
|
|
1082
|
-
const url = this.buildUrl(endpoint, options);
|
|
1083
|
-
const response = await this.fetchWithTracking(url, {
|
|
1084
|
-
...this.defaultOptions,
|
|
1085
|
-
method: "GET"
|
|
1086
|
-
});
|
|
1087
|
-
return this.parseFindResponse(response);
|
|
1088
|
-
}
|
|
1089
|
-
/**
|
|
1090
|
-
* Find-like response from a custom endpoint
|
|
1091
|
-
* POST /api/...custom-endpoint
|
|
1092
|
-
*/
|
|
1093
|
-
async requestFindEndpoint(endpoint, data) {
|
|
1094
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1095
|
-
...this.defaultOptions,
|
|
1096
|
-
method: "POST",
|
|
1097
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1098
|
-
});
|
|
1099
|
-
return this.parseFindResponse(response);
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* Find document by ID
|
|
1103
|
-
* GET /api/{collection}/{id}
|
|
1104
|
-
*/
|
|
1105
|
-
async requestFindById(endpoint, options) {
|
|
1106
|
-
const url = this.buildUrl(endpoint, options);
|
|
1107
|
-
const response = await this.fetchWithTracking(url, {
|
|
1108
|
-
...this.defaultOptions,
|
|
1109
|
-
method: "GET"
|
|
1110
|
-
});
|
|
1111
|
-
return this.parseDocumentResponse(response);
|
|
1112
|
-
}
|
|
1113
|
-
/**
|
|
1114
|
-
* Create document
|
|
1115
|
-
* POST /api/{collection}
|
|
1116
|
-
*/
|
|
1117
|
-
async requestCreate(endpoint, data) {
|
|
1118
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1119
|
-
...this.defaultOptions,
|
|
1120
|
-
method: "POST",
|
|
1121
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1122
|
-
});
|
|
1123
|
-
return this.parseMutationResponse(response);
|
|
1124
|
-
}
|
|
1125
|
-
/**
|
|
1126
|
-
* Update document
|
|
1127
|
-
* PATCH /api/{collection}/{id}
|
|
1128
|
-
*/
|
|
1129
|
-
async requestUpdate(endpoint, data) {
|
|
1130
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1131
|
-
...this.defaultOptions,
|
|
1132
|
-
method: "PATCH",
|
|
1133
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1134
|
-
});
|
|
1135
|
-
return this.parseMutationResponse(response);
|
|
1136
|
-
}
|
|
1137
|
-
/**
|
|
1138
|
-
* Count documents
|
|
1139
|
-
* GET /api/{collection}/count
|
|
1140
|
-
*/
|
|
1141
|
-
async requestCount(endpoint, options) {
|
|
1142
|
-
const url = this.buildUrl(endpoint, options);
|
|
1143
|
-
const response = await this.fetchWithTracking(url, {
|
|
1144
|
-
...this.defaultOptions,
|
|
1145
|
-
method: "GET"
|
|
1146
|
-
});
|
|
1147
|
-
return this.parseDocumentResponse(response);
|
|
1148
|
-
}
|
|
1149
|
-
/**
|
|
1150
|
-
* Update multiple documents (bulk update)
|
|
1151
|
-
* PATCH /api/{collection}
|
|
1152
|
-
*/
|
|
1153
|
-
async requestUpdateMany(endpoint, data) {
|
|
1154
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1155
|
-
...this.defaultOptions,
|
|
1156
|
-
method: "PATCH",
|
|
1157
|
-
body: JSON.stringify(data)
|
|
1158
|
-
});
|
|
1159
|
-
return this.parseFindResponse(response);
|
|
1160
|
-
}
|
|
1161
|
-
/**
|
|
1162
|
-
* Delete document
|
|
1163
|
-
* DELETE /api/{collection}/{id}
|
|
1164
|
-
*/
|
|
1165
|
-
async requestDelete(endpoint) {
|
|
1166
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1167
|
-
...this.defaultOptions,
|
|
1168
|
-
method: "DELETE"
|
|
1169
|
-
});
|
|
1170
|
-
return this.parseDocumentResponse(response);
|
|
892
|
+
async find(options) {
|
|
893
|
+
return this.api.requestFind(
|
|
894
|
+
`/api/${String(this.collection)}`,
|
|
895
|
+
options
|
|
896
|
+
);
|
|
1171
897
|
}
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1178
|
-
...this.defaultOptions,
|
|
1179
|
-
method: "DELETE",
|
|
1180
|
-
body: JSON.stringify(data)
|
|
1181
|
-
});
|
|
1182
|
-
return this.parseFindResponse(response);
|
|
898
|
+
async findById(id, options) {
|
|
899
|
+
return this.api.requestFindById(
|
|
900
|
+
`/api/${String(this.collection)}/${String(id)}`,
|
|
901
|
+
options
|
|
902
|
+
);
|
|
1183
903
|
}
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1190
|
-
...this.defaultOptions,
|
|
1191
|
-
method: "POST",
|
|
1192
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1193
|
-
});
|
|
1194
|
-
return this.parseMutationResponse(response);
|
|
904
|
+
async count(options) {
|
|
905
|
+
return this.api.requestCount(
|
|
906
|
+
`/api/${String(this.collection)}/count`,
|
|
907
|
+
options
|
|
908
|
+
);
|
|
1195
909
|
}
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1205
|
-
});
|
|
1206
|
-
return this.parseMutationResponse(response);
|
|
910
|
+
async findMetadata(options, metadataOptions) {
|
|
911
|
+
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
912
|
+
const doc = docs[0];
|
|
913
|
+
if (!doc) return null;
|
|
914
|
+
return generateMetadata(
|
|
915
|
+
extractSeo(doc),
|
|
916
|
+
metadataOptions
|
|
917
|
+
);
|
|
1207
918
|
}
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
919
|
+
async findMetadataById(id, metadataOptions) {
|
|
920
|
+
const doc = await this.findById(id, { depth: 1 });
|
|
921
|
+
return generateMetadata(
|
|
922
|
+
extractSeo(doc),
|
|
923
|
+
metadataOptions
|
|
924
|
+
);
|
|
1212
925
|
}
|
|
1213
926
|
};
|
|
927
|
+
|
|
928
|
+
// src/core/collection/collection-client.ts
|
|
1214
929
|
var ReadOnlyCollectionClient = class extends HttpClient {
|
|
1215
930
|
from(collection) {
|
|
1216
931
|
return new ReadOnlyCollectionQueryBuilder(this, collection);
|
|
@@ -1241,126 +956,6 @@ var ReadOnlyCollectionClient = class extends HttpClient {
|
|
|
1241
956
|
}
|
|
1242
957
|
};
|
|
1243
958
|
|
|
1244
|
-
// src/core/collection/const.ts
|
|
1245
|
-
var INTERNAL_COLLECTIONS = [
|
|
1246
|
-
"users",
|
|
1247
|
-
"payload-kv",
|
|
1248
|
-
"payload-locked-documents",
|
|
1249
|
-
"payload-preferences",
|
|
1250
|
-
"payload-migrations",
|
|
1251
|
-
"payload-folders",
|
|
1252
|
-
"field-configs",
|
|
1253
|
-
"system-media",
|
|
1254
|
-
"track-assets",
|
|
1255
|
-
"audiences",
|
|
1256
|
-
"email-logs",
|
|
1257
|
-
"api-usage",
|
|
1258
|
-
"tenant-analytics-daily",
|
|
1259
|
-
"tenant-web-analytics-config",
|
|
1260
|
-
"analytics-event-schemas",
|
|
1261
|
-
"subscriptions",
|
|
1262
|
-
"billing-history",
|
|
1263
|
-
"inventory-reservations",
|
|
1264
|
-
"order-status-logs",
|
|
1265
|
-
"api-keys",
|
|
1266
|
-
"personal-access-tokens",
|
|
1267
|
-
"tenant-entitlements",
|
|
1268
|
-
"tenant-purge-jobs",
|
|
1269
|
-
"direct-upload-sessions",
|
|
1270
|
-
"webhook-events",
|
|
1271
|
-
"webhook-deliveries",
|
|
1272
|
-
"audit-logs",
|
|
1273
|
-
"plans",
|
|
1274
|
-
"webhooks",
|
|
1275
|
-
"event-registrations"
|
|
1276
|
-
];
|
|
1277
|
-
var COLLECTIONS = [
|
|
1278
|
-
"tenants",
|
|
1279
|
-
"tenant-metadata",
|
|
1280
|
-
"tenant-logos",
|
|
1281
|
-
"products",
|
|
1282
|
-
"product-variants",
|
|
1283
|
-
"product-options",
|
|
1284
|
-
"product-option-values",
|
|
1285
|
-
"product-categories",
|
|
1286
|
-
"product-tags",
|
|
1287
|
-
"product-collections",
|
|
1288
|
-
"brands",
|
|
1289
|
-
"brand-logos",
|
|
1290
|
-
"orders",
|
|
1291
|
-
"order-items",
|
|
1292
|
-
"returns",
|
|
1293
|
-
"return-items",
|
|
1294
|
-
"fulfillments",
|
|
1295
|
-
"fulfillment-items",
|
|
1296
|
-
"transactions",
|
|
1297
|
-
"customers",
|
|
1298
|
-
"customer-profiles",
|
|
1299
|
-
"customer-profile-lists",
|
|
1300
|
-
"customer-addresses",
|
|
1301
|
-
"carts",
|
|
1302
|
-
"cart-items",
|
|
1303
|
-
"discounts",
|
|
1304
|
-
"shipping-policies",
|
|
1305
|
-
"shipping-zones",
|
|
1306
|
-
"documents",
|
|
1307
|
-
"document-categories",
|
|
1308
|
-
"document-types",
|
|
1309
|
-
"articles",
|
|
1310
|
-
"article-authors",
|
|
1311
|
-
"article-categories",
|
|
1312
|
-
"article-tags",
|
|
1313
|
-
"playlists",
|
|
1314
|
-
"playlist-categories",
|
|
1315
|
-
"playlist-tags",
|
|
1316
|
-
"tracks",
|
|
1317
|
-
"track-categories",
|
|
1318
|
-
"track-tags",
|
|
1319
|
-
"galleries",
|
|
1320
|
-
"gallery-categories",
|
|
1321
|
-
"gallery-tags",
|
|
1322
|
-
"gallery-items",
|
|
1323
|
-
"links",
|
|
1324
|
-
"link-categories",
|
|
1325
|
-
"link-tags",
|
|
1326
|
-
"canvases",
|
|
1327
|
-
"canvas-node-types",
|
|
1328
|
-
"canvas-edge-types",
|
|
1329
|
-
"canvas-categories",
|
|
1330
|
-
"canvas-tags",
|
|
1331
|
-
"canvas-nodes",
|
|
1332
|
-
"canvas-edges",
|
|
1333
|
-
"videos",
|
|
1334
|
-
"video-categories",
|
|
1335
|
-
"video-tags",
|
|
1336
|
-
"live-streams",
|
|
1337
|
-
"images",
|
|
1338
|
-
"forms",
|
|
1339
|
-
"form-submissions",
|
|
1340
|
-
// Community
|
|
1341
|
-
"posts",
|
|
1342
|
-
"comments",
|
|
1343
|
-
"reactions",
|
|
1344
|
-
"reaction-types",
|
|
1345
|
-
"bookmarks",
|
|
1346
|
-
"post-categories",
|
|
1347
|
-
// Events
|
|
1348
|
-
"event-calendars",
|
|
1349
|
-
"events",
|
|
1350
|
-
"event-categories",
|
|
1351
|
-
"event-occurrences",
|
|
1352
|
-
"event-tags"
|
|
1353
|
-
];
|
|
1354
|
-
var SERVER_ONLY_COLLECTIONS = [
|
|
1355
|
-
"customer-groups",
|
|
1356
|
-
"reports",
|
|
1357
|
-
"community-bans"
|
|
1358
|
-
];
|
|
1359
|
-
var SERVER_COLLECTIONS = [
|
|
1360
|
-
...COLLECTIONS,
|
|
1361
|
-
...SERVER_ONLY_COLLECTIONS
|
|
1362
|
-
];
|
|
1363
|
-
|
|
1364
959
|
// src/core/api/parse-response.ts
|
|
1365
960
|
async function parseApiResponse(response, endpoint) {
|
|
1366
961
|
let data;
|
|
@@ -1416,6 +1011,7 @@ var CommunityClient = class {
|
|
|
1416
1011
|
options.secretKey
|
|
1417
1012
|
);
|
|
1418
1013
|
this.secretKey = options.secretKey;
|
|
1014
|
+
this.apiUrl = options.apiUrl;
|
|
1419
1015
|
this.customerToken = options.customerToken;
|
|
1420
1016
|
this.onUnauthorized = options.onUnauthorized;
|
|
1421
1017
|
this.onRequestId = options.onRequestId;
|
|
@@ -1430,6 +1026,7 @@ var CommunityClient = class {
|
|
|
1430
1026
|
try {
|
|
1431
1027
|
const response = await httpFetch(endpoint, {
|
|
1432
1028
|
method,
|
|
1029
|
+
apiUrl: this.apiUrl,
|
|
1433
1030
|
publishableKey: this.publishableKey,
|
|
1434
1031
|
secretKey: this.secretKey,
|
|
1435
1032
|
customerToken: token ?? void 0,
|
|
@@ -1578,67 +1175,20 @@ var CommunityClient = class {
|
|
|
1578
1175
|
}
|
|
1579
1176
|
};
|
|
1580
1177
|
|
|
1581
|
-
// src/core/
|
|
1582
|
-
var
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
apiName,
|
|
1589
|
-
options.publishableKey,
|
|
1590
|
-
options.secretKey
|
|
1591
|
-
);
|
|
1592
|
-
this.secretKey = options.secretKey;
|
|
1593
|
-
this.onRequestId = options.onRequestId;
|
|
1594
|
-
}
|
|
1595
|
-
async request(endpoint, body, options) {
|
|
1596
|
-
const method = options?.method ?? "POST";
|
|
1597
|
-
try {
|
|
1598
|
-
const response = await httpFetch(endpoint, {
|
|
1599
|
-
method,
|
|
1600
|
-
publishableKey: this.publishableKey,
|
|
1601
|
-
secretKey: this.secretKey,
|
|
1602
|
-
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1603
|
-
...options?.headers && { headers: options.headers }
|
|
1604
|
-
});
|
|
1605
|
-
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1606
|
-
return parseApiResponse(response, endpoint);
|
|
1607
|
-
} catch (err) {
|
|
1608
|
-
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1609
|
-
this.onRequestId?.(id);
|
|
1610
|
-
throw err;
|
|
1611
|
-
}
|
|
1612
|
-
}
|
|
1613
|
-
};
|
|
1614
|
-
|
|
1615
|
-
// src/core/community/moderation-api.ts
|
|
1616
|
-
var ModerationApi = class extends BaseApi {
|
|
1617
|
-
constructor(options) {
|
|
1618
|
-
super("ModerationApi", options);
|
|
1619
|
-
}
|
|
1620
|
-
banCustomer(params) {
|
|
1621
|
-
return this.request("/api/community-bans/ban", params);
|
|
1622
|
-
}
|
|
1623
|
-
unbanCustomer(params) {
|
|
1624
|
-
return this.request("/api/community-bans/unban", params);
|
|
1625
|
-
}
|
|
1626
|
-
};
|
|
1627
|
-
|
|
1628
|
-
// src/core/customer/customer-auth.ts
|
|
1629
|
-
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1630
|
-
function safeGetItem(key) {
|
|
1631
|
-
try {
|
|
1632
|
-
return localStorage.getItem(key);
|
|
1633
|
-
} catch {
|
|
1634
|
-
return null;
|
|
1178
|
+
// src/core/customer/customer-auth.ts
|
|
1179
|
+
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1180
|
+
function safeGetItem(key) {
|
|
1181
|
+
try {
|
|
1182
|
+
return localStorage.getItem(key);
|
|
1183
|
+
} catch {
|
|
1184
|
+
return null;
|
|
1635
1185
|
}
|
|
1636
1186
|
}
|
|
1637
1187
|
var CustomerAuth = class {
|
|
1638
|
-
constructor(publishableKey, options) {
|
|
1188
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1639
1189
|
this.refreshPromise = null;
|
|
1640
1190
|
this.publishableKey = publishableKey;
|
|
1641
|
-
this.baseUrl = resolveApiUrl();
|
|
1191
|
+
this.baseUrl = resolveApiUrl(apiUrl);
|
|
1642
1192
|
const persist = options?.persist ?? true;
|
|
1643
1193
|
if (persist) {
|
|
1644
1194
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
@@ -1869,8 +1419,8 @@ var CustomerAuth = class {
|
|
|
1869
1419
|
|
|
1870
1420
|
// src/core/customer/customer-namespace.ts
|
|
1871
1421
|
var CustomerNamespace = class {
|
|
1872
|
-
constructor(publishableKey, options) {
|
|
1873
|
-
this.auth = new CustomerAuth(publishableKey, options);
|
|
1422
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1423
|
+
this.auth = new CustomerAuth(publishableKey, options, apiUrl);
|
|
1874
1424
|
}
|
|
1875
1425
|
};
|
|
1876
1426
|
|
|
@@ -1888,6 +1438,7 @@ var CartApi = class {
|
|
|
1888
1438
|
options.secretKey
|
|
1889
1439
|
);
|
|
1890
1440
|
this.secretKey = options.secretKey;
|
|
1441
|
+
this.apiUrl = options.apiUrl;
|
|
1891
1442
|
this.customerToken = options.customerToken;
|
|
1892
1443
|
this.onUnauthorized = options.onUnauthorized;
|
|
1893
1444
|
this.onRequestId = options.onRequestId;
|
|
@@ -1897,6 +1448,7 @@ var CartApi = class {
|
|
|
1897
1448
|
try {
|
|
1898
1449
|
const response = await httpFetch(endpoint, {
|
|
1899
1450
|
method,
|
|
1451
|
+
apiUrl: this.apiUrl,
|
|
1900
1452
|
publishableKey: this.publishableKey,
|
|
1901
1453
|
secretKey: this.secretKey,
|
|
1902
1454
|
customerToken: token ?? void 0,
|
|
@@ -1947,6 +1499,7 @@ var CommerceClient = class {
|
|
|
1947
1499
|
constructor(options) {
|
|
1948
1500
|
const cartApi = new CartApi({
|
|
1949
1501
|
publishableKey: options.publishableKey,
|
|
1502
|
+
apiUrl: options.apiUrl,
|
|
1950
1503
|
customerToken: options.customerToken,
|
|
1951
1504
|
onUnauthorized: options.onUnauthorized,
|
|
1952
1505
|
onRequestId: options.onRequestId
|
|
@@ -1956,6 +1509,7 @@ var CommerceClient = class {
|
|
|
1956
1509
|
try {
|
|
1957
1510
|
const response = await httpFetch(endpoint, {
|
|
1958
1511
|
method: "POST",
|
|
1512
|
+
apiUrl: options.apiUrl,
|
|
1959
1513
|
publishableKey: options.publishableKey,
|
|
1960
1514
|
customerToken: token ?? void 0,
|
|
1961
1515
|
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
|
|
@@ -2003,70 +1557,104 @@ var CommerceClient = class {
|
|
|
2003
1557
|
}
|
|
2004
1558
|
};
|
|
2005
1559
|
|
|
2006
|
-
// src/core/
|
|
2007
|
-
var
|
|
1560
|
+
// src/core/client/client.ts
|
|
1561
|
+
var Client = class {
|
|
2008
1562
|
constructor(options) {
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
2014
|
-
* to remain available by the time an order is placed.
|
|
2015
|
-
*/
|
|
2016
|
-
stockCheck(params) {
|
|
2017
|
-
return this.request("/api/products/stock-check", params);
|
|
2018
|
-
}
|
|
2019
|
-
listingGroups(params) {
|
|
2020
|
-
return this.request(
|
|
2021
|
-
"/api/products/listing-groups",
|
|
2022
|
-
params
|
|
2023
|
-
);
|
|
2024
|
-
}
|
|
2025
|
-
/**
|
|
2026
|
-
* Fetch full product detail by slug or id.
|
|
2027
|
-
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
2028
|
-
* `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
|
|
2029
|
-
* inspect `client.lastRequestId` against backend logs.
|
|
2030
|
-
*/
|
|
2031
|
-
async detail(params) {
|
|
2032
|
-
try {
|
|
2033
|
-
return await this.request("/api/products/detail", params);
|
|
2034
|
-
} catch (err) {
|
|
2035
|
-
if (err instanceof NotFoundError) return null;
|
|
2036
|
-
throw err;
|
|
1563
|
+
this.lastRequestId = null;
|
|
1564
|
+
const publishableKey = options.publishableKey;
|
|
1565
|
+
if (!publishableKey) {
|
|
1566
|
+
throw createConfigError("publishableKey is required.");
|
|
2037
1567
|
}
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
1568
|
+
this.config = { ...options, publishableKey };
|
|
1569
|
+
const metadata = {
|
|
1570
|
+
timestamp: Date.now(),
|
|
1571
|
+
userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
|
|
1572
|
+
};
|
|
1573
|
+
this.state = { metadata };
|
|
1574
|
+
this.customer = new CustomerNamespace(
|
|
1575
|
+
this.config.publishableKey,
|
|
1576
|
+
options.customer,
|
|
1577
|
+
this.config.apiUrl
|
|
1578
|
+
);
|
|
1579
|
+
const onUnauthorized = async () => {
|
|
1580
|
+
try {
|
|
1581
|
+
const result = await this.customer.auth.refreshToken();
|
|
1582
|
+
return result.token ?? null;
|
|
1583
|
+
} catch {
|
|
1584
|
+
return null;
|
|
1585
|
+
}
|
|
1586
|
+
};
|
|
1587
|
+
const onRequestId = (id) => {
|
|
1588
|
+
this.lastRequestId = id;
|
|
1589
|
+
};
|
|
1590
|
+
this.commerce = new CommerceClient({
|
|
1591
|
+
publishableKey: this.config.publishableKey,
|
|
1592
|
+
apiUrl: this.config.apiUrl,
|
|
1593
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1594
|
+
onUnauthorized,
|
|
1595
|
+
onRequestId,
|
|
1596
|
+
customerAuth: this.customer.auth
|
|
1597
|
+
});
|
|
1598
|
+
this.community = new CommunityClient({
|
|
1599
|
+
publishableKey: this.config.publishableKey,
|
|
1600
|
+
apiUrl: this.config.apiUrl,
|
|
1601
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1602
|
+
onUnauthorized,
|
|
1603
|
+
onRequestId
|
|
1604
|
+
});
|
|
1605
|
+
this.collections = new ReadOnlyCollectionClient(
|
|
1606
|
+
this.config.publishableKey,
|
|
1607
|
+
void 0,
|
|
1608
|
+
() => this.customer.auth.getToken(),
|
|
1609
|
+
onUnauthorized,
|
|
1610
|
+
onRequestId,
|
|
1611
|
+
this.config.apiUrl
|
|
2049
1612
|
);
|
|
2050
1613
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
// src/core/api/discount-api.ts
|
|
2054
|
-
var DiscountApi = class extends BaseApi {
|
|
2055
|
-
constructor(options) {
|
|
2056
|
-
super("DiscountApi", options);
|
|
1614
|
+
getState() {
|
|
1615
|
+
return { ...this.state };
|
|
2057
1616
|
}
|
|
2058
|
-
|
|
2059
|
-
return this.
|
|
1617
|
+
getConfig() {
|
|
1618
|
+
return { ...this.config };
|
|
2060
1619
|
}
|
|
2061
1620
|
};
|
|
1621
|
+
function createClient(options) {
|
|
1622
|
+
return new Client(options);
|
|
1623
|
+
}
|
|
2062
1624
|
|
|
2063
|
-
// src/core/api/
|
|
2064
|
-
var
|
|
2065
|
-
constructor(options) {
|
|
2066
|
-
|
|
1625
|
+
// src/core/api/base-api.ts
|
|
1626
|
+
var BaseApi = class {
|
|
1627
|
+
constructor(apiName, options) {
|
|
1628
|
+
if (!options.secretKey) {
|
|
1629
|
+
throw createConfigError(`secretKey is required for ${apiName}.`);
|
|
1630
|
+
}
|
|
1631
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
1632
|
+
apiName,
|
|
1633
|
+
options.publishableKey,
|
|
1634
|
+
options.secretKey
|
|
1635
|
+
);
|
|
1636
|
+
this.secretKey = options.secretKey;
|
|
1637
|
+
this.apiUrl = options.apiUrl;
|
|
1638
|
+
this.onRequestId = options.onRequestId;
|
|
2067
1639
|
}
|
|
2068
|
-
|
|
2069
|
-
|
|
1640
|
+
async request(endpoint, body, options) {
|
|
1641
|
+
const method = options?.method ?? "POST";
|
|
1642
|
+
try {
|
|
1643
|
+
const response = await httpFetch(endpoint, {
|
|
1644
|
+
method,
|
|
1645
|
+
apiUrl: this.apiUrl,
|
|
1646
|
+
publishableKey: this.publishableKey,
|
|
1647
|
+
secretKey: this.secretKey,
|
|
1648
|
+
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1649
|
+
...options?.headers && { headers: options.headers }
|
|
1650
|
+
});
|
|
1651
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1652
|
+
return parseApiResponse(response, endpoint);
|
|
1653
|
+
} catch (err) {
|
|
1654
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1655
|
+
this.onRequestId?.(id);
|
|
1656
|
+
throw err;
|
|
1657
|
+
}
|
|
2070
1658
|
}
|
|
2071
1659
|
};
|
|
2072
1660
|
|
|
@@ -2120,751 +1708,306 @@ var OrderApi = class extends BaseApi {
|
|
|
2120
1708
|
}
|
|
2121
1709
|
};
|
|
2122
1710
|
|
|
2123
|
-
// src/core/
|
|
2124
|
-
var
|
|
1711
|
+
// src/core/api/discount-api.ts
|
|
1712
|
+
var DiscountApi = class extends BaseApi {
|
|
2125
1713
|
constructor(options) {
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
);
|
|
2131
|
-
const serverOptions = {
|
|
2132
|
-
publishableKey,
|
|
2133
|
-
secretKey: options.secretKey,
|
|
2134
|
-
onRequestId: options.onRequestId
|
|
2135
|
-
};
|
|
2136
|
-
const productApi = new ProductApi(serverOptions);
|
|
2137
|
-
const cartApi = new CartApi(serverOptions);
|
|
2138
|
-
const discountApi = new DiscountApi(serverOptions);
|
|
2139
|
-
const shippingApi = new ShippingApi(serverOptions);
|
|
2140
|
-
const orderApi = new OrderApi(serverOptions);
|
|
2141
|
-
this.product = {
|
|
2142
|
-
stockCheck: productApi.stockCheck.bind(productApi),
|
|
2143
|
-
listingGroups: productApi.listingGroups.bind(productApi),
|
|
2144
|
-
detail: productApi.detail.bind(productApi),
|
|
2145
|
-
upsert: productApi.upsert.bind(productApi)
|
|
2146
|
-
};
|
|
2147
|
-
this.cart = {
|
|
2148
|
-
get: cartApi.getCart.bind(cartApi),
|
|
2149
|
-
addItem: cartApi.addItem.bind(cartApi),
|
|
2150
|
-
updateItem: cartApi.updateItem.bind(cartApi),
|
|
2151
|
-
removeItem: cartApi.removeItem.bind(cartApi),
|
|
2152
|
-
applyDiscount: cartApi.applyDiscount.bind(cartApi),
|
|
2153
|
-
removeDiscount: cartApi.removeDiscount.bind(cartApi),
|
|
2154
|
-
clear: cartApi.clearCart.bind(cartApi)
|
|
2155
|
-
};
|
|
2156
|
-
this.orders = {
|
|
2157
|
-
checkout: orderApi.checkout.bind(orderApi),
|
|
2158
|
-
create: orderApi.createOrder.bind(orderApi),
|
|
2159
|
-
update: orderApi.updateOrder.bind(orderApi),
|
|
2160
|
-
updateTransaction: orderApi.updateTransaction.bind(orderApi),
|
|
2161
|
-
confirmPayment: orderApi.confirmPayment.bind(orderApi),
|
|
2162
|
-
createFulfillment: orderApi.createFulfillment.bind(orderApi),
|
|
2163
|
-
updateFulfillment: orderApi.updateFulfillment.bind(orderApi),
|
|
2164
|
-
bulkImportFulfillments: orderApi.bulkImportFulfillments.bind(orderApi),
|
|
2165
|
-
createReturn: orderApi.createReturn.bind(orderApi),
|
|
2166
|
-
updateReturn: orderApi.updateReturn.bind(orderApi),
|
|
2167
|
-
returnWithRefund: orderApi.returnWithRefund.bind(orderApi)
|
|
2168
|
-
};
|
|
2169
|
-
this.discounts = {
|
|
2170
|
-
validate: discountApi.validate.bind(discountApi)
|
|
2171
|
-
};
|
|
2172
|
-
this.shipping = {
|
|
2173
|
-
calculate: shippingApi.calculate.bind(shippingApi)
|
|
2174
|
-
};
|
|
1714
|
+
super("DiscountApi", options);
|
|
1715
|
+
}
|
|
1716
|
+
validate(params) {
|
|
1717
|
+
return this.request("/api/discounts/validate", params);
|
|
2175
1718
|
}
|
|
2176
1719
|
};
|
|
2177
1720
|
|
|
2178
|
-
// src/core/
|
|
2179
|
-
var
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
defaultOptions: {
|
|
2183
|
-
queries: {
|
|
2184
|
-
// Infinite staleTime: server-fetched data persists until explicitly invalidated.
|
|
2185
|
-
// For browser clients needing fresher data, override per-query:
|
|
2186
|
-
// useQuery({ ..., staleTime: 5 * 60 * 1000 })
|
|
2187
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
2188
|
-
refetchOnWindowFocus: false
|
|
2189
|
-
},
|
|
2190
|
-
dehydrate: {
|
|
2191
|
-
shouldDehydrateQuery: (query) => (0, import_react_query.defaultShouldDehydrateQuery)(query) || query.state.status === "pending",
|
|
2192
|
-
shouldRedactErrors: () => false
|
|
2193
|
-
}
|
|
2194
|
-
}
|
|
2195
|
-
});
|
|
2196
|
-
}
|
|
2197
|
-
var browserQueryClient;
|
|
2198
|
-
function getQueryClient() {
|
|
2199
|
-
if (import_react_query.isServer) {
|
|
2200
|
-
return makeQueryClient();
|
|
1721
|
+
// src/core/api/shipping-api.ts
|
|
1722
|
+
var ShippingApi = class extends BaseApi {
|
|
1723
|
+
constructor(options) {
|
|
1724
|
+
super("ShippingApi", options);
|
|
2201
1725
|
}
|
|
2202
|
-
|
|
2203
|
-
|
|
1726
|
+
calculate(params) {
|
|
1727
|
+
return this.request("/api/shipping-policies/calculate", params);
|
|
2204
1728
|
}
|
|
2205
|
-
return browserQueryClient;
|
|
2206
|
-
}
|
|
2207
|
-
|
|
2208
|
-
// src/core/query/query-hooks.ts
|
|
2209
|
-
var import_react_query4 = require("@tanstack/react-query");
|
|
2210
|
-
|
|
2211
|
-
// src/core/query/collection-hooks.ts
|
|
2212
|
-
var import_react_query2 = require("@tanstack/react-query");
|
|
2213
|
-
|
|
2214
|
-
// src/core/query/query-keys.ts
|
|
2215
|
-
function collectionKeys(collection) {
|
|
2216
|
-
return {
|
|
2217
|
-
all: [collection],
|
|
2218
|
-
lists: () => [collection, "list"],
|
|
2219
|
-
list: (options) => [collection, "list", options],
|
|
2220
|
-
details: () => [collection, "detail"],
|
|
2221
|
-
detail: (id, options) => [collection, "detail", id, options],
|
|
2222
|
-
infinites: () => [collection, "infinite"],
|
|
2223
|
-
infinite: (options) => [collection, "infinite", options]
|
|
2224
|
-
};
|
|
2225
|
-
}
|
|
2226
|
-
var customerKeys = {
|
|
2227
|
-
all: ["customer"],
|
|
2228
|
-
me: () => ["customer", "me"]
|
|
2229
|
-
};
|
|
2230
|
-
var productKeys = {
|
|
2231
|
-
listingGroups: (options) => ["products", "listing-groups", "list", options],
|
|
2232
|
-
listingGroupsInfinite: (options) => ["products", "listing-groups", "infinite", options],
|
|
2233
|
-
detail: (params) => ["products", "detail", params],
|
|
2234
|
-
detailAll: () => ["products", "detail"]
|
|
2235
1729
|
};
|
|
2236
1730
|
|
|
2237
|
-
// src/core/
|
|
2238
|
-
var
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
"product-options",
|
|
2242
|
-
"product-option-values",
|
|
2243
|
-
"product-categories",
|
|
2244
|
-
"product-tags",
|
|
2245
|
-
"product-collections",
|
|
2246
|
-
"brands",
|
|
2247
|
-
"brand-logos",
|
|
2248
|
-
"images"
|
|
2249
|
-
]);
|
|
2250
|
-
var DEFAULT_PAGE_SIZE = 20;
|
|
2251
|
-
var CollectionHooks = class {
|
|
2252
|
-
constructor(queryClient, collectionClient) {
|
|
2253
|
-
this.queryClient = queryClient;
|
|
2254
|
-
this.collectionClient = collectionClient;
|
|
2255
|
-
}
|
|
2256
|
-
// ===== useQuery =====
|
|
2257
|
-
useQuery(params, options) {
|
|
2258
|
-
const { collection, options: queryOptions } = params;
|
|
2259
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2260
|
-
return (0, import_react_query2.useQuery)({
|
|
2261
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2262
|
-
queryFn: async () => {
|
|
2263
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2264
|
-
},
|
|
2265
|
-
...restOptions,
|
|
2266
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2267
|
-
...placeholderData !== void 0 && {
|
|
2268
|
-
placeholderData
|
|
2269
|
-
}
|
|
2270
|
-
});
|
|
1731
|
+
// src/core/api/product-api.ts
|
|
1732
|
+
var ProductApi = class extends BaseApi {
|
|
1733
|
+
constructor(options) {
|
|
1734
|
+
super("ProductApi", options);
|
|
2271
1735
|
}
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
},
|
|
2280
|
-
...options
|
|
2281
|
-
});
|
|
1736
|
+
/**
|
|
1737
|
+
* Check point-in-time stock availability for one or more product variants.
|
|
1738
|
+
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
1739
|
+
* to remain available by the time an order is placed.
|
|
1740
|
+
*/
|
|
1741
|
+
stockCheck(params) {
|
|
1742
|
+
return this.request("/api/products/stock-check", params);
|
|
2282
1743
|
}
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
queryKey: collectionKeys(collection).detail(id, queryOptions),
|
|
2289
|
-
queryFn: async () => {
|
|
2290
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2291
|
-
},
|
|
2292
|
-
...restOptions,
|
|
2293
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2294
|
-
...placeholderData !== void 0 && {
|
|
2295
|
-
placeholderData
|
|
2296
|
-
}
|
|
2297
|
-
});
|
|
1744
|
+
listingGroups(params) {
|
|
1745
|
+
return this.request(
|
|
1746
|
+
"/api/products/listing-groups",
|
|
1747
|
+
params
|
|
1748
|
+
);
|
|
2298
1749
|
}
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
})
|
|
1750
|
+
/**
|
|
1751
|
+
* Fetch full product detail by slug or id.
|
|
1752
|
+
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
1753
|
+
* `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
|
|
1754
|
+
* inspect `client.lastRequestId` against backend logs.
|
|
1755
|
+
*/
|
|
1756
|
+
async detail(params) {
|
|
1757
|
+
try {
|
|
1758
|
+
return await this.request("/api/products/detail", params);
|
|
1759
|
+
} catch (err) {
|
|
1760
|
+
if (err instanceof NotFoundError) return null;
|
|
1761
|
+
throw err;
|
|
1762
|
+
}
|
|
2309
1763
|
}
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
return (
|
|
2318
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2319
|
-
queryFn: async ({ pageParam }) => {
|
|
2320
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2321
|
-
return response;
|
|
2322
|
-
},
|
|
2323
|
-
initialPageParam: 1,
|
|
2324
|
-
getNextPageParam: (lastPage) => {
|
|
2325
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2326
|
-
},
|
|
2327
|
-
...options
|
|
2328
|
-
});
|
|
2329
|
-
}
|
|
2330
|
-
// ===== useSuspenseInfiniteQuery =====
|
|
2331
|
-
useSuspenseInfiniteQuery(params, options) {
|
|
2332
|
-
const {
|
|
2333
|
-
collection,
|
|
2334
|
-
options: queryOptions,
|
|
2335
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
2336
|
-
} = params;
|
|
2337
|
-
return (0, import_react_query2.useSuspenseInfiniteQuery)({
|
|
2338
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2339
|
-
queryFn: async ({ pageParam }) => {
|
|
2340
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2341
|
-
return response;
|
|
2342
|
-
},
|
|
2343
|
-
initialPageParam: 1,
|
|
2344
|
-
getNextPageParam: (lastPage) => {
|
|
2345
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2346
|
-
},
|
|
2347
|
-
...options
|
|
2348
|
-
});
|
|
2349
|
-
}
|
|
2350
|
-
// ===== prefetchQuery =====
|
|
2351
|
-
async prefetchQuery(params, options) {
|
|
2352
|
-
const { collection, options: queryOptions } = params;
|
|
2353
|
-
return this.queryClient.prefetchQuery({
|
|
2354
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2355
|
-
queryFn: async () => {
|
|
2356
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2357
|
-
},
|
|
2358
|
-
...options
|
|
2359
|
-
});
|
|
2360
|
-
}
|
|
2361
|
-
// ===== prefetchQueryById =====
|
|
2362
|
-
async prefetchQueryById(params, options) {
|
|
2363
|
-
const { collection, id, options: queryOptions } = params;
|
|
2364
|
-
return this.queryClient.prefetchQuery({
|
|
2365
|
-
queryKey: collectionKeys(collection).detail(id, queryOptions),
|
|
2366
|
-
queryFn: async () => {
|
|
2367
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2368
|
-
},
|
|
2369
|
-
...options
|
|
2370
|
-
});
|
|
2371
|
-
}
|
|
2372
|
-
// ===== prefetchInfiniteQuery =====
|
|
2373
|
-
async prefetchInfiniteQuery(params, options) {
|
|
2374
|
-
const {
|
|
2375
|
-
collection,
|
|
2376
|
-
options: queryOptions,
|
|
2377
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
2378
|
-
} = params;
|
|
2379
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2380
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2381
|
-
queryFn: async ({ pageParam }) => {
|
|
2382
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2383
|
-
return response;
|
|
2384
|
-
},
|
|
2385
|
-
initialPageParam: 1,
|
|
2386
|
-
getNextPageParam: (lastPage) => {
|
|
2387
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2388
|
-
},
|
|
2389
|
-
pages: options?.pages ?? 1,
|
|
2390
|
-
staleTime: options?.staleTime
|
|
2391
|
-
});
|
|
2392
|
-
}
|
|
2393
|
-
// ===== Mutation Hooks =====
|
|
2394
|
-
useCreate(params, options) {
|
|
2395
|
-
const { collection } = params;
|
|
2396
|
-
return (0, import_react_query2.useMutation)({
|
|
2397
|
-
mutationFn: async (variables) => {
|
|
2398
|
-
return await this.collectionClient.from(collection).create(
|
|
2399
|
-
variables.data,
|
|
2400
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2401
|
-
);
|
|
2402
|
-
},
|
|
2403
|
-
onSuccess: (data) => {
|
|
2404
|
-
this.queryClient.invalidateQueries({
|
|
2405
|
-
queryKey: collectionKeys(collection).all
|
|
2406
|
-
});
|
|
2407
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2408
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2409
|
-
}
|
|
2410
|
-
options?.onSuccess?.(data);
|
|
2411
|
-
},
|
|
2412
|
-
onError: options?.onError,
|
|
2413
|
-
onSettled: options?.onSettled
|
|
2414
|
-
});
|
|
2415
|
-
}
|
|
2416
|
-
useUpdate(params, options) {
|
|
2417
|
-
const { collection } = params;
|
|
2418
|
-
return (0, import_react_query2.useMutation)({
|
|
2419
|
-
mutationFn: async (variables) => {
|
|
2420
|
-
return await this.collectionClient.from(collection).update(
|
|
2421
|
-
variables.id,
|
|
2422
|
-
variables.data,
|
|
2423
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2424
|
-
);
|
|
2425
|
-
},
|
|
2426
|
-
onSuccess: (data) => {
|
|
2427
|
-
this.queryClient.invalidateQueries({
|
|
2428
|
-
queryKey: collectionKeys(collection).all
|
|
2429
|
-
});
|
|
2430
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2431
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2432
|
-
}
|
|
2433
|
-
options?.onSuccess?.(data);
|
|
2434
|
-
},
|
|
2435
|
-
onError: options?.onError,
|
|
2436
|
-
onSettled: options?.onSettled
|
|
2437
|
-
});
|
|
2438
|
-
}
|
|
2439
|
-
useRemove(params, options) {
|
|
2440
|
-
const { collection } = params;
|
|
2441
|
-
return (0, import_react_query2.useMutation)({
|
|
2442
|
-
mutationFn: async (id) => {
|
|
2443
|
-
return await this.collectionClient.from(collection).remove(id);
|
|
2444
|
-
},
|
|
2445
|
-
onSuccess: (data) => {
|
|
2446
|
-
this.queryClient.invalidateQueries({
|
|
2447
|
-
queryKey: collectionKeys(collection).all
|
|
2448
|
-
});
|
|
2449
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2450
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2451
|
-
}
|
|
2452
|
-
options?.onSuccess?.(data);
|
|
2453
|
-
},
|
|
2454
|
-
onError: options?.onError,
|
|
2455
|
-
onSettled: options?.onSettled
|
|
2456
|
-
});
|
|
2457
|
-
}
|
|
2458
|
-
// ===== Cache Utilities =====
|
|
2459
|
-
invalidateQueries(collection, type) {
|
|
2460
|
-
const queryKey = type ? [collection, type] : [collection];
|
|
2461
|
-
return this.queryClient.invalidateQueries({ queryKey });
|
|
2462
|
-
}
|
|
2463
|
-
getQueryData(collection, type, idOrOptions, options) {
|
|
2464
|
-
if (type === "list") {
|
|
2465
|
-
return this.queryClient.getQueryData(
|
|
2466
|
-
collectionKeys(collection).list(idOrOptions)
|
|
2467
|
-
);
|
|
2468
|
-
}
|
|
2469
|
-
return this.queryClient.getQueryData(
|
|
2470
|
-
collectionKeys(collection).detail(idOrOptions, options)
|
|
2471
|
-
);
|
|
2472
|
-
}
|
|
2473
|
-
setQueryData(collection, type, dataOrId, dataOrOptions, options) {
|
|
2474
|
-
if (type === "list") {
|
|
2475
|
-
this.queryClient.setQueryData(
|
|
2476
|
-
collectionKeys(collection).list(dataOrOptions),
|
|
2477
|
-
dataOrId
|
|
2478
|
-
);
|
|
2479
|
-
} else {
|
|
2480
|
-
this.queryClient.setQueryData(
|
|
2481
|
-
collectionKeys(collection).detail(dataOrId, options),
|
|
2482
|
-
dataOrOptions
|
|
2483
|
-
);
|
|
2484
|
-
}
|
|
1764
|
+
/**
|
|
1765
|
+
* Atomically create or update a product together with its options,
|
|
1766
|
+
* option-values, and variants in a single transaction. Mirrors Shopify's
|
|
1767
|
+
* `productSet` shape and is the canonical write path for the MCP
|
|
1768
|
+
* `product-upsert` tool.
|
|
1769
|
+
*/
|
|
1770
|
+
upsert(params) {
|
|
1771
|
+
return this.request("/api/products/upsert", params);
|
|
2485
1772
|
}
|
|
2486
1773
|
};
|
|
2487
1774
|
|
|
2488
|
-
// src/core/
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
onSuccess: (data) => {
|
|
2494
|
-
onSuccessExtra?.(data);
|
|
2495
|
-
callbacks?.onSuccess?.(data);
|
|
2496
|
-
},
|
|
2497
|
-
onError: callbacks?.onError,
|
|
2498
|
-
onSettled: callbacks?.onSettled
|
|
2499
|
-
});
|
|
1775
|
+
// src/core/webhook/index.ts
|
|
1776
|
+
function isValidWebhookEvent(data) {
|
|
1777
|
+
if (typeof data !== "object" || data === null) return false;
|
|
1778
|
+
const obj = data;
|
|
1779
|
+
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
2500
1780
|
}
|
|
2501
|
-
var
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
}
|
|
2515
|
-
return this.customerAuth;
|
|
2516
|
-
}
|
|
2517
|
-
// ===== useCustomerMe =====
|
|
2518
|
-
useCustomerMe(options) {
|
|
2519
|
-
return (0, import_react_query3.useQuery)({
|
|
2520
|
-
queryKey: customerKeys.me(),
|
|
2521
|
-
queryFn: async () => {
|
|
2522
|
-
return await this.ensureCustomerAuth().me();
|
|
2523
|
-
},
|
|
2524
|
-
...options,
|
|
2525
|
-
enabled: (options?.enabled ?? true) && !!this.customerAuth?.isAuthenticated()
|
|
2526
|
-
});
|
|
2527
|
-
}
|
|
2528
|
-
// ===== Mutations =====
|
|
2529
|
-
useCustomerLogin(options) {
|
|
2530
|
-
return createMutation(
|
|
2531
|
-
(data) => this.ensureCustomerAuth().login(data),
|
|
2532
|
-
options,
|
|
2533
|
-
this.invalidateMe
|
|
2534
|
-
);
|
|
2535
|
-
}
|
|
2536
|
-
useCustomerRegister(options) {
|
|
2537
|
-
return createMutation(
|
|
2538
|
-
(data) => this.ensureCustomerAuth().register(data),
|
|
2539
|
-
options
|
|
2540
|
-
);
|
|
2541
|
-
}
|
|
2542
|
-
useCustomerLogout(options) {
|
|
2543
|
-
return (0, import_react_query3.useMutation)({
|
|
2544
|
-
mutationFn: async () => {
|
|
2545
|
-
this.ensureCustomerAuth().logout();
|
|
2546
|
-
},
|
|
2547
|
-
onSuccess: () => {
|
|
2548
|
-
this.queryClient.removeQueries({ queryKey: customerKeys.all });
|
|
2549
|
-
options?.onSuccess?.();
|
|
2550
|
-
},
|
|
2551
|
-
onError: options?.onError,
|
|
2552
|
-
onSettled: options?.onSettled
|
|
2553
|
-
});
|
|
2554
|
-
}
|
|
2555
|
-
useCustomerForgotPassword(options) {
|
|
2556
|
-
return createMutation(
|
|
2557
|
-
(email) => this.ensureCustomerAuth().forgotPassword(email).then(() => {
|
|
2558
|
-
}),
|
|
2559
|
-
options
|
|
2560
|
-
);
|
|
2561
|
-
}
|
|
2562
|
-
useCustomerResetPassword(options) {
|
|
2563
|
-
return createMutation(
|
|
2564
|
-
(data) => this.ensureCustomerAuth().resetPassword(data.token, data.password).then(() => {
|
|
2565
|
-
}),
|
|
2566
|
-
options
|
|
2567
|
-
);
|
|
2568
|
-
}
|
|
2569
|
-
useCustomerRefreshToken(options) {
|
|
2570
|
-
return createMutation(
|
|
2571
|
-
() => this.ensureCustomerAuth().refreshToken(),
|
|
2572
|
-
options,
|
|
2573
|
-
this.invalidateMe
|
|
2574
|
-
);
|
|
2575
|
-
}
|
|
2576
|
-
useCustomerUpdateProfile(options) {
|
|
2577
|
-
return createMutation(
|
|
2578
|
-
(data) => this.ensureCustomerAuth().updateProfile(data),
|
|
2579
|
-
options,
|
|
2580
|
-
this.invalidateMe
|
|
2581
|
-
);
|
|
2582
|
-
}
|
|
2583
|
-
useCustomerChangePassword(options) {
|
|
2584
|
-
return createMutation(
|
|
2585
|
-
(data) => this.ensureCustomerAuth().changePassword(data.currentPassword, data.newPassword).then(() => {
|
|
2586
|
-
}),
|
|
2587
|
-
options
|
|
2588
|
-
);
|
|
2589
|
-
}
|
|
2590
|
-
// ===== Customer Cache Utilities =====
|
|
2591
|
-
invalidateCustomerQueries() {
|
|
2592
|
-
return this.queryClient.invalidateQueries({ queryKey: customerKeys.all });
|
|
2593
|
-
}
|
|
2594
|
-
getCustomerData() {
|
|
2595
|
-
return this.queryClient.getQueryData(customerKeys.me());
|
|
2596
|
-
}
|
|
2597
|
-
setCustomerData(data) {
|
|
2598
|
-
this.queryClient.setQueryData(customerKeys.me(), data);
|
|
2599
|
-
}
|
|
2600
|
-
};
|
|
2601
|
-
|
|
2602
|
-
// src/core/query/query-hooks.ts
|
|
2603
|
-
var QueryHooks = class extends CollectionHooks {
|
|
2604
|
-
constructor(queryClient, collectionClient, customerAuth, commerceClient) {
|
|
2605
|
-
super(queryClient, collectionClient);
|
|
2606
|
-
// --- Customer hooks delegation ---
|
|
2607
|
-
this.useCustomerMe = (...args) => this._customer.useCustomerMe(...args);
|
|
2608
|
-
this.useCustomerLogin = (...args) => this._customer.useCustomerLogin(...args);
|
|
2609
|
-
this.useCustomerRegister = (...args) => this._customer.useCustomerRegister(...args);
|
|
2610
|
-
this.useCustomerLogout = (...args) => this._customer.useCustomerLogout(...args);
|
|
2611
|
-
this.useCustomerForgotPassword = (...args) => this._customer.useCustomerForgotPassword(...args);
|
|
2612
|
-
this.useCustomerResetPassword = (...args) => this._customer.useCustomerResetPassword(...args);
|
|
2613
|
-
this.useCustomerRefreshToken = (...args) => this._customer.useCustomerRefreshToken(...args);
|
|
2614
|
-
this.useCustomerUpdateProfile = (...args) => this._customer.useCustomerUpdateProfile(...args);
|
|
2615
|
-
this.useCustomerChangePassword = (...args) => this._customer.useCustomerChangePassword(...args);
|
|
2616
|
-
// --- Customer cache delegation ---
|
|
2617
|
-
this.invalidateCustomerQueries = () => this._customer.invalidateCustomerQueries();
|
|
2618
|
-
this.getCustomerData = () => this._customer.getCustomerData();
|
|
2619
|
-
this.setCustomerData = (data) => this._customer.setCustomerData(data);
|
|
2620
|
-
this._customer = new CustomerHooks(queryClient, customerAuth);
|
|
2621
|
-
this._commerce = commerceClient;
|
|
2622
|
-
}
|
|
2623
|
-
useProductListingGroupsQuery(params, options) {
|
|
2624
|
-
const queryOptions = params.options;
|
|
2625
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2626
|
-
return (0, import_react_query4.useQuery)({
|
|
2627
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2628
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2629
|
-
"/api/products/listing-groups/query",
|
|
2630
|
-
{ options: queryOptions }
|
|
2631
|
-
),
|
|
2632
|
-
...restOptions,
|
|
2633
|
-
...placeholderData !== void 0 && {
|
|
2634
|
-
placeholderData
|
|
2635
|
-
}
|
|
2636
|
-
});
|
|
2637
|
-
}
|
|
2638
|
-
useSuspenseProductListingGroupsQuery(params, options) {
|
|
2639
|
-
const queryOptions = params.options;
|
|
2640
|
-
return (0, import_react_query4.useSuspenseQuery)({
|
|
2641
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2642
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2643
|
-
"/api/products/listing-groups/query",
|
|
2644
|
-
{ options: queryOptions }
|
|
2645
|
-
),
|
|
2646
|
-
...options
|
|
2647
|
-
});
|
|
2648
|
-
}
|
|
2649
|
-
useInfiniteProductListingGroupsQuery(params, options) {
|
|
2650
|
-
const {
|
|
2651
|
-
options: queryOptions,
|
|
2652
|
-
pageSize = 20
|
|
2653
|
-
} = params;
|
|
2654
|
-
return (0, import_react_query4.useInfiniteQuery)({
|
|
2655
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2656
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2657
|
-
"/api/products/listing-groups/query",
|
|
2658
|
-
{
|
|
2659
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2660
|
-
}
|
|
2661
|
-
),
|
|
2662
|
-
initialPageParam: 1,
|
|
2663
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2664
|
-
...options
|
|
2665
|
-
});
|
|
2666
|
-
}
|
|
2667
|
-
useSuspenseInfiniteProductListingGroupsQuery(params, options) {
|
|
2668
|
-
const {
|
|
2669
|
-
options: queryOptions,
|
|
2670
|
-
pageSize = 20
|
|
2671
|
-
} = params;
|
|
2672
|
-
return (0, import_react_query4.useSuspenseInfiniteQuery)({
|
|
2673
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2674
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2675
|
-
"/api/products/listing-groups/query",
|
|
2676
|
-
{
|
|
2677
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2678
|
-
}
|
|
2679
|
-
),
|
|
2680
|
-
initialPageParam: 1,
|
|
2681
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2682
|
-
...options
|
|
2683
|
-
});
|
|
2684
|
-
}
|
|
2685
|
-
async prefetchProductListingGroupsQuery(params, options) {
|
|
2686
|
-
const queryOptions = params.options;
|
|
2687
|
-
return this.queryClient.prefetchQuery({
|
|
2688
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2689
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2690
|
-
"/api/products/listing-groups/query",
|
|
2691
|
-
{ options: queryOptions }
|
|
2692
|
-
),
|
|
2693
|
-
...options
|
|
2694
|
-
});
|
|
2695
|
-
}
|
|
2696
|
-
async prefetchInfiniteProductListingGroupsQuery(params, options) {
|
|
2697
|
-
const {
|
|
2698
|
-
options: queryOptions,
|
|
2699
|
-
pageSize = 20
|
|
2700
|
-
} = params;
|
|
2701
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2702
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2703
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2704
|
-
"/api/products/listing-groups/query",
|
|
2705
|
-
{
|
|
2706
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2707
|
-
}
|
|
2708
|
-
),
|
|
2709
|
-
initialPageParam: 1,
|
|
2710
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2711
|
-
pages: options?.pages ?? 1,
|
|
2712
|
-
staleTime: options?.staleTime
|
|
2713
|
-
});
|
|
2714
|
-
}
|
|
2715
|
-
useProductDetail(params, options) {
|
|
2716
|
-
const discriminator = "slug" in params ? params.slug : params.id;
|
|
2717
|
-
const enabled = options?.enabled !== false && Boolean(discriminator);
|
|
2718
|
-
return (0, import_react_query4.useQuery)({
|
|
2719
|
-
queryKey: productKeys.detail(params),
|
|
2720
|
-
queryFn: () => this._commerce.product.detail(params),
|
|
2721
|
-
enabled
|
|
2722
|
-
});
|
|
2723
|
-
}
|
|
2724
|
-
useProductDetailBySlug(slug, options) {
|
|
2725
|
-
return this.useProductDetail({ slug }, options);
|
|
2726
|
-
}
|
|
2727
|
-
useProductDetailById(id, options) {
|
|
2728
|
-
return this.useProductDetail({ id }, options);
|
|
1781
|
+
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
1782
|
+
function isRecord(value) {
|
|
1783
|
+
return typeof value === "object" && value !== null;
|
|
1784
|
+
}
|
|
1785
|
+
function hasString(value, key) {
|
|
1786
|
+
return typeof value[key] === "string";
|
|
1787
|
+
}
|
|
1788
|
+
function hasStringOrNumber(value, key) {
|
|
1789
|
+
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
1790
|
+
}
|
|
1791
|
+
function isCustomerPasswordResetWebhookEvent(event) {
|
|
1792
|
+
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
|
|
1793
|
+
return false;
|
|
2729
1794
|
}
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
if (!publishableKey) {
|
|
2738
|
-
throw createConfigError("publishableKey is required.");
|
|
1795
|
+
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
1796
|
+
}
|
|
1797
|
+
function createCustomerAuthWebhookHandler(handlers) {
|
|
1798
|
+
return async (event) => {
|
|
1799
|
+
if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
|
|
1800
|
+
await handlers.passwordReset(event.data, event);
|
|
1801
|
+
return;
|
|
2739
1802
|
}
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
return result.token ?? null;
|
|
2755
|
-
} catch {
|
|
2756
|
-
return null;
|
|
2757
|
-
}
|
|
2758
|
-
};
|
|
2759
|
-
const onRequestId = (id) => {
|
|
2760
|
-
this.lastRequestId = id;
|
|
2761
|
-
};
|
|
2762
|
-
this.commerce = new CommerceClient({
|
|
2763
|
-
publishableKey: this.config.publishableKey,
|
|
2764
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2765
|
-
onUnauthorized,
|
|
2766
|
-
onRequestId,
|
|
2767
|
-
customerAuth: this.customer.auth
|
|
2768
|
-
});
|
|
2769
|
-
this.community = new CommunityClient({
|
|
2770
|
-
publishableKey: this.config.publishableKey,
|
|
2771
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2772
|
-
onUnauthorized,
|
|
2773
|
-
onRequestId
|
|
2774
|
-
});
|
|
2775
|
-
const collectionClient = new CollectionClient(
|
|
2776
|
-
this.config.publishableKey,
|
|
2777
|
-
void 0,
|
|
2778
|
-
() => this.customer.auth.getToken(),
|
|
2779
|
-
onUnauthorized,
|
|
2780
|
-
onRequestId
|
|
2781
|
-
);
|
|
2782
|
-
this.collections = new ReadOnlyCollectionClient(
|
|
2783
|
-
this.config.publishableKey,
|
|
2784
|
-
void 0,
|
|
2785
|
-
() => this.customer.auth.getToken(),
|
|
2786
|
-
onUnauthorized,
|
|
2787
|
-
onRequestId
|
|
2788
|
-
);
|
|
2789
|
-
this.query = new QueryHooks(
|
|
2790
|
-
this.queryClient,
|
|
2791
|
-
collectionClient,
|
|
2792
|
-
this.customer.auth,
|
|
2793
|
-
this.commerce
|
|
2794
|
-
);
|
|
2795
|
-
}
|
|
2796
|
-
getState() {
|
|
2797
|
-
return { ...this.state };
|
|
2798
|
-
}
|
|
2799
|
-
getConfig() {
|
|
2800
|
-
return { ...this.config };
|
|
1803
|
+
await handlers.unhandled?.(event);
|
|
1804
|
+
};
|
|
1805
|
+
}
|
|
1806
|
+
async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
|
|
1807
|
+
const encoder = new TextEncoder();
|
|
1808
|
+
const key = await crypto.subtle.importKey(
|
|
1809
|
+
"raw",
|
|
1810
|
+
encoder.encode(secret),
|
|
1811
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1812
|
+
false,
|
|
1813
|
+
["verify"]
|
|
1814
|
+
);
|
|
1815
|
+
if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
|
|
1816
|
+
return false;
|
|
2801
1817
|
}
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
1818
|
+
const sigBytes = new Uint8Array(
|
|
1819
|
+
(signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
|
|
1820
|
+
);
|
|
1821
|
+
return crypto.subtle.verify(
|
|
1822
|
+
"HMAC",
|
|
1823
|
+
key,
|
|
1824
|
+
sigBytes,
|
|
1825
|
+
encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
|
|
1826
|
+
);
|
|
2805
1827
|
}
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
1828
|
+
function timestampIsFresh(timestamp, toleranceSeconds) {
|
|
1829
|
+
if (!/^\d+$/.test(timestamp)) return false;
|
|
1830
|
+
const timestampMs = Number(timestamp);
|
|
1831
|
+
if (!Number.isFinite(timestampMs)) return false;
|
|
1832
|
+
const skewMs = Math.abs(Date.now() - timestampMs);
|
|
1833
|
+
return skewMs <= toleranceSeconds * 1e3;
|
|
1834
|
+
}
|
|
1835
|
+
async function handleWebhook(request, handler, options) {
|
|
1836
|
+
try {
|
|
1837
|
+
const rawBody = await request.text();
|
|
1838
|
+
if (options?.secret) {
|
|
1839
|
+
const signature = request.headers.get("x-webhook-signature") || "";
|
|
1840
|
+
const timestamp = request.headers.get("x-webhook-timestamp") || "";
|
|
1841
|
+
const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
|
|
1842
|
+
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
1843
|
+
const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
|
|
1844
|
+
rawBody,
|
|
1845
|
+
options.secret,
|
|
1846
|
+
signature,
|
|
1847
|
+
timestamp,
|
|
1848
|
+
deliveryId
|
|
2814
1849
|
);
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
1850
|
+
if (!valid) {
|
|
1851
|
+
return new Response(
|
|
1852
|
+
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
1853
|
+
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
1856
|
+
} else {
|
|
1857
|
+
console.warn(
|
|
1858
|
+
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
2822
1859
|
);
|
|
2823
1860
|
}
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
secretKey: this.config.secretKey,
|
|
2836
|
-
onRequestId
|
|
2837
|
-
};
|
|
2838
|
-
this.commerce = new ServerCommerceClient(serverOptions);
|
|
2839
|
-
const communityClient = new CommunityClient(serverOptions);
|
|
2840
|
-
const moderationApi = new ModerationApi(serverOptions);
|
|
2841
|
-
this.community = Object.assign(communityClient, {
|
|
2842
|
-
moderation: {
|
|
2843
|
-
banCustomer: moderationApi.banCustomer.bind(moderationApi),
|
|
2844
|
-
unbanCustomer: moderationApi.unbanCustomer.bind(moderationApi)
|
|
2845
|
-
}
|
|
2846
|
-
});
|
|
2847
|
-
this.collections = new ServerCollectionClient(
|
|
2848
|
-
this.config.publishableKey,
|
|
2849
|
-
this.config.secretKey,
|
|
2850
|
-
void 0,
|
|
2851
|
-
void 0,
|
|
2852
|
-
onRequestId
|
|
1861
|
+
const body = JSON.parse(rawBody);
|
|
1862
|
+
if (!isValidWebhookEvent(body)) {
|
|
1863
|
+
return new Response(
|
|
1864
|
+
JSON.stringify({ error: "Invalid webhook event format" }),
|
|
1865
|
+
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
await handler(body);
|
|
1869
|
+
return new Response(
|
|
1870
|
+
JSON.stringify({ success: true, message: "Webhook processed" }),
|
|
1871
|
+
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
2853
1872
|
);
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
getConfig() {
|
|
2861
|
-
const { secretKey: _, ...safeConfig } = this.config;
|
|
2862
|
-
return safeConfig;
|
|
1873
|
+
} catch (error) {
|
|
1874
|
+
console.error("Webhook processing error:", error);
|
|
1875
|
+
return new Response(JSON.stringify({ error: "Internal server error" }), {
|
|
1876
|
+
status: 500,
|
|
1877
|
+
headers: { "Content-Type": "application/json" }
|
|
1878
|
+
});
|
|
2863
1879
|
}
|
|
2864
|
-
};
|
|
2865
|
-
function createServerClient(options) {
|
|
2866
|
-
return new ServerClient(options);
|
|
2867
1880
|
}
|
|
1881
|
+
function createTypedWebhookHandler(collection, handler) {
|
|
1882
|
+
return async (event) => {
|
|
1883
|
+
if (event.collection !== collection) {
|
|
1884
|
+
throw new Error(
|
|
1885
|
+
`Expected collection "${collection}", got "${event.collection}"`
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
return handler(event);
|
|
1889
|
+
};
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
// src/core/collection/const.ts
|
|
1893
|
+
var INTERNAL_COLLECTIONS = [
|
|
1894
|
+
"users",
|
|
1895
|
+
"payload-kv",
|
|
1896
|
+
"payload-locked-documents",
|
|
1897
|
+
"payload-preferences",
|
|
1898
|
+
"payload-migrations",
|
|
1899
|
+
"payload-folders",
|
|
1900
|
+
"field-configs",
|
|
1901
|
+
"system-media",
|
|
1902
|
+
"track-assets",
|
|
1903
|
+
"audiences",
|
|
1904
|
+
"email-logs",
|
|
1905
|
+
"api-usage",
|
|
1906
|
+
"tenant-analytics-daily",
|
|
1907
|
+
"tenant-web-analytics-config",
|
|
1908
|
+
"analytics-event-schemas",
|
|
1909
|
+
"subscriptions",
|
|
1910
|
+
"billing-history",
|
|
1911
|
+
"inventory-reservations",
|
|
1912
|
+
"order-status-logs",
|
|
1913
|
+
"api-keys",
|
|
1914
|
+
"personal-access-tokens",
|
|
1915
|
+
"tenant-entitlements",
|
|
1916
|
+
"tenant-purge-jobs",
|
|
1917
|
+
"direct-upload-sessions",
|
|
1918
|
+
"webhook-events",
|
|
1919
|
+
"webhook-deliveries",
|
|
1920
|
+
"audit-logs",
|
|
1921
|
+
"plans",
|
|
1922
|
+
"webhooks",
|
|
1923
|
+
"event-registrations"
|
|
1924
|
+
];
|
|
1925
|
+
var COLLECTIONS = [
|
|
1926
|
+
"tenants",
|
|
1927
|
+
"tenant-metadata",
|
|
1928
|
+
"tenant-logos",
|
|
1929
|
+
"products",
|
|
1930
|
+
"product-variants",
|
|
1931
|
+
"product-options",
|
|
1932
|
+
"product-option-values",
|
|
1933
|
+
"product-categories",
|
|
1934
|
+
"product-tags",
|
|
1935
|
+
"product-collections",
|
|
1936
|
+
"brands",
|
|
1937
|
+
"brand-logos",
|
|
1938
|
+
"orders",
|
|
1939
|
+
"order-items",
|
|
1940
|
+
"returns",
|
|
1941
|
+
"return-items",
|
|
1942
|
+
"fulfillments",
|
|
1943
|
+
"fulfillment-items",
|
|
1944
|
+
"transactions",
|
|
1945
|
+
"customers",
|
|
1946
|
+
"customer-profiles",
|
|
1947
|
+
"customer-profile-lists",
|
|
1948
|
+
"customer-addresses",
|
|
1949
|
+
"carts",
|
|
1950
|
+
"cart-items",
|
|
1951
|
+
"discounts",
|
|
1952
|
+
"shipping-policies",
|
|
1953
|
+
"shipping-zones",
|
|
1954
|
+
"documents",
|
|
1955
|
+
"document-categories",
|
|
1956
|
+
"document-types",
|
|
1957
|
+
"articles",
|
|
1958
|
+
"article-authors",
|
|
1959
|
+
"article-categories",
|
|
1960
|
+
"article-tags",
|
|
1961
|
+
"playlists",
|
|
1962
|
+
"playlist-categories",
|
|
1963
|
+
"playlist-tags",
|
|
1964
|
+
"tracks",
|
|
1965
|
+
"track-categories",
|
|
1966
|
+
"track-tags",
|
|
1967
|
+
"galleries",
|
|
1968
|
+
"gallery-categories",
|
|
1969
|
+
"gallery-tags",
|
|
1970
|
+
"gallery-items",
|
|
1971
|
+
"links",
|
|
1972
|
+
"link-categories",
|
|
1973
|
+
"link-tags",
|
|
1974
|
+
"canvases",
|
|
1975
|
+
"canvas-node-types",
|
|
1976
|
+
"canvas-edge-types",
|
|
1977
|
+
"canvas-categories",
|
|
1978
|
+
"canvas-tags",
|
|
1979
|
+
"canvas-nodes",
|
|
1980
|
+
"canvas-edges",
|
|
1981
|
+
"videos",
|
|
1982
|
+
"video-categories",
|
|
1983
|
+
"video-tags",
|
|
1984
|
+
"live-streams",
|
|
1985
|
+
"images",
|
|
1986
|
+
"forms",
|
|
1987
|
+
"form-submissions",
|
|
1988
|
+
// Community
|
|
1989
|
+
"posts",
|
|
1990
|
+
"comments",
|
|
1991
|
+
"reactions",
|
|
1992
|
+
"reaction-types",
|
|
1993
|
+
"bookmarks",
|
|
1994
|
+
"post-categories",
|
|
1995
|
+
// Events
|
|
1996
|
+
"event-calendars",
|
|
1997
|
+
"events",
|
|
1998
|
+
"event-categories",
|
|
1999
|
+
"event-occurrences",
|
|
2000
|
+
"event-tags"
|
|
2001
|
+
];
|
|
2002
|
+
var SERVER_ONLY_COLLECTIONS = [
|
|
2003
|
+
"customer-groups",
|
|
2004
|
+
"reports",
|
|
2005
|
+
"community-bans"
|
|
2006
|
+
];
|
|
2007
|
+
var SERVER_COLLECTIONS = [
|
|
2008
|
+
...COLLECTIONS,
|
|
2009
|
+
...SERVER_ONLY_COLLECTIONS
|
|
2010
|
+
];
|
|
2868
2011
|
|
|
2869
2012
|
// src/core/query/realtime.ts
|
|
2870
2013
|
var INITIAL_RECONNECT_DELAY = 1e3;
|
|
@@ -2985,150 +2128,40 @@ var RealtimeConnection = class {
|
|
|
2985
2128
|
currentEvent = "";
|
|
2986
2129
|
currentData = "";
|
|
2987
2130
|
}
|
|
2988
|
-
}
|
|
2989
|
-
}
|
|
2990
|
-
} catch {
|
|
2991
|
-
} finally {
|
|
2992
|
-
reader.releaseLock();
|
|
2993
|
-
this._connected = false;
|
|
2994
|
-
if (!signal.aborted) {
|
|
2995
|
-
this.scheduleReconnect();
|
|
2996
|
-
}
|
|
2997
|
-
}
|
|
2998
|
-
}
|
|
2999
|
-
scheduleReconnect() {
|
|
3000
|
-
if (this.reconnectTimer) return;
|
|
3001
|
-
const delay2 = Math.min(
|
|
3002
|
-
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
3003
|
-
MAX_RECONNECT_DELAY
|
|
3004
|
-
);
|
|
3005
|
-
this.reconnectAttempt++;
|
|
3006
|
-
this.reconnectTimer = setTimeout(() => {
|
|
3007
|
-
this.reconnectTimer = null;
|
|
3008
|
-
this.abortController = new AbortController();
|
|
3009
|
-
this.startStream(this.abortController.signal);
|
|
3010
|
-
}, delay2);
|
|
3011
|
-
}
|
|
3012
|
-
};
|
|
3013
|
-
|
|
3014
|
-
// src/core/webhook/index.ts
|
|
3015
|
-
function isValidWebhookEvent(data) {
|
|
3016
|
-
if (typeof data !== "object" || data === null) return false;
|
|
3017
|
-
const obj = data;
|
|
3018
|
-
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
3019
|
-
}
|
|
3020
|
-
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
3021
|
-
function isRecord(value) {
|
|
3022
|
-
return typeof value === "object" && value !== null;
|
|
3023
|
-
}
|
|
3024
|
-
function hasString(value, key) {
|
|
3025
|
-
return typeof value[key] === "string";
|
|
3026
|
-
}
|
|
3027
|
-
function hasStringOrNumber(value, key) {
|
|
3028
|
-
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
3029
|
-
}
|
|
3030
|
-
function isCustomerPasswordResetWebhookEvent(event) {
|
|
3031
|
-
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
|
|
3032
|
-
return false;
|
|
3033
|
-
}
|
|
3034
|
-
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
3035
|
-
}
|
|
3036
|
-
function createCustomerAuthWebhookHandler(handlers) {
|
|
3037
|
-
return async (event) => {
|
|
3038
|
-
if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
|
|
3039
|
-
await handlers.passwordReset(event.data, event);
|
|
3040
|
-
return;
|
|
3041
|
-
}
|
|
3042
|
-
await handlers.unhandled?.(event);
|
|
3043
|
-
};
|
|
3044
|
-
}
|
|
3045
|
-
async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
|
|
3046
|
-
const encoder = new TextEncoder();
|
|
3047
|
-
const key = await crypto.subtle.importKey(
|
|
3048
|
-
"raw",
|
|
3049
|
-
encoder.encode(secret),
|
|
3050
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
3051
|
-
false,
|
|
3052
|
-
["verify"]
|
|
3053
|
-
);
|
|
3054
|
-
if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
|
|
3055
|
-
return false;
|
|
3056
|
-
}
|
|
3057
|
-
const sigBytes = new Uint8Array(
|
|
3058
|
-
(signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
|
|
3059
|
-
);
|
|
3060
|
-
return crypto.subtle.verify(
|
|
3061
|
-
"HMAC",
|
|
3062
|
-
key,
|
|
3063
|
-
sigBytes,
|
|
3064
|
-
encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
|
|
3065
|
-
);
|
|
3066
|
-
}
|
|
3067
|
-
function timestampIsFresh(timestamp, toleranceSeconds) {
|
|
3068
|
-
if (!/^\d+$/.test(timestamp)) return false;
|
|
3069
|
-
const timestampMs = Number(timestamp);
|
|
3070
|
-
if (!Number.isFinite(timestampMs)) return false;
|
|
3071
|
-
const skewMs = Math.abs(Date.now() - timestampMs);
|
|
3072
|
-
return skewMs <= toleranceSeconds * 1e3;
|
|
3073
|
-
}
|
|
3074
|
-
async function handleWebhook(request, handler, options) {
|
|
3075
|
-
try {
|
|
3076
|
-
const rawBody = await request.text();
|
|
3077
|
-
if (options?.secret) {
|
|
3078
|
-
const signature = request.headers.get("x-webhook-signature") || "";
|
|
3079
|
-
const timestamp = request.headers.get("x-webhook-timestamp") || "";
|
|
3080
|
-
const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
|
|
3081
|
-
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
3082
|
-
const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
|
|
3083
|
-
rawBody,
|
|
3084
|
-
options.secret,
|
|
3085
|
-
signature,
|
|
3086
|
-
timestamp,
|
|
3087
|
-
deliveryId
|
|
3088
|
-
);
|
|
3089
|
-
if (!valid) {
|
|
3090
|
-
return new Response(
|
|
3091
|
-
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
3092
|
-
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
3093
|
-
);
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
} catch {
|
|
2134
|
+
} finally {
|
|
2135
|
+
reader.releaseLock();
|
|
2136
|
+
this._connected = false;
|
|
2137
|
+
if (!signal.aborted) {
|
|
2138
|
+
this.scheduleReconnect();
|
|
3094
2139
|
}
|
|
3095
|
-
} else {
|
|
3096
|
-
console.warn(
|
|
3097
|
-
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
3098
|
-
);
|
|
3099
|
-
}
|
|
3100
|
-
const body = JSON.parse(rawBody);
|
|
3101
|
-
if (!isValidWebhookEvent(body)) {
|
|
3102
|
-
return new Response(
|
|
3103
|
-
JSON.stringify({ error: "Invalid webhook event format" }),
|
|
3104
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
3105
|
-
);
|
|
3106
2140
|
}
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
2141
|
+
}
|
|
2142
|
+
scheduleReconnect() {
|
|
2143
|
+
if (this.reconnectTimer) return;
|
|
2144
|
+
const delay2 = Math.min(
|
|
2145
|
+
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
2146
|
+
MAX_RECONNECT_DELAY
|
|
3111
2147
|
);
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
});
|
|
2148
|
+
this.reconnectAttempt++;
|
|
2149
|
+
this.reconnectTimer = setTimeout(() => {
|
|
2150
|
+
this.reconnectTimer = null;
|
|
2151
|
+
this.abortController = new AbortController();
|
|
2152
|
+
this.startStream(this.abortController.signal);
|
|
2153
|
+
}, delay2);
|
|
3118
2154
|
}
|
|
3119
|
-
}
|
|
3120
|
-
function createTypedWebhookHandler(collection, handler) {
|
|
3121
|
-
return async (event) => {
|
|
3122
|
-
if (event.collection !== collection) {
|
|
3123
|
-
throw new Error(
|
|
3124
|
-
`Expected collection "${collection}", got "${event.collection}"`
|
|
3125
|
-
);
|
|
3126
|
-
}
|
|
3127
|
-
return handler(event);
|
|
3128
|
-
};
|
|
3129
|
-
}
|
|
2155
|
+
};
|
|
3130
2156
|
|
|
3131
2157
|
// src/utils/ecommerce.ts
|
|
2158
|
+
var ProductSelectionCodecError = class extends Error {
|
|
2159
|
+
constructor(message) {
|
|
2160
|
+
super(message);
|
|
2161
|
+
this.code = "ambiguous_product_selection_query";
|
|
2162
|
+
this.name = "ProductSelectionCodecError";
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
3132
2165
|
function getRelationID(value) {
|
|
3133
2166
|
if (typeof value === "string") return value;
|
|
3134
2167
|
if (typeof value === "number") return String(value);
|
|
@@ -3175,10 +2208,11 @@ function getFirstAvailableVariantPrimaryImage(variants) {
|
|
|
3175
2208
|
}
|
|
3176
2209
|
return null;
|
|
3177
2210
|
}
|
|
3178
|
-
function normalizeOptionValue(value, fallbackOptionId) {
|
|
2211
|
+
function normalizeOptionValue(value, fallbackOptionId, fallbackOptionSlug) {
|
|
3179
2212
|
return {
|
|
3180
2213
|
id: String(value.id),
|
|
3181
2214
|
optionId: getRelationID(value.option) ?? fallbackOptionId,
|
|
2215
|
+
optionSlug: fallbackOptionSlug,
|
|
3182
2216
|
label: value.value || value.slug || String(value.id),
|
|
3183
2217
|
slug: value.slug ?? null,
|
|
3184
2218
|
swatchColor: value.swatchColor ?? null,
|
|
@@ -3187,20 +2221,26 @@ function normalizeOptionValue(value, fallbackOptionId) {
|
|
|
3187
2221
|
order: value._order ?? value["_product-option-values_values_order"] ?? ""
|
|
3188
2222
|
};
|
|
3189
2223
|
}
|
|
3190
|
-
function normalizeVariantOptionValues(variant, valueToOptionId, optionIds) {
|
|
2224
|
+
function normalizeVariantOptionValues(variant, optionById, valueToOptionId, optionIds) {
|
|
3191
2225
|
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
2226
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
3192
2227
|
for (const rawValue of Array.isArray(variant.optionValues) ? variant.optionValues : []) {
|
|
3193
2228
|
const valueId = getRelationID(rawValue);
|
|
3194
2229
|
if (!valueId) continue;
|
|
3195
2230
|
const optionId = valueToOptionId.get(valueId) ?? (isProductOptionValueDoc(rawValue) ? getRelationID(rawValue.option) : void 0);
|
|
3196
2231
|
if (!optionId || optionValueByOptionId.has(optionId)) continue;
|
|
3197
2232
|
optionValueByOptionId.set(optionId, valueId);
|
|
2233
|
+
const optionSlug = optionById.get(optionId)?.slug;
|
|
2234
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
2235
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
2236
|
+
}
|
|
3198
2237
|
}
|
|
3199
2238
|
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
3200
2239
|
return {
|
|
3201
2240
|
id: String(variant.id),
|
|
3202
2241
|
optionValueIds,
|
|
3203
2242
|
optionValueByOptionId,
|
|
2243
|
+
optionValueByOptionSlug,
|
|
3204
2244
|
source: variant
|
|
3205
2245
|
};
|
|
3206
2246
|
}
|
|
@@ -3210,17 +2250,20 @@ function buildProductOptionMatrix({
|
|
|
3210
2250
|
}) {
|
|
3211
2251
|
const normalizedOptions = options.map((option) => {
|
|
3212
2252
|
const valuesById = /* @__PURE__ */ new Map();
|
|
2253
|
+
const optionSlug = option.slug ?? String(option.id);
|
|
3213
2254
|
for (const rawValue of option.values?.docs ?? []) {
|
|
3214
2255
|
if (!isProductOptionValueDoc(rawValue)) continue;
|
|
3215
2256
|
const normalizedValue = normalizeOptionValue(
|
|
3216
2257
|
rawValue,
|
|
3217
|
-
String(option.id)
|
|
2258
|
+
String(option.id),
|
|
2259
|
+
optionSlug
|
|
3218
2260
|
);
|
|
3219
2261
|
valuesById.set(normalizedValue.id, normalizedValue);
|
|
3220
2262
|
}
|
|
3221
2263
|
return {
|
|
3222
2264
|
id: String(option.id),
|
|
3223
2265
|
title: option.title ?? String(option.id),
|
|
2266
|
+
slug: optionSlug,
|
|
3224
2267
|
order: option._order ?? option["_product-options_options_order"] ?? "",
|
|
3225
2268
|
values: Array.from(valuesById.values()).sort(
|
|
3226
2269
|
(left, right) => compareOrder(left.order, right.order)
|
|
@@ -3230,24 +2273,113 @@ function buildProductOptionMatrix({
|
|
|
3230
2273
|
const optionById = new Map(
|
|
3231
2274
|
normalizedOptions.map((option) => [option.id, option])
|
|
3232
2275
|
);
|
|
2276
|
+
const optionBySlug = new Map(
|
|
2277
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
2278
|
+
);
|
|
3233
2279
|
const valueById = /* @__PURE__ */ new Map();
|
|
3234
2280
|
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
2281
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
3235
2282
|
for (const option of normalizedOptions) {
|
|
3236
2283
|
for (const value of option.values) {
|
|
3237
2284
|
valueById.set(value.id, value);
|
|
3238
2285
|
valueToOptionId.set(value.id, option.id);
|
|
2286
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
3239
2287
|
}
|
|
3240
2288
|
}
|
|
3241
2289
|
const optionIds = normalizedOptions.map((option) => option.id);
|
|
2290
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
3242
2291
|
const normalizedVariants = variants.map(
|
|
3243
|
-
(variant) => normalizeVariantOptionValues(
|
|
2292
|
+
(variant) => normalizeVariantOptionValues(
|
|
2293
|
+
variant,
|
|
2294
|
+
optionById,
|
|
2295
|
+
valueToOptionId,
|
|
2296
|
+
optionIds
|
|
2297
|
+
)
|
|
2298
|
+
);
|
|
2299
|
+
return {
|
|
2300
|
+
options: normalizedOptions,
|
|
2301
|
+
optionIds,
|
|
2302
|
+
optionSlugs,
|
|
2303
|
+
optionById,
|
|
2304
|
+
optionBySlug,
|
|
2305
|
+
valueById,
|
|
2306
|
+
valueToOptionId,
|
|
2307
|
+
valueToOptionSlug,
|
|
2308
|
+
variants: normalizedVariants
|
|
2309
|
+
};
|
|
2310
|
+
}
|
|
2311
|
+
function matrixOrder(index) {
|
|
2312
|
+
return String(index).padStart(6, "0");
|
|
2313
|
+
}
|
|
2314
|
+
function buildProductOptionMatrixFromDetail(detail) {
|
|
2315
|
+
const normalizedOptions = detail.options.map((option, optionIndex) => ({
|
|
2316
|
+
id: String(option.id),
|
|
2317
|
+
title: option.title || String(option.id),
|
|
2318
|
+
slug: option.slug,
|
|
2319
|
+
order: matrixOrder(optionIndex),
|
|
2320
|
+
values: option.values.map((value, valueIndex) => ({
|
|
2321
|
+
id: String(value.id),
|
|
2322
|
+
optionId: String(option.id),
|
|
2323
|
+
optionSlug: option.slug,
|
|
2324
|
+
label: value.value || value.slug || String(value.id),
|
|
2325
|
+
slug: value.slug,
|
|
2326
|
+
swatchColor: value.swatchColor ?? null,
|
|
2327
|
+
thumbnail: value.thumbnail ?? null,
|
|
2328
|
+
images: value.images ?? null,
|
|
2329
|
+
order: matrixOrder(valueIndex)
|
|
2330
|
+
}))
|
|
2331
|
+
}));
|
|
2332
|
+
const optionById = new Map(
|
|
2333
|
+
normalizedOptions.map((option) => [option.id, option])
|
|
2334
|
+
);
|
|
2335
|
+
const optionBySlug = new Map(
|
|
2336
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
3244
2337
|
);
|
|
2338
|
+
const valueById = /* @__PURE__ */ new Map();
|
|
2339
|
+
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
2340
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
2341
|
+
for (const option of normalizedOptions) {
|
|
2342
|
+
for (const value of option.values) {
|
|
2343
|
+
valueById.set(value.id, value);
|
|
2344
|
+
valueToOptionId.set(value.id, option.id);
|
|
2345
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
const optionIds = normalizedOptions.map((option) => option.id);
|
|
2349
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
2350
|
+
const normalizedVariants = detail.variants.map((variant) => {
|
|
2351
|
+
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
2352
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
2353
|
+
for (const rawValue of variant.optionValues) {
|
|
2354
|
+
const optionId = String(rawValue.optionId);
|
|
2355
|
+
const valueId = String(rawValue.valueId);
|
|
2356
|
+
const optionSlug = rawValue.optionSlug;
|
|
2357
|
+
if (!optionById.has(optionId)) continue;
|
|
2358
|
+
if (valueToOptionId.get(valueId) !== optionId) continue;
|
|
2359
|
+
if (optionValueByOptionId.has(optionId)) continue;
|
|
2360
|
+
optionValueByOptionId.set(optionId, valueId);
|
|
2361
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
2362
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
2366
|
+
return {
|
|
2367
|
+
id: String(variant.id),
|
|
2368
|
+
optionValueIds,
|
|
2369
|
+
optionValueByOptionId,
|
|
2370
|
+
optionValueByOptionSlug,
|
|
2371
|
+
source: variant
|
|
2372
|
+
};
|
|
2373
|
+
});
|
|
3245
2374
|
return {
|
|
3246
2375
|
options: normalizedOptions,
|
|
3247
2376
|
optionIds,
|
|
2377
|
+
optionSlugs,
|
|
3248
2378
|
optionById,
|
|
2379
|
+
optionBySlug,
|
|
3249
2380
|
valueById,
|
|
3250
2381
|
valueToOptionId,
|
|
2382
|
+
valueToOptionSlug,
|
|
3251
2383
|
variants: normalizedVariants
|
|
3252
2384
|
};
|
|
3253
2385
|
}
|
|
@@ -3280,7 +2412,7 @@ function getAvailableOptionValues(matrix, optionId, selectedValueIds) {
|
|
|
3280
2412
|
)
|
|
3281
2413
|
);
|
|
3282
2414
|
const availableValueIds = new Set(
|
|
3283
|
-
matchingVariants.map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
2415
|
+
matchingVariants.filter((variant) => variant.source.isActive !== false).map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
3284
2416
|
);
|
|
3285
2417
|
return option.values.filter((value) => availableValueIds.has(value.id));
|
|
3286
2418
|
}
|
|
@@ -3295,6 +2427,659 @@ function resolveVariantForSelection(matrix, selectedValueIds) {
|
|
|
3295
2427
|
)
|
|
3296
2428
|
);
|
|
3297
2429
|
}
|
|
2430
|
+
function getVariantSelection(matrix, variantId) {
|
|
2431
|
+
if (variantId == null) return void 0;
|
|
2432
|
+
const id = String(variantId);
|
|
2433
|
+
return matrix.variants.find((variant) => variant.id === id);
|
|
2434
|
+
}
|
|
2435
|
+
function hasExplicitSelection(selection) {
|
|
2436
|
+
return Boolean(
|
|
2437
|
+
selection.variantId != null || selection.search || selection.valueIds || Object.keys(selection.byOptionId ?? {}).length > 0 || Object.keys(selection.byOptionSlug ?? {}).length > 0
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2440
|
+
function assignSelectedValue(matrix, selectedByOptionId, optionId, valueId) {
|
|
2441
|
+
if (valueId == null) return false;
|
|
2442
|
+
const normalizedValueId = String(valueId);
|
|
2443
|
+
if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return false;
|
|
2444
|
+
selectedByOptionId.set(optionId, normalizedValueId);
|
|
2445
|
+
return true;
|
|
2446
|
+
}
|
|
2447
|
+
function assignSelectedValueSlugByOptionId(matrix, selectedByOptionId, optionId, valueSlug) {
|
|
2448
|
+
if (!valueSlug) return false;
|
|
2449
|
+
const option = matrix.optionById.get(optionId);
|
|
2450
|
+
if (!option) return false;
|
|
2451
|
+
const values = option.values.filter(
|
|
2452
|
+
(candidate) => candidate.slug === valueSlug
|
|
2453
|
+
);
|
|
2454
|
+
if (values.length > 1) {
|
|
2455
|
+
throw new ProductSelectionCodecError(
|
|
2456
|
+
`Ambiguous product selection value slug "${valueSlug}" for option "${optionId}". Use opt.<optionId>=<valueId>.`
|
|
2457
|
+
);
|
|
2458
|
+
}
|
|
2459
|
+
const value = values[0];
|
|
2460
|
+
if (!value) return false;
|
|
2461
|
+
selectedByOptionId.set(optionId, value.id);
|
|
2462
|
+
return true;
|
|
2463
|
+
}
|
|
2464
|
+
function assignSelectedValueSlugByOptionSlug(matrix, selectedByOptionId, optionSlug, valueSlug) {
|
|
2465
|
+
if (!valueSlug) return false;
|
|
2466
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
2467
|
+
if (!option) return false;
|
|
2468
|
+
const values = option.values.filter(
|
|
2469
|
+
(candidate) => candidate.slug === valueSlug
|
|
2470
|
+
);
|
|
2471
|
+
if (values.length > 1) {
|
|
2472
|
+
throw new ProductSelectionCodecError(
|
|
2473
|
+
`Ambiguous product selection value slug "${valueSlug}" for option "${optionSlug}". Use opt.<optionId>=<valueId>.`
|
|
2474
|
+
);
|
|
2475
|
+
}
|
|
2476
|
+
const value = values[0];
|
|
2477
|
+
if (!value) return false;
|
|
2478
|
+
selectedByOptionId.set(option.id, value.id);
|
|
2479
|
+
return true;
|
|
2480
|
+
}
|
|
2481
|
+
function toSearchParams(search) {
|
|
2482
|
+
if (!search) return new URLSearchParams();
|
|
2483
|
+
if (search instanceof URLSearchParams) return new URLSearchParams(search);
|
|
2484
|
+
if (search instanceof URL) return new URLSearchParams(search.searchParams);
|
|
2485
|
+
const trimmed = search.trim();
|
|
2486
|
+
if (!trimmed) return new URLSearchParams();
|
|
2487
|
+
try {
|
|
2488
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
2489
|
+
return new URL(trimmed).searchParams;
|
|
2490
|
+
}
|
|
2491
|
+
} catch {
|
|
2492
|
+
return new URLSearchParams();
|
|
2493
|
+
}
|
|
2494
|
+
return new URLSearchParams(
|
|
2495
|
+
trimmed.startsWith("?") ? trimmed.slice(1) : trimmed
|
|
2496
|
+
);
|
|
2497
|
+
}
|
|
2498
|
+
function slugLike(value) {
|
|
2499
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2500
|
+
}
|
|
2501
|
+
function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
2502
|
+
const knownSelectionKeys = /* @__PURE__ */ new Set();
|
|
2503
|
+
const hasVariantParam = params.has("variant");
|
|
2504
|
+
const hasOptionParams = Array.from(params.keys()).some(
|
|
2505
|
+
(key) => key.startsWith("opt.")
|
|
2506
|
+
);
|
|
2507
|
+
if (hasVariantParam && hasOptionParams) {
|
|
2508
|
+
throw new ProductSelectionCodecError(
|
|
2509
|
+
"Product selection URL cannot mix variant=<variantId> with opt.<optionId>=<valueId> params."
|
|
2510
|
+
);
|
|
2511
|
+
}
|
|
2512
|
+
for (const option of matrix.options) {
|
|
2513
|
+
knownSelectionKeys.add(slugLike(option.slug));
|
|
2514
|
+
knownSelectionKeys.add(slugLike(option.title));
|
|
2515
|
+
for (const value of option.values) {
|
|
2516
|
+
if (value.slug) knownSelectionKeys.add(slugLike(value.slug));
|
|
2517
|
+
}
|
|
2518
|
+
}
|
|
2519
|
+
for (const [key, value] of params.entries()) {
|
|
2520
|
+
if (key.startsWith("opt.")) {
|
|
2521
|
+
const optionToken = key.slice(4);
|
|
2522
|
+
if (!optionToken || !matrix.optionBySlug.has(optionToken) && !matrix.optionById.has(optionToken)) {
|
|
2523
|
+
throw new ProductSelectionCodecError(
|
|
2524
|
+
`Unknown product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
|
|
2525
|
+
);
|
|
2526
|
+
}
|
|
2527
|
+
if (!value) {
|
|
2528
|
+
throw new ProductSelectionCodecError(
|
|
2529
|
+
`Product selection query parameter "${key}" requires a value ID or compatibility value slug.`
|
|
2530
|
+
);
|
|
2531
|
+
}
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
if (key === "variant") {
|
|
2535
|
+
if (!value) {
|
|
2536
|
+
throw new ProductSelectionCodecError(
|
|
2537
|
+
'Product selection query parameter "variant" requires a variant ID.'
|
|
2538
|
+
);
|
|
2539
|
+
}
|
|
2540
|
+
if (!getVariantSelection(matrix, value)) {
|
|
2541
|
+
throw new ProductSelectionCodecError(
|
|
2542
|
+
`Unknown product selection variant "${value}".`
|
|
2543
|
+
);
|
|
2544
|
+
}
|
|
2545
|
+
continue;
|
|
2546
|
+
}
|
|
2547
|
+
const keyToken = slugLike(key);
|
|
2548
|
+
if (knownSelectionKeys.has(keyToken) || value === "" && knownSelectionKeys.has(keyToken)) {
|
|
2549
|
+
throw new ProductSelectionCodecError(
|
|
2550
|
+
`Ambiguous product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
|
|
2551
|
+
);
|
|
2552
|
+
}
|
|
2553
|
+
}
|
|
2554
|
+
}
|
|
2555
|
+
function emitCompatibilityOptionIdParam(options, event) {
|
|
2556
|
+
try {
|
|
2557
|
+
if (options?.onCompatibilityOptionIdParam) {
|
|
2558
|
+
options.onCompatibilityOptionIdParam(event);
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
2561
|
+
options?.onLegacyOptionIdParam?.(event);
|
|
2562
|
+
} catch {
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
function assignSearchSelection(matrix, selectedByOptionId, search, options) {
|
|
2566
|
+
if (!search) return null;
|
|
2567
|
+
const params = toSearchParams(search);
|
|
2568
|
+
assertNoAmbiguousSelectionParams(matrix, params);
|
|
2569
|
+
const variantParam = params.get("variant");
|
|
2570
|
+
if (variantParam != null) {
|
|
2571
|
+
const variantSelection = getVariantSelection(matrix, variantParam);
|
|
2572
|
+
if (!variantSelection) {
|
|
2573
|
+
throw new ProductSelectionCodecError(
|
|
2574
|
+
`Unknown product selection variant "${variantParam}".`
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
|
|
2578
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2579
|
+
}
|
|
2580
|
+
return variantSelection.id;
|
|
2581
|
+
}
|
|
2582
|
+
for (const [key, valueToken] of params.entries()) {
|
|
2583
|
+
if (!key.startsWith("opt.")) continue;
|
|
2584
|
+
const optionToken = key.slice(4);
|
|
2585
|
+
const optionById = matrix.optionById.get(optionToken);
|
|
2586
|
+
if (optionById) {
|
|
2587
|
+
if (assignSelectedValue(
|
|
2588
|
+
matrix,
|
|
2589
|
+
selectedByOptionId,
|
|
2590
|
+
optionById.id,
|
|
2591
|
+
valueToken
|
|
2592
|
+
)) {
|
|
2593
|
+
continue;
|
|
2594
|
+
}
|
|
2595
|
+
const before = selectedByOptionId.get(optionById.id);
|
|
2596
|
+
if (assignSelectedValueSlugByOptionId(
|
|
2597
|
+
matrix,
|
|
2598
|
+
selectedByOptionId,
|
|
2599
|
+
optionById.id,
|
|
2600
|
+
valueToken
|
|
2601
|
+
) && selectedByOptionId.get(optionById.id) !== before) {
|
|
2602
|
+
emitCompatibilityOptionIdParam(options, {
|
|
2603
|
+
optionId: optionById.id,
|
|
2604
|
+
optionSlug: optionById.slug,
|
|
2605
|
+
valueSlug: valueToken,
|
|
2606
|
+
searchParam: key
|
|
2607
|
+
});
|
|
2608
|
+
continue;
|
|
2609
|
+
}
|
|
2610
|
+
throw new ProductSelectionCodecError(
|
|
2611
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
|
|
2612
|
+
);
|
|
2613
|
+
}
|
|
2614
|
+
const optionBySlug = matrix.optionBySlug.get(optionToken);
|
|
2615
|
+
if (optionBySlug) {
|
|
2616
|
+
if (assignSelectedValueSlugByOptionSlug(
|
|
2617
|
+
matrix,
|
|
2618
|
+
selectedByOptionId,
|
|
2619
|
+
optionBySlug.slug,
|
|
2620
|
+
valueToken
|
|
2621
|
+
)) {
|
|
2622
|
+
continue;
|
|
2623
|
+
}
|
|
2624
|
+
if (matrix.valueById.has(valueToken)) {
|
|
2625
|
+
throw new ProductSelectionCodecError(
|
|
2626
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
|
|
2627
|
+
);
|
|
2628
|
+
}
|
|
2629
|
+
throw new ProductSelectionCodecError(
|
|
2630
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionSlug>=<valueSlug> for compatibility URLs.`
|
|
2631
|
+
);
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
return null;
|
|
2635
|
+
}
|
|
2636
|
+
function normalizeProductSelection(detail, selection = {}, options) {
|
|
2637
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2638
|
+
return normalizeProductSelectionFromMatrix(matrix, selection, options);
|
|
2639
|
+
}
|
|
2640
|
+
function normalizeProductSelectionFromMatrix(matrix, selection = {}, options) {
|
|
2641
|
+
const selectedByOptionId = /* @__PURE__ */ new Map();
|
|
2642
|
+
const variantSelection = getVariantSelection(matrix, selection.variantId);
|
|
2643
|
+
const variantId = variantSelection?.id ?? null;
|
|
2644
|
+
if (variantSelection) {
|
|
2645
|
+
for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
|
|
2646
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
for (const rawValueId of selection.valueIds ?? []) {
|
|
2650
|
+
const valueId = getRelationID(rawValueId);
|
|
2651
|
+
if (!valueId) continue;
|
|
2652
|
+
const optionId = matrix.valueToOptionId.get(valueId);
|
|
2653
|
+
if (!optionId) continue;
|
|
2654
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2655
|
+
}
|
|
2656
|
+
const searchVariantId = assignSearchSelection(
|
|
2657
|
+
matrix,
|
|
2658
|
+
selectedByOptionId,
|
|
2659
|
+
selection.search,
|
|
2660
|
+
options
|
|
2661
|
+
);
|
|
2662
|
+
for (const [rawOptionId, rawSelection] of Object.entries(
|
|
2663
|
+
selection.byOptionId ?? {}
|
|
2664
|
+
)) {
|
|
2665
|
+
const optionId = String(rawOptionId);
|
|
2666
|
+
if (!matrix.optionById.has(optionId)) continue;
|
|
2667
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
2668
|
+
assignSelectedValue(
|
|
2669
|
+
matrix,
|
|
2670
|
+
selectedByOptionId,
|
|
2671
|
+
optionId,
|
|
2672
|
+
rawSelection.valueId
|
|
2673
|
+
);
|
|
2674
|
+
continue;
|
|
2675
|
+
}
|
|
2676
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
2677
|
+
assignSelectedValue(matrix, selectedByOptionId, optionId, rawSelection);
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
2681
|
+
assignSelectedValueSlugByOptionId(
|
|
2682
|
+
matrix,
|
|
2683
|
+
selectedByOptionId,
|
|
2684
|
+
optionId,
|
|
2685
|
+
rawSelection.valueSlug
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2689
|
+
for (const [rawOptionSlug, rawSelection] of Object.entries(
|
|
2690
|
+
selection.byOptionSlug ?? {}
|
|
2691
|
+
)) {
|
|
2692
|
+
const optionSlug = String(rawOptionSlug);
|
|
2693
|
+
if (!matrix.optionBySlug.has(optionSlug)) continue;
|
|
2694
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
2695
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
2696
|
+
if (option) {
|
|
2697
|
+
assignSelectedValue(
|
|
2698
|
+
matrix,
|
|
2699
|
+
selectedByOptionId,
|
|
2700
|
+
option.id,
|
|
2701
|
+
rawSelection.valueId
|
|
2702
|
+
);
|
|
2703
|
+
}
|
|
2704
|
+
continue;
|
|
2705
|
+
}
|
|
2706
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
2707
|
+
assignSelectedValueSlugByOptionSlug(
|
|
2708
|
+
matrix,
|
|
2709
|
+
selectedByOptionId,
|
|
2710
|
+
optionSlug,
|
|
2711
|
+
rawSelection.valueSlug
|
|
2712
|
+
);
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
2716
|
+
assignSelectedValueSlugByOptionSlug(
|
|
2717
|
+
matrix,
|
|
2718
|
+
selectedByOptionId,
|
|
2719
|
+
optionSlug,
|
|
2720
|
+
String(rawSelection)
|
|
2721
|
+
);
|
|
2722
|
+
}
|
|
2723
|
+
}
|
|
2724
|
+
const byOptionId = Object.fromEntries(
|
|
2725
|
+
matrix.optionIds.map((optionId) => [optionId, selectedByOptionId.get(optionId)]).filter((entry) => Boolean(entry[1]))
|
|
2726
|
+
);
|
|
2727
|
+
const byOptionSlug = Object.fromEntries(
|
|
2728
|
+
matrix.options.map((option) => {
|
|
2729
|
+
const valueId = selectedByOptionId.get(option.id);
|
|
2730
|
+
const value = valueId ? matrix.valueById.get(valueId) : void 0;
|
|
2731
|
+
return [option.slug, value?.slug ?? void 0];
|
|
2732
|
+
}).filter((entry) => Boolean(entry[1]))
|
|
2733
|
+
);
|
|
2734
|
+
return {
|
|
2735
|
+
byOptionSlug,
|
|
2736
|
+
byOptionId,
|
|
2737
|
+
valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
|
|
2738
|
+
variantId: searchVariantId ?? variantId
|
|
2739
|
+
};
|
|
2740
|
+
}
|
|
2741
|
+
function parseProductSelection(detail, search, options) {
|
|
2742
|
+
return normalizeProductSelection(detail, { search }, options);
|
|
2743
|
+
}
|
|
2744
|
+
function stringifyProductSelection(detail, selection = {}, options) {
|
|
2745
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2746
|
+
const normalized = normalizeProductSelectionFromMatrix(
|
|
2747
|
+
matrix,
|
|
2748
|
+
selection,
|
|
2749
|
+
options
|
|
2750
|
+
);
|
|
2751
|
+
const params = new URLSearchParams();
|
|
2752
|
+
if (hasExplicitSelection(selection)) {
|
|
2753
|
+
const matchingVariants = getMatchingVariantEntries(matrix, normalized);
|
|
2754
|
+
const exactVariant = getExactSelectedVariantEntry(
|
|
2755
|
+
matrix,
|
|
2756
|
+
normalized,
|
|
2757
|
+
matchingVariants
|
|
2758
|
+
);
|
|
2759
|
+
if (exactVariant) {
|
|
2760
|
+
params.set("variant", exactVariant.id);
|
|
2761
|
+
return params.toString();
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
for (const optionId of matrix.optionIds) {
|
|
2765
|
+
const valueId = normalized.byOptionId[optionId];
|
|
2766
|
+
if (!valueId) continue;
|
|
2767
|
+
if (!matrix.optionById.has(optionId) || !matrix.valueById.has(valueId)) {
|
|
2768
|
+
continue;
|
|
2769
|
+
}
|
|
2770
|
+
params.append(`opt.${optionId}`, valueId);
|
|
2771
|
+
}
|
|
2772
|
+
return params.toString();
|
|
2773
|
+
}
|
|
2774
|
+
function createProductSelectionCodec(detail, options) {
|
|
2775
|
+
return {
|
|
2776
|
+
parse: (search) => parseProductSelection(detail, search, options),
|
|
2777
|
+
stringify: (selection = {}) => stringifyProductSelection(detail, selection, options)
|
|
2778
|
+
};
|
|
2779
|
+
}
|
|
2780
|
+
function selectedEntries(selection) {
|
|
2781
|
+
return Object.entries(selection.byOptionId);
|
|
2782
|
+
}
|
|
2783
|
+
function getMatchingVariantEntries(matrix, selection) {
|
|
2784
|
+
const entries = selectedEntries(selection);
|
|
2785
|
+
if (entries.length === 0) return matrix.variants;
|
|
2786
|
+
return matrix.variants.filter(
|
|
2787
|
+
(variant) => entries.every(
|
|
2788
|
+
([optionId, valueId]) => variant.optionValueByOptionId.get(optionId) === valueId
|
|
2789
|
+
)
|
|
2790
|
+
);
|
|
2791
|
+
}
|
|
2792
|
+
function activeVariantEntries(variants) {
|
|
2793
|
+
return variants.filter((variant) => variant.source.isActive !== false);
|
|
2794
|
+
}
|
|
2795
|
+
function getExactSelectedVariantEntry(matrix, selection, matchingVariants) {
|
|
2796
|
+
if (matrix.optionIds.length === 0) {
|
|
2797
|
+
return getVariantSelection(matrix, selection.variantId) ?? (matchingVariants.length === 1 ? matchingVariants[0] ?? null : null);
|
|
2798
|
+
}
|
|
2799
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
2800
|
+
(optionId) => Boolean(selection.byOptionId[optionId])
|
|
2801
|
+
);
|
|
2802
|
+
if (!allOptionsSelected) return null;
|
|
2803
|
+
return matchingVariants.find(
|
|
2804
|
+
(variant) => matrix.optionIds.every(
|
|
2805
|
+
(optionId) => variant.optionValueByOptionId.get(optionId) === selection.byOptionId[optionId]
|
|
2806
|
+
)
|
|
2807
|
+
) ?? null;
|
|
2808
|
+
}
|
|
2809
|
+
function buildSelectionPrice(variants) {
|
|
2810
|
+
const { min, max } = getMinMax(variants.map((variant) => variant.price));
|
|
2811
|
+
const { min: compareAtMin, max: compareAtMax } = getMinMax(
|
|
2812
|
+
variants.map((variant) => variant.compareAtPrice)
|
|
2813
|
+
);
|
|
2814
|
+
return {
|
|
2815
|
+
min,
|
|
2816
|
+
max,
|
|
2817
|
+
compareAtMin,
|
|
2818
|
+
compareAtMax,
|
|
2819
|
+
isRange: min !== null && max !== null ? min !== max : false
|
|
2820
|
+
};
|
|
2821
|
+
}
|
|
2822
|
+
function firstMedia(value) {
|
|
2823
|
+
if (value == null) return null;
|
|
2824
|
+
if (Array.isArray(value)) return firstMedia(value[0]);
|
|
2825
|
+
return value;
|
|
2826
|
+
}
|
|
2827
|
+
function isPresentMedia(value) {
|
|
2828
|
+
return value != null;
|
|
2829
|
+
}
|
|
2830
|
+
function mediaArray(values) {
|
|
2831
|
+
if (!Array.isArray(values)) return [];
|
|
2832
|
+
return values.filter(isPresentMedia);
|
|
2833
|
+
}
|
|
2834
|
+
function buildSelectionMedia(detail, selectedVariant, matchingVariants, selectedValues) {
|
|
2835
|
+
const selectedValueImages = selectedValues.flatMap(
|
|
2836
|
+
(value) => mediaArray(value.images)
|
|
2837
|
+
);
|
|
2838
|
+
const selectedValuePrimary = selectedValues.map((value) => firstMedia(value.thumbnail) ?? firstMedia(value.images)).find((value) => value != null) ?? null;
|
|
2839
|
+
const selectedVariantPrimary = firstMedia(selectedVariant?.thumbnail) ?? firstMedia(selectedVariant?.images);
|
|
2840
|
+
const matchingVariantPrimary = matchingVariants.map(
|
|
2841
|
+
(variant) => firstMedia(variant.thumbnail) ?? firstMedia(variant.images)
|
|
2842
|
+
).find((value) => value != null) ?? null;
|
|
2843
|
+
const detailImages = mediaArray(detail.images);
|
|
2844
|
+
const primaryImage = selectedVariantPrimary ?? selectedValuePrimary ?? firstMedia(detail.listing.primaryImage) ?? matchingVariantPrimary ?? firstMedia(detailImages);
|
|
2845
|
+
const images = mediaArray(selectedVariant?.images).length > 0 ? mediaArray(selectedVariant?.images) : selectedValueImages.length > 0 ? selectedValueImages : detailImages;
|
|
2846
|
+
return {
|
|
2847
|
+
primaryImage,
|
|
2848
|
+
images
|
|
2849
|
+
};
|
|
2850
|
+
}
|
|
2851
|
+
function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
2852
|
+
if (selectedVariant) {
|
|
2853
|
+
const availableStock = selectedVariant.isUnlimited ? null : Math.max(0, selectedVariant.stock - selectedVariant.reservedStock);
|
|
2854
|
+
const isActive = selectedVariant.isActive !== false;
|
|
2855
|
+
return {
|
|
2856
|
+
availableForSale: isActive && (selectedVariant.isUnlimited || (availableStock ?? 0) > 0),
|
|
2857
|
+
isUnlimited: selectedVariant.isUnlimited,
|
|
2858
|
+
stock: selectedVariant.stock,
|
|
2859
|
+
reservedStock: selectedVariant.reservedStock,
|
|
2860
|
+
availableStock
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2863
|
+
return {
|
|
2864
|
+
availableForSale: matchingVariants.some(isVariantAvailableForSale),
|
|
2865
|
+
isUnlimited: matchingVariants.some((variant) => variant.isUnlimited),
|
|
2866
|
+
stock: null,
|
|
2867
|
+
reservedStock: null,
|
|
2868
|
+
availableStock: null
|
|
2869
|
+
};
|
|
2870
|
+
}
|
|
2871
|
+
function buildAvailableValueStock(variants) {
|
|
2872
|
+
const activeVariants = variants.filter((variant) => variant.isActive !== false);
|
|
2873
|
+
const isUnlimited = activeVariants.some((variant) => variant.isUnlimited);
|
|
2874
|
+
const availableStock = isUnlimited ? null : activeVariants.reduce(
|
|
2875
|
+
(sum, variant) => sum + Math.max(0, variant.stock - variant.reservedStock),
|
|
2876
|
+
0
|
|
2877
|
+
);
|
|
2878
|
+
return {
|
|
2879
|
+
availableForSale: activeVariants.some(isVariantAvailableForSale),
|
|
2880
|
+
isUnlimited,
|
|
2881
|
+
availableStock
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
function getCandidateVariantsForValue(matrix, optionId, valueId, selectedValueIds) {
|
|
2885
|
+
const selectedByOption = getSelectedValueByOptionId(matrix, selectedValueIds);
|
|
2886
|
+
selectedByOption.set(optionId, valueId);
|
|
2887
|
+
return matrix.variants.filter(
|
|
2888
|
+
(variant) => Array.from(selectedByOption.entries()).every(
|
|
2889
|
+
([selectedOptionId, selectedValueId]) => variant.optionValueByOptionId.get(selectedOptionId) === selectedValueId
|
|
2890
|
+
)
|
|
2891
|
+
).map((variant) => variant.source);
|
|
2892
|
+
}
|
|
2893
|
+
function getResolutionContext(context) {
|
|
2894
|
+
return {
|
|
2895
|
+
images: context?.images ?? context?.detail?.images ?? [],
|
|
2896
|
+
listing: context?.listing ?? context?.detail?.listing ?? {}
|
|
2897
|
+
};
|
|
2898
|
+
}
|
|
2899
|
+
function resolveProductSelectionFromMatrix(matrix, selection = {}, options, context) {
|
|
2900
|
+
const { images, listing } = getResolutionContext(context);
|
|
2901
|
+
const effectiveSelection = hasExplicitSelection(selection) || listing.selectionHintVariant == null ? selection : { ...selection, variantId: listing.selectionHintVariant };
|
|
2902
|
+
const normalizedSelection = normalizeProductSelectionFromMatrix(
|
|
2903
|
+
matrix,
|
|
2904
|
+
effectiveSelection,
|
|
2905
|
+
options
|
|
2906
|
+
);
|
|
2907
|
+
const matchingVariantEntries = getMatchingVariantEntries(
|
|
2908
|
+
matrix,
|
|
2909
|
+
normalizedSelection
|
|
2910
|
+
);
|
|
2911
|
+
const activeMatchingVariantEntries = activeVariantEntries(
|
|
2912
|
+
matchingVariantEntries
|
|
2913
|
+
);
|
|
2914
|
+
const selectedVariantEntry = getExactSelectedVariantEntry(
|
|
2915
|
+
matrix,
|
|
2916
|
+
normalizedSelection,
|
|
2917
|
+
matchingVariantEntries
|
|
2918
|
+
);
|
|
2919
|
+
const selectedVariant = selectedVariantEntry?.source ?? null;
|
|
2920
|
+
const matchingVariants = activeMatchingVariantEntries.map(
|
|
2921
|
+
(variant) => variant.source
|
|
2922
|
+
);
|
|
2923
|
+
const selectedValues = matrix.optionIds.map((optionId) => normalizedSelection.byOptionId[optionId]).map((valueId) => valueId ? matrix.valueById.get(valueId) : void 0).filter((value) => value !== void 0);
|
|
2924
|
+
const availableValuesByOptionId = Object.fromEntries(
|
|
2925
|
+
matrix.options.map((option) => {
|
|
2926
|
+
const availableValueIds = new Set(
|
|
2927
|
+
getAvailableOptionValues(
|
|
2928
|
+
matrix,
|
|
2929
|
+
option.id,
|
|
2930
|
+
normalizedSelection.valueIds
|
|
2931
|
+
).map((value) => value.id)
|
|
2932
|
+
);
|
|
2933
|
+
return [
|
|
2934
|
+
option.id,
|
|
2935
|
+
option.values.map((value) => ({
|
|
2936
|
+
valueId: value.id,
|
|
2937
|
+
value: value.label,
|
|
2938
|
+
slug: value.slug ?? "",
|
|
2939
|
+
selected: normalizedSelection.byOptionId[option.id] === value.id,
|
|
2940
|
+
available: availableValueIds.has(value.id),
|
|
2941
|
+
...buildAvailableValueStock(
|
|
2942
|
+
getCandidateVariantsForValue(
|
|
2943
|
+
matrix,
|
|
2944
|
+
option.id,
|
|
2945
|
+
value.id,
|
|
2946
|
+
normalizedSelection.valueIds
|
|
2947
|
+
)
|
|
2948
|
+
),
|
|
2949
|
+
swatchColor: value.swatchColor ?? null,
|
|
2950
|
+
thumbnail: value.thumbnail ?? null,
|
|
2951
|
+
images: value.images ?? null
|
|
2952
|
+
}))
|
|
2953
|
+
];
|
|
2954
|
+
})
|
|
2955
|
+
);
|
|
2956
|
+
const availableValuesByOptionSlug = Object.fromEntries(
|
|
2957
|
+
matrix.options.map((option) => [
|
|
2958
|
+
option.slug,
|
|
2959
|
+
availableValuesByOptionId[option.id] ?? []
|
|
2960
|
+
])
|
|
2961
|
+
);
|
|
2962
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
2963
|
+
(optionId) => Boolean(normalizedSelection.byOptionId[optionId])
|
|
2964
|
+
);
|
|
2965
|
+
const priceVariants = selectedVariant ? [selectedVariant] : matchingVariants;
|
|
2966
|
+
return {
|
|
2967
|
+
normalizedSelection,
|
|
2968
|
+
selectedVariant,
|
|
2969
|
+
matchingVariants,
|
|
2970
|
+
partialVariants: selectedVariant ? [] : matchingVariants,
|
|
2971
|
+
availableValuesByOptionSlug,
|
|
2972
|
+
availableValuesByOptionId,
|
|
2973
|
+
allOptionsSelected,
|
|
2974
|
+
price: buildSelectionPrice(priceVariants),
|
|
2975
|
+
media: buildSelectionMedia(
|
|
2976
|
+
{
|
|
2977
|
+
images,
|
|
2978
|
+
listing: {
|
|
2979
|
+
primaryImage: listing.primaryImage ?? null
|
|
2980
|
+
}
|
|
2981
|
+
},
|
|
2982
|
+
selectedVariant,
|
|
2983
|
+
matchingVariants,
|
|
2984
|
+
selectedValues
|
|
2985
|
+
),
|
|
2986
|
+
stock: buildSelectionStock(selectedVariant, matchingVariants)
|
|
2987
|
+
};
|
|
2988
|
+
}
|
|
2989
|
+
function resolveProductSelection(detail, selection = {}, options) {
|
|
2990
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2991
|
+
return resolveProductSelectionFromMatrix(matrix, selection, options, {
|
|
2992
|
+
detail
|
|
2993
|
+
});
|
|
2994
|
+
}
|
|
2995
|
+
function isProductDetailImageMedia(value) {
|
|
2996
|
+
return typeof value === "object" && value !== null;
|
|
2997
|
+
}
|
|
2998
|
+
function mediaDedupeKey(value) {
|
|
2999
|
+
if (value.id != null) return `id:${String(value.id)}`;
|
|
3000
|
+
if (value.url) return `url:${value.url}`;
|
|
3001
|
+
return null;
|
|
3002
|
+
}
|
|
3003
|
+
function getProductSelectionImages(resolution) {
|
|
3004
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3005
|
+
const images = [];
|
|
3006
|
+
const candidates = [
|
|
3007
|
+
resolution.media.primaryImage,
|
|
3008
|
+
...resolution.media.images
|
|
3009
|
+
].filter(isProductDetailImageMedia);
|
|
3010
|
+
for (const candidate of candidates) {
|
|
3011
|
+
const key = mediaDedupeKey(candidate);
|
|
3012
|
+
if (key && seen.has(key)) continue;
|
|
3013
|
+
if (key) seen.add(key);
|
|
3014
|
+
images.push(candidate);
|
|
3015
|
+
}
|
|
3016
|
+
return images;
|
|
3017
|
+
}
|
|
3018
|
+
function getProductHrefSlug(product) {
|
|
3019
|
+
if ("product" in product && product.product?.slug) {
|
|
3020
|
+
return product.product.slug;
|
|
3021
|
+
}
|
|
3022
|
+
if ("slug" in product && product.slug) return product.slug;
|
|
3023
|
+
throw new ProductSelectionCodecError(
|
|
3024
|
+
"Product slug is required to build a product href."
|
|
3025
|
+
);
|
|
3026
|
+
}
|
|
3027
|
+
function joinProductPath(basePath, slug, trailingSlash) {
|
|
3028
|
+
const base = basePath.replace(/\/+$/, "");
|
|
3029
|
+
const encodedSlug = encodeURIComponent(slug);
|
|
3030
|
+
return `${base}/${encodedSlug}${trailingSlash ? "/" : ""}`;
|
|
3031
|
+
}
|
|
3032
|
+
function getProductHrefGroupSelection(group, matrix) {
|
|
3033
|
+
if (!group) return null;
|
|
3034
|
+
if (group.variantId != null) return { variantId: group.variantId };
|
|
3035
|
+
const listingVariantId = group.listing?.selectionHintVariant;
|
|
3036
|
+
const optionId = group.optionId != null ? String(group.optionId) : group.optionSlug ? matrix?.optionBySlug.get(group.optionSlug)?.id : void 0;
|
|
3037
|
+
if (!optionId) {
|
|
3038
|
+
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
3039
|
+
}
|
|
3040
|
+
const option = matrix?.optionById.get(optionId);
|
|
3041
|
+
const optionValueId = group.optionValueId != null ? String(group.optionValueId) : group.optionValueSlug && option ? option.values.find((value) => value.slug === group.optionValueSlug)?.id : void 0;
|
|
3042
|
+
if (!optionValueId) {
|
|
3043
|
+
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
3044
|
+
}
|
|
3045
|
+
return { byOptionId: { [optionId]: optionValueId } };
|
|
3046
|
+
}
|
|
3047
|
+
function buildProductHref(product, group, options = {}) {
|
|
3048
|
+
const path = joinProductPath(
|
|
3049
|
+
options.basePath ?? "/products",
|
|
3050
|
+
getProductHrefSlug(product),
|
|
3051
|
+
options.trailingSlash ?? false
|
|
3052
|
+
);
|
|
3053
|
+
const params = new URLSearchParams();
|
|
3054
|
+
if (options.detail && options.selection) {
|
|
3055
|
+
const selection = stringifyProductSelection(options.detail, options.selection);
|
|
3056
|
+
return selection ? `${path}?${selection}` : path;
|
|
3057
|
+
}
|
|
3058
|
+
const groupSelection = getProductHrefGroupSelection(group, options.matrix);
|
|
3059
|
+
if (groupSelection) {
|
|
3060
|
+
if (options.detail) {
|
|
3061
|
+
const selection = stringifyProductSelection(options.detail, groupSelection);
|
|
3062
|
+
return selection ? `${path}?${selection}` : path;
|
|
3063
|
+
}
|
|
3064
|
+
if (groupSelection.variantId != null) {
|
|
3065
|
+
params.set("variant", String(groupSelection.variantId));
|
|
3066
|
+
return `${path}?${params.toString()}`;
|
|
3067
|
+
}
|
|
3068
|
+
const [selectionEntry] = Object.entries(groupSelection.byOptionId ?? {});
|
|
3069
|
+
if (selectionEntry) {
|
|
3070
|
+
const [optionId, valueId] = selectionEntry;
|
|
3071
|
+
params.set(`opt.${optionId}`, String(valueId));
|
|
3072
|
+
return `${path}?${params.toString()}`;
|
|
3073
|
+
}
|
|
3074
|
+
}
|
|
3075
|
+
if (group?.optionValueSlug) {
|
|
3076
|
+
const optionSlug = group.optionSlug ?? (group.optionId != null ? options.matrix?.optionById.get(String(group.optionId))?.slug : void 0);
|
|
3077
|
+
if (optionSlug) {
|
|
3078
|
+
params.set(`opt.${optionSlug}`, group.optionValueSlug);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
return params.size > 0 ? `${path}?${params.toString()}` : path;
|
|
3082
|
+
}
|
|
3298
3083
|
function compareVariantOrder(a, b) {
|
|
3299
3084
|
const aOrder = Number(a._order ?? Number.MAX_SAFE_INTEGER);
|
|
3300
3085
|
const bOrder = Number(b._order ?? Number.MAX_SAFE_INTEGER);
|
|
@@ -3674,4 +3459,9 @@ function createAnalytics(config) {
|
|
|
3674
3459
|
}
|
|
3675
3460
|
};
|
|
3676
3461
|
}
|
|
3462
|
+
|
|
3463
|
+
// src/index.ts
|
|
3464
|
+
function createClient2(options) {
|
|
3465
|
+
return createClient(options);
|
|
3466
|
+
}
|
|
3677
3467
|
//# sourceMappingURL=index.cjs.map
|