@01.software/sdk 0.29.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-DAjQYNuM.d.ts → const-B75IFDRi.d.ts} +2 -4
- package/dist/{const-Dsixdi6z.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 +784 -1530
- 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 +784 -1548
- 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-Ci-ZA7aM.d.cts → payload-types-DPjO_IbQ.d.cts} +9 -3
- package/dist/{payload-types-Ci-ZA7aM.d.ts → payload-types-DPjO_IbQ.d.ts} +9 -3
- 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 +299 -840
- 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 +299 -858
- package/dist/server.js.map +1 -1
- package/dist/{types-BWq_WlbB.d.ts → types-1fBLrYU7.d.ts} +1 -1
- package/dist/{types-zKjATmDK.d.cts → types-BwT0eeaz.d.cts} +1 -1
- package/dist/{server-Cv0Q4dPQ.d.ts → types-Dlb2mwpX.d.cts} +228 -741
- package/dist/{server-C0C8dtms.d.cts → types-DuSKPiY5.d.ts} +228 -741
- 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 +84 -15
package/dist/index.cjs
CHANGED
|
@@ -26,61 +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
|
-
QueryHooks: () => QueryHooks,
|
|
52
45
|
RateLimitError: () => RateLimitError,
|
|
53
|
-
ReadOnlyCollectionClient: () => ReadOnlyCollectionClient,
|
|
54
46
|
RealtimeConnection: () => RealtimeConnection,
|
|
55
47
|
SDKError: () => SDKError,
|
|
56
48
|
SERVER_COLLECTIONS: () => SERVER_COLLECTIONS,
|
|
57
49
|
SERVER_ONLY_COLLECTIONS: () => SERVER_ONLY_COLLECTIONS,
|
|
58
|
-
ServerClient: () => ServerClient,
|
|
59
|
-
ServerCollectionClient: () => ServerCollectionClient,
|
|
60
|
-
ServerCollectionQueryBuilder: () => ServerCollectionQueryBuilder,
|
|
61
|
-
ServerCommerceClient: () => ServerCommerceClient,
|
|
62
50
|
ServiceUnavailableError: () => ServiceUnavailableError,
|
|
63
51
|
ShippingApi: () => ShippingApi,
|
|
64
52
|
TimeoutError: () => TimeoutError,
|
|
65
53
|
UsageLimitError: () => UsageLimitError,
|
|
66
54
|
ValidationError: () => ValidationError,
|
|
55
|
+
buildProductHref: () => buildProductHref,
|
|
67
56
|
buildProductListingGroupsByOption: () => buildProductListingGroupsByOption,
|
|
68
57
|
buildProductListingProjection: () => buildProductListingProjection,
|
|
69
58
|
buildProductOptionMatrix: () => buildProductOptionMatrix,
|
|
70
59
|
buildProductOptionMatrixFromDetail: () => buildProductOptionMatrixFromDetail,
|
|
71
|
-
collectionKeys: () => collectionKeys,
|
|
72
60
|
createAnalytics: () => createAnalytics,
|
|
73
61
|
createAuthError: () => createAuthError,
|
|
74
|
-
createClient: () =>
|
|
62
|
+
createClient: () => createClient2,
|
|
75
63
|
createConflictError: () => createConflictError,
|
|
76
64
|
createCustomerAuthWebhookHandler: () => createCustomerAuthWebhookHandler,
|
|
77
65
|
createNotFoundError: () => createNotFoundError,
|
|
78
66
|
createPermissionError: () => createPermissionError,
|
|
79
67
|
createProductSelectionCodec: () => createProductSelectionCodec,
|
|
80
68
|
createRateLimitError: () => createRateLimitError,
|
|
81
|
-
createServerClient: () => createServerClient,
|
|
82
69
|
createTypedWebhookHandler: () => createTypedWebhookHandler,
|
|
83
|
-
customerKeys: () => customerKeys,
|
|
84
70
|
formatOrderName: () => formatOrderName,
|
|
85
71
|
generateOrderNumber: () => generateOrderNumber,
|
|
86
72
|
getAvailableOptionValues: () => getAvailableOptionValues,
|
|
@@ -89,7 +75,7 @@ __export(src_exports, {
|
|
|
89
75
|
getImagePlaceholderStyle: () => getImagePlaceholderStyle,
|
|
90
76
|
getImageSrcSet: () => getImageSrcSet,
|
|
91
77
|
getImageUrl: () => getImageUrl,
|
|
92
|
-
|
|
78
|
+
getProductSelectionImages: () => getProductSelectionImages,
|
|
93
79
|
getSelectedValueByOptionId: () => getSelectedValueByOptionId,
|
|
94
80
|
getVideoGif: () => getVideoGif,
|
|
95
81
|
getVideoMp4Url: () => getVideoMp4Url,
|
|
@@ -114,255 +100,17 @@ __export(src_exports, {
|
|
|
114
100
|
isValidWebhookEvent: () => isValidWebhookEvent,
|
|
115
101
|
isValidationError: () => isValidationError,
|
|
116
102
|
normalizeProductSelection: () => normalizeProductSelection,
|
|
103
|
+
normalizeProductSelectionFromMatrix: () => normalizeProductSelectionFromMatrix,
|
|
117
104
|
normalizeSelectedValueIds: () => normalizeSelectedValueIds,
|
|
118
105
|
parseProductSelection: () => parseProductSelection,
|
|
119
|
-
productKeys: () => productKeys,
|
|
120
106
|
resolveProductSelection: () => resolveProductSelection,
|
|
107
|
+
resolveProductSelectionFromMatrix: () => resolveProductSelectionFromMatrix,
|
|
121
108
|
resolveRelation: () => resolveRelation,
|
|
122
109
|
resolveVariantForSelection: () => resolveVariantForSelection,
|
|
123
110
|
stringifyProductSelection: () => stringifyProductSelection
|
|
124
111
|
});
|
|
125
112
|
module.exports = __toCommonJS(src_exports);
|
|
126
113
|
|
|
127
|
-
// src/utils/types.ts
|
|
128
|
-
var resolveRelation = (ref) => {
|
|
129
|
-
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
130
|
-
return null;
|
|
131
|
-
return ref;
|
|
132
|
-
};
|
|
133
|
-
|
|
134
|
-
// src/core/metadata/index.ts
|
|
135
|
-
function extractSeo(doc) {
|
|
136
|
-
const seo = doc.seo ?? {};
|
|
137
|
-
const og = seo.openGraph ?? {};
|
|
138
|
-
return {
|
|
139
|
-
title: seo.title ?? doc.title ?? null,
|
|
140
|
-
description: seo.description ?? null,
|
|
141
|
-
noIndex: seo.noIndex ?? null,
|
|
142
|
-
canonical: seo.canonical ?? null,
|
|
143
|
-
openGraph: {
|
|
144
|
-
title: og.title ?? null,
|
|
145
|
-
description: og.description ?? null,
|
|
146
|
-
image: og.image ?? null
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function generateMetadata(input, options) {
|
|
151
|
-
const title = input.title ?? void 0;
|
|
152
|
-
const description = input.description ?? void 0;
|
|
153
|
-
const ogTitle = input.openGraph?.title ?? title;
|
|
154
|
-
const ogDescription = input.openGraph?.description ?? description;
|
|
155
|
-
const image = resolveMetaImage(input.openGraph?.image);
|
|
156
|
-
return {
|
|
157
|
-
title,
|
|
158
|
-
description,
|
|
159
|
-
...input.noIndex && { robots: { index: false, follow: false } },
|
|
160
|
-
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
161
|
-
openGraph: {
|
|
162
|
-
...ogTitle && { title: ogTitle },
|
|
163
|
-
...ogDescription && { description: ogDescription },
|
|
164
|
-
...options?.siteName && { siteName: options.siteName },
|
|
165
|
-
...image && { images: [image] }
|
|
166
|
-
},
|
|
167
|
-
twitter: {
|
|
168
|
-
card: image ? "summary_large_image" : "summary",
|
|
169
|
-
...ogTitle && { title: ogTitle },
|
|
170
|
-
...ogDescription && { description: ogDescription },
|
|
171
|
-
...image && { images: [image.url] }
|
|
172
|
-
}
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
function resolveMetaImage(ref) {
|
|
176
|
-
const image = resolveRelation(ref);
|
|
177
|
-
if (!image) return null;
|
|
178
|
-
const sized = image.sizes?.["1536"];
|
|
179
|
-
const url = sized?.url || image.url;
|
|
180
|
-
if (!url) return null;
|
|
181
|
-
const width = sized?.url ? sized.width : image.width;
|
|
182
|
-
const height = sized?.url ? sized.height : image.height;
|
|
183
|
-
return {
|
|
184
|
-
url,
|
|
185
|
-
...width && { width },
|
|
186
|
-
...height && { height },
|
|
187
|
-
...image.alt && { alt: image.alt }
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// src/core/collection/query-builder.ts
|
|
192
|
-
var ReadOnlyCollectionQueryBuilder = class {
|
|
193
|
-
constructor(api, collection) {
|
|
194
|
-
this.api = api;
|
|
195
|
-
this.collection = collection;
|
|
196
|
-
}
|
|
197
|
-
async find(options) {
|
|
198
|
-
return this.api.requestFind(
|
|
199
|
-
`/api/${String(this.collection)}`,
|
|
200
|
-
options
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
async findById(id, options) {
|
|
204
|
-
return this.api.requestFindById(
|
|
205
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
206
|
-
options
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
async count(options) {
|
|
210
|
-
return this.api.requestCount(
|
|
211
|
-
`/api/${String(this.collection)}/count`,
|
|
212
|
-
options
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
async findMetadata(options, metadataOptions) {
|
|
216
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
217
|
-
const doc = docs[0];
|
|
218
|
-
if (!doc) return null;
|
|
219
|
-
return generateMetadata(
|
|
220
|
-
extractSeo(doc),
|
|
221
|
-
metadataOptions
|
|
222
|
-
);
|
|
223
|
-
}
|
|
224
|
-
async findMetadataById(id, metadataOptions) {
|
|
225
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
226
|
-
return generateMetadata(
|
|
227
|
-
extractSeo(doc),
|
|
228
|
-
metadataOptions
|
|
229
|
-
);
|
|
230
|
-
}
|
|
231
|
-
};
|
|
232
|
-
var CollectionQueryBuilder = class {
|
|
233
|
-
constructor(api, collection) {
|
|
234
|
-
this.api = api;
|
|
235
|
-
this.collection = collection;
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Find documents (list query)
|
|
239
|
-
* GET /api/{collection}
|
|
240
|
-
* @returns Payload CMS find response with docs array and pagination
|
|
241
|
-
*/
|
|
242
|
-
async find(options) {
|
|
243
|
-
return this.api.requestFind(
|
|
244
|
-
`/api/${String(this.collection)}`,
|
|
245
|
-
options
|
|
246
|
-
);
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Find document by ID
|
|
250
|
-
* GET /api/{collection}/{id}
|
|
251
|
-
* @returns Document object directly (no wrapper)
|
|
252
|
-
*/
|
|
253
|
-
async findById(id, options) {
|
|
254
|
-
return this.api.requestFindById(
|
|
255
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
256
|
-
options
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Create a new document
|
|
261
|
-
* POST /api/{collection}
|
|
262
|
-
* @returns Payload CMS mutation response with doc and message
|
|
263
|
-
*/
|
|
264
|
-
async create(data, options) {
|
|
265
|
-
const endpoint = `/api/${String(this.collection)}`;
|
|
266
|
-
if (options?.file) {
|
|
267
|
-
return this.api.requestCreateWithFile(
|
|
268
|
-
endpoint,
|
|
269
|
-
data,
|
|
270
|
-
options.file,
|
|
271
|
-
options.filename
|
|
272
|
-
);
|
|
273
|
-
}
|
|
274
|
-
return this.api.requestCreate(endpoint, data);
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Update a document by ID
|
|
278
|
-
* PATCH /api/{collection}/{id}
|
|
279
|
-
* @returns Payload CMS mutation response with doc and message
|
|
280
|
-
*/
|
|
281
|
-
async update(id, data, options) {
|
|
282
|
-
const endpoint = `/api/${String(this.collection)}/${String(id)}`;
|
|
283
|
-
if (options?.file) {
|
|
284
|
-
return this.api.requestUpdateWithFile(
|
|
285
|
-
endpoint,
|
|
286
|
-
data,
|
|
287
|
-
options.file,
|
|
288
|
-
options.filename
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
return this.api.requestUpdate(endpoint, data);
|
|
292
|
-
}
|
|
293
|
-
/**
|
|
294
|
-
* Count documents
|
|
295
|
-
* GET /api/{collection}/count
|
|
296
|
-
* @returns Count response with totalDocs
|
|
297
|
-
*/
|
|
298
|
-
async count(options) {
|
|
299
|
-
return this.api.requestCount(
|
|
300
|
-
`/api/${String(this.collection)}/count`,
|
|
301
|
-
options
|
|
302
|
-
);
|
|
303
|
-
}
|
|
304
|
-
/**
|
|
305
|
-
* Find first matching document and return its Next.js Metadata.
|
|
306
|
-
* Applies depth: 1 (SEO image populate) and limit: 1 automatically.
|
|
307
|
-
* @returns Metadata or null if no document matches
|
|
308
|
-
*/
|
|
309
|
-
async findMetadata(options, metadataOptions) {
|
|
310
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
311
|
-
const doc = docs[0];
|
|
312
|
-
if (!doc) return null;
|
|
313
|
-
return generateMetadata(
|
|
314
|
-
extractSeo(doc),
|
|
315
|
-
metadataOptions
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
/**
|
|
319
|
-
* Find document by ID and return its Next.js Metadata.
|
|
320
|
-
* Applies depth: 1 (SEO image populate) automatically.
|
|
321
|
-
* @returns Metadata (throws on 404)
|
|
322
|
-
*/
|
|
323
|
-
async findMetadataById(id, metadataOptions) {
|
|
324
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
325
|
-
return generateMetadata(
|
|
326
|
-
extractSeo(doc),
|
|
327
|
-
metadataOptions
|
|
328
|
-
);
|
|
329
|
-
}
|
|
330
|
-
/**
|
|
331
|
-
* Update multiple documents (bulk update)
|
|
332
|
-
* PATCH /api/{collection}
|
|
333
|
-
* @returns Payload CMS find response with updated docs
|
|
334
|
-
*/
|
|
335
|
-
async updateMany(where, data) {
|
|
336
|
-
return this.api.requestUpdateMany(
|
|
337
|
-
`/api/${String(this.collection)}`,
|
|
338
|
-
{ where, data }
|
|
339
|
-
);
|
|
340
|
-
}
|
|
341
|
-
/**
|
|
342
|
-
* Delete a document by ID
|
|
343
|
-
* DELETE /api/{collection}/{id}
|
|
344
|
-
* @returns Deleted document object directly (no wrapper)
|
|
345
|
-
*/
|
|
346
|
-
async remove(id) {
|
|
347
|
-
return this.api.requestDelete(
|
|
348
|
-
`/api/${String(this.collection)}/${String(id)}`
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
/**
|
|
352
|
-
* Delete multiple documents (bulk delete)
|
|
353
|
-
* DELETE /api/{collection}
|
|
354
|
-
* @returns Payload CMS find response with deleted docs
|
|
355
|
-
*/
|
|
356
|
-
async removeMany(where) {
|
|
357
|
-
return this.api.requestDeleteMany(
|
|
358
|
-
`/api/${String(this.collection)}`,
|
|
359
|
-
{ where }
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
var ServerCollectionQueryBuilder = class extends CollectionQueryBuilder {
|
|
364
|
-
};
|
|
365
|
-
|
|
366
114
|
// src/core/collection/http-client.ts
|
|
367
115
|
var import_qs_esm = require("qs-esm");
|
|
368
116
|
|
|
@@ -556,7 +304,10 @@ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
|
|
|
556
304
|
}
|
|
557
305
|
|
|
558
306
|
// src/core/client/types.ts
|
|
559
|
-
function resolveApiUrl() {
|
|
307
|
+
function resolveApiUrl(apiUrl) {
|
|
308
|
+
if (apiUrl) {
|
|
309
|
+
return apiUrl.replace(/\/$/, "");
|
|
310
|
+
}
|
|
560
311
|
if (typeof process !== "undefined" && process.env) {
|
|
561
312
|
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
562
313
|
if (envUrl) {
|
|
@@ -737,6 +488,7 @@ function createHttpStatusError(status, parsed, details, requestId) {
|
|
|
737
488
|
}
|
|
738
489
|
async function httpFetch(url, options) {
|
|
739
490
|
const {
|
|
491
|
+
apiUrl,
|
|
740
492
|
publishableKey,
|
|
741
493
|
secretKey,
|
|
742
494
|
customerToken,
|
|
@@ -746,7 +498,7 @@ async function httpFetch(url, options) {
|
|
|
746
498
|
onUnauthorized,
|
|
747
499
|
...requestInit
|
|
748
500
|
} = options || {};
|
|
749
|
-
const baseUrl = resolveApiUrl();
|
|
501
|
+
const baseUrl = resolveApiUrl(apiUrl);
|
|
750
502
|
const retryConfig = {
|
|
751
503
|
maxRetries: retry?.maxRetries ?? 3,
|
|
752
504
|
retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
|
|
@@ -935,7 +687,7 @@ async function httpFetch(url, options) {
|
|
|
935
687
|
|
|
936
688
|
// src/core/collection/http-client.ts
|
|
937
689
|
var HttpClient = class {
|
|
938
|
-
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
|
|
690
|
+
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
939
691
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
940
692
|
"CollectionClient",
|
|
941
693
|
publishableKey,
|
|
@@ -945,9 +697,11 @@ var HttpClient = class {
|
|
|
945
697
|
this.getCustomerToken = getCustomerToken;
|
|
946
698
|
this.onUnauthorized = onUnauthorized;
|
|
947
699
|
this.onRequestId = onRequestId;
|
|
700
|
+
this.apiUrl = apiUrl;
|
|
948
701
|
}
|
|
949
702
|
get defaultOptions() {
|
|
950
703
|
const opts = {
|
|
704
|
+
apiUrl: this.apiUrl,
|
|
951
705
|
publishableKey: this.publishableKey,
|
|
952
706
|
secretKey: this.secretKey
|
|
953
707
|
};
|
|
@@ -1065,159 +819,113 @@ var HttpClient = class {
|
|
|
1065
819
|
}
|
|
1066
820
|
};
|
|
1067
821
|
|
|
1068
|
-
// src/
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
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
|
+
};
|
|
1076
844
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
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;
|
|
1080
891
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
* GET /api/{collection}
|
|
1087
|
-
*/
|
|
1088
|
-
async requestFind(endpoint, options) {
|
|
1089
|
-
const url = this.buildUrl(endpoint, options);
|
|
1090
|
-
const response = await this.fetchWithTracking(url, {
|
|
1091
|
-
...this.defaultOptions,
|
|
1092
|
-
method: "GET"
|
|
1093
|
-
});
|
|
1094
|
-
return this.parseFindResponse(response);
|
|
1095
|
-
}
|
|
1096
|
-
/**
|
|
1097
|
-
* Find-like response from a custom endpoint
|
|
1098
|
-
* POST /api/...custom-endpoint
|
|
1099
|
-
*/
|
|
1100
|
-
async requestFindEndpoint(endpoint, data) {
|
|
1101
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1102
|
-
...this.defaultOptions,
|
|
1103
|
-
method: "POST",
|
|
1104
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1105
|
-
});
|
|
1106
|
-
return this.parseFindResponse(response);
|
|
1107
|
-
}
|
|
1108
|
-
/**
|
|
1109
|
-
* Find document by ID
|
|
1110
|
-
* GET /api/{collection}/{id}
|
|
1111
|
-
*/
|
|
1112
|
-
async requestFindById(endpoint, options) {
|
|
1113
|
-
const url = this.buildUrl(endpoint, options);
|
|
1114
|
-
const response = await this.fetchWithTracking(url, {
|
|
1115
|
-
...this.defaultOptions,
|
|
1116
|
-
method: "GET"
|
|
1117
|
-
});
|
|
1118
|
-
return this.parseDocumentResponse(response);
|
|
1119
|
-
}
|
|
1120
|
-
/**
|
|
1121
|
-
* Create document
|
|
1122
|
-
* POST /api/{collection}
|
|
1123
|
-
*/
|
|
1124
|
-
async requestCreate(endpoint, data) {
|
|
1125
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1126
|
-
...this.defaultOptions,
|
|
1127
|
-
method: "POST",
|
|
1128
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1129
|
-
});
|
|
1130
|
-
return this.parseMutationResponse(response);
|
|
1131
|
-
}
|
|
1132
|
-
/**
|
|
1133
|
-
* Update document
|
|
1134
|
-
* PATCH /api/{collection}/{id}
|
|
1135
|
-
*/
|
|
1136
|
-
async requestUpdate(endpoint, data) {
|
|
1137
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1138
|
-
...this.defaultOptions,
|
|
1139
|
-
method: "PATCH",
|
|
1140
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1141
|
-
});
|
|
1142
|
-
return this.parseMutationResponse(response);
|
|
1143
|
-
}
|
|
1144
|
-
/**
|
|
1145
|
-
* Count documents
|
|
1146
|
-
* GET /api/{collection}/count
|
|
1147
|
-
*/
|
|
1148
|
-
async requestCount(endpoint, options) {
|
|
1149
|
-
const url = this.buildUrl(endpoint, options);
|
|
1150
|
-
const response = await this.fetchWithTracking(url, {
|
|
1151
|
-
...this.defaultOptions,
|
|
1152
|
-
method: "GET"
|
|
1153
|
-
});
|
|
1154
|
-
return this.parseDocumentResponse(response);
|
|
1155
|
-
}
|
|
1156
|
-
/**
|
|
1157
|
-
* Update multiple documents (bulk update)
|
|
1158
|
-
* PATCH /api/{collection}
|
|
1159
|
-
*/
|
|
1160
|
-
async requestUpdateMany(endpoint, data) {
|
|
1161
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1162
|
-
...this.defaultOptions,
|
|
1163
|
-
method: "PATCH",
|
|
1164
|
-
body: JSON.stringify(data)
|
|
1165
|
-
});
|
|
1166
|
-
return this.parseFindResponse(response);
|
|
1167
|
-
}
|
|
1168
|
-
/**
|
|
1169
|
-
* Delete document
|
|
1170
|
-
* DELETE /api/{collection}/{id}
|
|
1171
|
-
*/
|
|
1172
|
-
async requestDelete(endpoint) {
|
|
1173
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1174
|
-
...this.defaultOptions,
|
|
1175
|
-
method: "DELETE"
|
|
1176
|
-
});
|
|
1177
|
-
return this.parseDocumentResponse(response);
|
|
892
|
+
async find(options) {
|
|
893
|
+
return this.api.requestFind(
|
|
894
|
+
`/api/${String(this.collection)}`,
|
|
895
|
+
options
|
|
896
|
+
);
|
|
1178
897
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1185
|
-
...this.defaultOptions,
|
|
1186
|
-
method: "DELETE",
|
|
1187
|
-
body: JSON.stringify(data)
|
|
1188
|
-
});
|
|
1189
|
-
return this.parseFindResponse(response);
|
|
898
|
+
async findById(id, options) {
|
|
899
|
+
return this.api.requestFindById(
|
|
900
|
+
`/api/${String(this.collection)}/${String(id)}`,
|
|
901
|
+
options
|
|
902
|
+
);
|
|
1190
903
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1197
|
-
...this.defaultOptions,
|
|
1198
|
-
method: "POST",
|
|
1199
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1200
|
-
});
|
|
1201
|
-
return this.parseMutationResponse(response);
|
|
904
|
+
async count(options) {
|
|
905
|
+
return this.api.requestCount(
|
|
906
|
+
`/api/${String(this.collection)}/count`,
|
|
907
|
+
options
|
|
908
|
+
);
|
|
1202
909
|
}
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1212
|
-
});
|
|
1213
|
-
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
|
+
);
|
|
1214
918
|
}
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
919
|
+
async findMetadataById(id, metadataOptions) {
|
|
920
|
+
const doc = await this.findById(id, { depth: 1 });
|
|
921
|
+
return generateMetadata(
|
|
922
|
+
extractSeo(doc),
|
|
923
|
+
metadataOptions
|
|
924
|
+
);
|
|
1219
925
|
}
|
|
1220
926
|
};
|
|
927
|
+
|
|
928
|
+
// src/core/collection/collection-client.ts
|
|
1221
929
|
var ReadOnlyCollectionClient = class extends HttpClient {
|
|
1222
930
|
from(collection) {
|
|
1223
931
|
return new ReadOnlyCollectionQueryBuilder(this, collection);
|
|
@@ -1248,126 +956,6 @@ var ReadOnlyCollectionClient = class extends HttpClient {
|
|
|
1248
956
|
}
|
|
1249
957
|
};
|
|
1250
958
|
|
|
1251
|
-
// src/core/collection/const.ts
|
|
1252
|
-
var INTERNAL_COLLECTIONS = [
|
|
1253
|
-
"users",
|
|
1254
|
-
"payload-kv",
|
|
1255
|
-
"payload-locked-documents",
|
|
1256
|
-
"payload-preferences",
|
|
1257
|
-
"payload-migrations",
|
|
1258
|
-
"payload-folders",
|
|
1259
|
-
"field-configs",
|
|
1260
|
-
"system-media",
|
|
1261
|
-
"track-assets",
|
|
1262
|
-
"audiences",
|
|
1263
|
-
"email-logs",
|
|
1264
|
-
"api-usage",
|
|
1265
|
-
"tenant-analytics-daily",
|
|
1266
|
-
"tenant-web-analytics-config",
|
|
1267
|
-
"analytics-event-schemas",
|
|
1268
|
-
"subscriptions",
|
|
1269
|
-
"billing-history",
|
|
1270
|
-
"inventory-reservations",
|
|
1271
|
-
"order-status-logs",
|
|
1272
|
-
"api-keys",
|
|
1273
|
-
"personal-access-tokens",
|
|
1274
|
-
"tenant-entitlements",
|
|
1275
|
-
"tenant-purge-jobs",
|
|
1276
|
-
"direct-upload-sessions",
|
|
1277
|
-
"webhook-events",
|
|
1278
|
-
"webhook-deliveries",
|
|
1279
|
-
"audit-logs",
|
|
1280
|
-
"plans",
|
|
1281
|
-
"webhooks",
|
|
1282
|
-
"event-registrations"
|
|
1283
|
-
];
|
|
1284
|
-
var COLLECTIONS = [
|
|
1285
|
-
"tenants",
|
|
1286
|
-
"tenant-metadata",
|
|
1287
|
-
"tenant-logos",
|
|
1288
|
-
"products",
|
|
1289
|
-
"product-variants",
|
|
1290
|
-
"product-options",
|
|
1291
|
-
"product-option-values",
|
|
1292
|
-
"product-categories",
|
|
1293
|
-
"product-tags",
|
|
1294
|
-
"product-collections",
|
|
1295
|
-
"brands",
|
|
1296
|
-
"brand-logos",
|
|
1297
|
-
"orders",
|
|
1298
|
-
"order-items",
|
|
1299
|
-
"returns",
|
|
1300
|
-
"return-items",
|
|
1301
|
-
"fulfillments",
|
|
1302
|
-
"fulfillment-items",
|
|
1303
|
-
"transactions",
|
|
1304
|
-
"customers",
|
|
1305
|
-
"customer-profiles",
|
|
1306
|
-
"customer-profile-lists",
|
|
1307
|
-
"customer-addresses",
|
|
1308
|
-
"carts",
|
|
1309
|
-
"cart-items",
|
|
1310
|
-
"discounts",
|
|
1311
|
-
"shipping-policies",
|
|
1312
|
-
"shipping-zones",
|
|
1313
|
-
"documents",
|
|
1314
|
-
"document-categories",
|
|
1315
|
-
"document-types",
|
|
1316
|
-
"articles",
|
|
1317
|
-
"article-authors",
|
|
1318
|
-
"article-categories",
|
|
1319
|
-
"article-tags",
|
|
1320
|
-
"playlists",
|
|
1321
|
-
"playlist-categories",
|
|
1322
|
-
"playlist-tags",
|
|
1323
|
-
"tracks",
|
|
1324
|
-
"track-categories",
|
|
1325
|
-
"track-tags",
|
|
1326
|
-
"galleries",
|
|
1327
|
-
"gallery-categories",
|
|
1328
|
-
"gallery-tags",
|
|
1329
|
-
"gallery-items",
|
|
1330
|
-
"links",
|
|
1331
|
-
"link-categories",
|
|
1332
|
-
"link-tags",
|
|
1333
|
-
"canvases",
|
|
1334
|
-
"canvas-node-types",
|
|
1335
|
-
"canvas-edge-types",
|
|
1336
|
-
"canvas-categories",
|
|
1337
|
-
"canvas-tags",
|
|
1338
|
-
"canvas-nodes",
|
|
1339
|
-
"canvas-edges",
|
|
1340
|
-
"videos",
|
|
1341
|
-
"video-categories",
|
|
1342
|
-
"video-tags",
|
|
1343
|
-
"live-streams",
|
|
1344
|
-
"images",
|
|
1345
|
-
"forms",
|
|
1346
|
-
"form-submissions",
|
|
1347
|
-
// Community
|
|
1348
|
-
"posts",
|
|
1349
|
-
"comments",
|
|
1350
|
-
"reactions",
|
|
1351
|
-
"reaction-types",
|
|
1352
|
-
"bookmarks",
|
|
1353
|
-
"post-categories",
|
|
1354
|
-
// Events
|
|
1355
|
-
"event-calendars",
|
|
1356
|
-
"events",
|
|
1357
|
-
"event-categories",
|
|
1358
|
-
"event-occurrences",
|
|
1359
|
-
"event-tags"
|
|
1360
|
-
];
|
|
1361
|
-
var SERVER_ONLY_COLLECTIONS = [
|
|
1362
|
-
"customer-groups",
|
|
1363
|
-
"reports",
|
|
1364
|
-
"community-bans"
|
|
1365
|
-
];
|
|
1366
|
-
var SERVER_COLLECTIONS = [
|
|
1367
|
-
...COLLECTIONS,
|
|
1368
|
-
...SERVER_ONLY_COLLECTIONS
|
|
1369
|
-
];
|
|
1370
|
-
|
|
1371
959
|
// src/core/api/parse-response.ts
|
|
1372
960
|
async function parseApiResponse(response, endpoint) {
|
|
1373
961
|
let data;
|
|
@@ -1423,6 +1011,7 @@ var CommunityClient = class {
|
|
|
1423
1011
|
options.secretKey
|
|
1424
1012
|
);
|
|
1425
1013
|
this.secretKey = options.secretKey;
|
|
1014
|
+
this.apiUrl = options.apiUrl;
|
|
1426
1015
|
this.customerToken = options.customerToken;
|
|
1427
1016
|
this.onUnauthorized = options.onUnauthorized;
|
|
1428
1017
|
this.onRequestId = options.onRequestId;
|
|
@@ -1437,6 +1026,7 @@ var CommunityClient = class {
|
|
|
1437
1026
|
try {
|
|
1438
1027
|
const response = await httpFetch(endpoint, {
|
|
1439
1028
|
method,
|
|
1029
|
+
apiUrl: this.apiUrl,
|
|
1440
1030
|
publishableKey: this.publishableKey,
|
|
1441
1031
|
secretKey: this.secretKey,
|
|
1442
1032
|
customerToken: token ?? void 0,
|
|
@@ -1585,67 +1175,20 @@ var CommunityClient = class {
|
|
|
1585
1175
|
}
|
|
1586
1176
|
};
|
|
1587
1177
|
|
|
1588
|
-
// src/core/
|
|
1589
|
-
var
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
apiName,
|
|
1596
|
-
options.publishableKey,
|
|
1597
|
-
options.secretKey
|
|
1598
|
-
);
|
|
1599
|
-
this.secretKey = options.secretKey;
|
|
1600
|
-
this.onRequestId = options.onRequestId;
|
|
1601
|
-
}
|
|
1602
|
-
async request(endpoint, body, options) {
|
|
1603
|
-
const method = options?.method ?? "POST";
|
|
1604
|
-
try {
|
|
1605
|
-
const response = await httpFetch(endpoint, {
|
|
1606
|
-
method,
|
|
1607
|
-
publishableKey: this.publishableKey,
|
|
1608
|
-
secretKey: this.secretKey,
|
|
1609
|
-
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1610
|
-
...options?.headers && { headers: options.headers }
|
|
1611
|
-
});
|
|
1612
|
-
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1613
|
-
return parseApiResponse(response, endpoint);
|
|
1614
|
-
} catch (err) {
|
|
1615
|
-
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1616
|
-
this.onRequestId?.(id);
|
|
1617
|
-
throw err;
|
|
1618
|
-
}
|
|
1619
|
-
}
|
|
1620
|
-
};
|
|
1621
|
-
|
|
1622
|
-
// src/core/community/moderation-api.ts
|
|
1623
|
-
var ModerationApi = class extends BaseApi {
|
|
1624
|
-
constructor(options) {
|
|
1625
|
-
super("ModerationApi", options);
|
|
1626
|
-
}
|
|
1627
|
-
banCustomer(params) {
|
|
1628
|
-
return this.request("/api/community-bans/ban", params);
|
|
1629
|
-
}
|
|
1630
|
-
unbanCustomer(params) {
|
|
1631
|
-
return this.request("/api/community-bans/unban", params);
|
|
1632
|
-
}
|
|
1633
|
-
};
|
|
1634
|
-
|
|
1635
|
-
// src/core/customer/customer-auth.ts
|
|
1636
|
-
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1637
|
-
function safeGetItem(key) {
|
|
1638
|
-
try {
|
|
1639
|
-
return localStorage.getItem(key);
|
|
1640
|
-
} catch {
|
|
1641
|
-
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;
|
|
1642
1185
|
}
|
|
1643
1186
|
}
|
|
1644
1187
|
var CustomerAuth = class {
|
|
1645
|
-
constructor(publishableKey, options) {
|
|
1188
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1646
1189
|
this.refreshPromise = null;
|
|
1647
1190
|
this.publishableKey = publishableKey;
|
|
1648
|
-
this.baseUrl = resolveApiUrl();
|
|
1191
|
+
this.baseUrl = resolveApiUrl(apiUrl);
|
|
1649
1192
|
const persist = options?.persist ?? true;
|
|
1650
1193
|
if (persist) {
|
|
1651
1194
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
@@ -1876,8 +1419,8 @@ var CustomerAuth = class {
|
|
|
1876
1419
|
|
|
1877
1420
|
// src/core/customer/customer-namespace.ts
|
|
1878
1421
|
var CustomerNamespace = class {
|
|
1879
|
-
constructor(publishableKey, options) {
|
|
1880
|
-
this.auth = new CustomerAuth(publishableKey, options);
|
|
1422
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1423
|
+
this.auth = new CustomerAuth(publishableKey, options, apiUrl);
|
|
1881
1424
|
}
|
|
1882
1425
|
};
|
|
1883
1426
|
|
|
@@ -1895,6 +1438,7 @@ var CartApi = class {
|
|
|
1895
1438
|
options.secretKey
|
|
1896
1439
|
);
|
|
1897
1440
|
this.secretKey = options.secretKey;
|
|
1441
|
+
this.apiUrl = options.apiUrl;
|
|
1898
1442
|
this.customerToken = options.customerToken;
|
|
1899
1443
|
this.onUnauthorized = options.onUnauthorized;
|
|
1900
1444
|
this.onRequestId = options.onRequestId;
|
|
@@ -1904,6 +1448,7 @@ var CartApi = class {
|
|
|
1904
1448
|
try {
|
|
1905
1449
|
const response = await httpFetch(endpoint, {
|
|
1906
1450
|
method,
|
|
1451
|
+
apiUrl: this.apiUrl,
|
|
1907
1452
|
publishableKey: this.publishableKey,
|
|
1908
1453
|
secretKey: this.secretKey,
|
|
1909
1454
|
customerToken: token ?? void 0,
|
|
@@ -1954,6 +1499,7 @@ var CommerceClient = class {
|
|
|
1954
1499
|
constructor(options) {
|
|
1955
1500
|
const cartApi = new CartApi({
|
|
1956
1501
|
publishableKey: options.publishableKey,
|
|
1502
|
+
apiUrl: options.apiUrl,
|
|
1957
1503
|
customerToken: options.customerToken,
|
|
1958
1504
|
onUnauthorized: options.onUnauthorized,
|
|
1959
1505
|
onRequestId: options.onRequestId
|
|
@@ -1963,6 +1509,7 @@ var CommerceClient = class {
|
|
|
1963
1509
|
try {
|
|
1964
1510
|
const response = await httpFetch(endpoint, {
|
|
1965
1511
|
method: "POST",
|
|
1512
|
+
apiUrl: options.apiUrl,
|
|
1966
1513
|
publishableKey: options.publishableKey,
|
|
1967
1514
|
customerToken: token ?? void 0,
|
|
1968
1515
|
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
|
|
@@ -2010,68 +1557,105 @@ var CommerceClient = class {
|
|
|
2010
1557
|
}
|
|
2011
1558
|
};
|
|
2012
1559
|
|
|
2013
|
-
// src/core/
|
|
2014
|
-
var
|
|
1560
|
+
// src/core/client/client.ts
|
|
1561
|
+
var Client = class {
|
|
2015
1562
|
constructor(options) {
|
|
2016
|
-
|
|
1563
|
+
this.lastRequestId = null;
|
|
1564
|
+
const publishableKey = options.publishableKey;
|
|
1565
|
+
if (!publishableKey) {
|
|
1566
|
+
throw createConfigError("publishableKey is required.");
|
|
1567
|
+
}
|
|
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
|
|
1612
|
+
);
|
|
2017
1613
|
}
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
2021
|
-
* to remain available by the time an order is placed.
|
|
2022
|
-
*/
|
|
2023
|
-
stockCheck(params) {
|
|
2024
|
-
return this.request("/api/products/stock-check", params);
|
|
1614
|
+
getState() {
|
|
1615
|
+
return { ...this.state };
|
|
2025
1616
|
}
|
|
2026
|
-
|
|
2027
|
-
return this.
|
|
2028
|
-
|
|
2029
|
-
|
|
1617
|
+
getConfig() {
|
|
1618
|
+
return { ...this.config };
|
|
1619
|
+
}
|
|
1620
|
+
};
|
|
1621
|
+
function createClient(options) {
|
|
1622
|
+
return new Client(options);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
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
|
|
2030
1635
|
);
|
|
1636
|
+
this.secretKey = options.secretKey;
|
|
1637
|
+
this.apiUrl = options.apiUrl;
|
|
1638
|
+
this.onRequestId = options.onRequestId;
|
|
2031
1639
|
}
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
2035
|
-
* `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
|
|
2036
|
-
* inspect `client.lastRequestId` against backend logs.
|
|
2037
|
-
*/
|
|
2038
|
-
async detail(params) {
|
|
1640
|
+
async request(endpoint, body, options) {
|
|
1641
|
+
const method = options?.method ?? "POST";
|
|
2039
1642
|
try {
|
|
2040
|
-
|
|
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);
|
|
2041
1653
|
} catch (err) {
|
|
2042
|
-
|
|
1654
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1655
|
+
this.onRequestId?.(id);
|
|
2043
1656
|
throw err;
|
|
2044
1657
|
}
|
|
2045
1658
|
}
|
|
2046
|
-
/**
|
|
2047
|
-
* Atomically create or update a product together with its options,
|
|
2048
|
-
* option-values, and variants in a single transaction. Mirrors Shopify's
|
|
2049
|
-
* `productSet` shape and is the canonical write path for the MCP
|
|
2050
|
-
* `product-upsert` tool.
|
|
2051
|
-
*/
|
|
2052
|
-
upsert(params) {
|
|
2053
|
-
return this.request("/api/products/upsert", params);
|
|
2054
|
-
}
|
|
2055
|
-
};
|
|
2056
|
-
|
|
2057
|
-
// src/core/api/discount-api.ts
|
|
2058
|
-
var DiscountApi = class extends BaseApi {
|
|
2059
|
-
constructor(options) {
|
|
2060
|
-
super("DiscountApi", options);
|
|
2061
|
-
}
|
|
2062
|
-
validate(params) {
|
|
2063
|
-
return this.request("/api/discounts/validate", params);
|
|
2064
|
-
}
|
|
2065
|
-
};
|
|
2066
|
-
|
|
2067
|
-
// src/core/api/shipping-api.ts
|
|
2068
|
-
var ShippingApi = class extends BaseApi {
|
|
2069
|
-
constructor(options) {
|
|
2070
|
-
super("ShippingApi", options);
|
|
2071
|
-
}
|
|
2072
|
-
calculate(params) {
|
|
2073
|
-
return this.request("/api/shipping-policies/calculate", params);
|
|
2074
|
-
}
|
|
2075
1659
|
};
|
|
2076
1660
|
|
|
2077
1661
|
// src/core/api/order-api.ts
|
|
@@ -2124,752 +1708,307 @@ var OrderApi = class extends BaseApi {
|
|
|
2124
1708
|
}
|
|
2125
1709
|
};
|
|
2126
1710
|
|
|
2127
|
-
// src/core/
|
|
2128
|
-
var
|
|
1711
|
+
// src/core/api/discount-api.ts
|
|
1712
|
+
var DiscountApi = class extends BaseApi {
|
|
2129
1713
|
constructor(options) {
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
);
|
|
2135
|
-
const serverOptions = {
|
|
2136
|
-
publishableKey,
|
|
2137
|
-
secretKey: options.secretKey,
|
|
2138
|
-
onRequestId: options.onRequestId
|
|
2139
|
-
};
|
|
2140
|
-
const productApi = new ProductApi(serverOptions);
|
|
2141
|
-
const cartApi = new CartApi(serverOptions);
|
|
2142
|
-
const discountApi = new DiscountApi(serverOptions);
|
|
2143
|
-
const shippingApi = new ShippingApi(serverOptions);
|
|
2144
|
-
const orderApi = new OrderApi(serverOptions);
|
|
2145
|
-
this.product = {
|
|
2146
|
-
stockCheck: productApi.stockCheck.bind(productApi),
|
|
2147
|
-
listingGroups: productApi.listingGroups.bind(productApi),
|
|
2148
|
-
detail: productApi.detail.bind(productApi),
|
|
2149
|
-
upsert: productApi.upsert.bind(productApi)
|
|
2150
|
-
};
|
|
2151
|
-
this.cart = {
|
|
2152
|
-
get: cartApi.getCart.bind(cartApi),
|
|
2153
|
-
addItem: cartApi.addItem.bind(cartApi),
|
|
2154
|
-
updateItem: cartApi.updateItem.bind(cartApi),
|
|
2155
|
-
removeItem: cartApi.removeItem.bind(cartApi),
|
|
2156
|
-
applyDiscount: cartApi.applyDiscount.bind(cartApi),
|
|
2157
|
-
removeDiscount: cartApi.removeDiscount.bind(cartApi),
|
|
2158
|
-
clear: cartApi.clearCart.bind(cartApi)
|
|
2159
|
-
};
|
|
2160
|
-
this.orders = {
|
|
2161
|
-
checkout: orderApi.checkout.bind(orderApi),
|
|
2162
|
-
create: orderApi.createOrder.bind(orderApi),
|
|
2163
|
-
update: orderApi.updateOrder.bind(orderApi),
|
|
2164
|
-
updateTransaction: orderApi.updateTransaction.bind(orderApi),
|
|
2165
|
-
confirmPayment: orderApi.confirmPayment.bind(orderApi),
|
|
2166
|
-
createFulfillment: orderApi.createFulfillment.bind(orderApi),
|
|
2167
|
-
updateFulfillment: orderApi.updateFulfillment.bind(orderApi),
|
|
2168
|
-
bulkImportFulfillments: orderApi.bulkImportFulfillments.bind(orderApi),
|
|
2169
|
-
createReturn: orderApi.createReturn.bind(orderApi),
|
|
2170
|
-
updateReturn: orderApi.updateReturn.bind(orderApi),
|
|
2171
|
-
returnWithRefund: orderApi.returnWithRefund.bind(orderApi)
|
|
2172
|
-
};
|
|
2173
|
-
this.discounts = {
|
|
2174
|
-
validate: discountApi.validate.bind(discountApi)
|
|
2175
|
-
};
|
|
2176
|
-
this.shipping = {
|
|
2177
|
-
calculate: shippingApi.calculate.bind(shippingApi)
|
|
2178
|
-
};
|
|
1714
|
+
super("DiscountApi", options);
|
|
1715
|
+
}
|
|
1716
|
+
validate(params) {
|
|
1717
|
+
return this.request("/api/discounts/validate", params);
|
|
2179
1718
|
}
|
|
2180
1719
|
};
|
|
2181
1720
|
|
|
2182
|
-
// src/core/
|
|
2183
|
-
var
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
defaultOptions: {
|
|
2187
|
-
queries: {
|
|
2188
|
-
// Infinite staleTime: server-fetched data persists until explicitly invalidated.
|
|
2189
|
-
// For browser clients needing fresher data, override per-query:
|
|
2190
|
-
// useQuery({ ..., staleTime: 5 * 60 * 1000 })
|
|
2191
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
2192
|
-
refetchOnWindowFocus: false
|
|
2193
|
-
},
|
|
2194
|
-
dehydrate: {
|
|
2195
|
-
shouldDehydrateQuery: (query) => (0, import_react_query.defaultShouldDehydrateQuery)(query) || query.state.status === "pending",
|
|
2196
|
-
shouldRedactErrors: () => false
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
});
|
|
2200
|
-
}
|
|
2201
|
-
var browserQueryClient;
|
|
2202
|
-
function getQueryClient() {
|
|
2203
|
-
if (import_react_query.isServer) {
|
|
2204
|
-
return makeQueryClient();
|
|
1721
|
+
// src/core/api/shipping-api.ts
|
|
1722
|
+
var ShippingApi = class extends BaseApi {
|
|
1723
|
+
constructor(options) {
|
|
1724
|
+
super("ShippingApi", options);
|
|
2205
1725
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
1726
|
+
calculate(params) {
|
|
1727
|
+
return this.request("/api/shipping-policies/calculate", params);
|
|
2208
1728
|
}
|
|
2209
|
-
return browserQueryClient;
|
|
2210
|
-
}
|
|
2211
|
-
|
|
2212
|
-
// src/core/query/query-hooks.ts
|
|
2213
|
-
var import_react_query4 = require("@tanstack/react-query");
|
|
2214
|
-
|
|
2215
|
-
// src/core/query/collection-hooks.ts
|
|
2216
|
-
var import_react_query2 = require("@tanstack/react-query");
|
|
2217
|
-
|
|
2218
|
-
// src/core/query/query-keys.ts
|
|
2219
|
-
function collectionKeys(collection) {
|
|
2220
|
-
return {
|
|
2221
|
-
all: [collection],
|
|
2222
|
-
lists: () => [collection, "list"],
|
|
2223
|
-
list: (options) => [collection, "list", options],
|
|
2224
|
-
details: () => [collection, "detail"],
|
|
2225
|
-
detail: (id, options) => [collection, "detail", id, options],
|
|
2226
|
-
infinites: () => [collection, "infinite"],
|
|
2227
|
-
infinite: (options) => [collection, "infinite", options]
|
|
2228
|
-
};
|
|
2229
|
-
}
|
|
2230
|
-
var customerKeys = {
|
|
2231
|
-
all: ["customer"],
|
|
2232
|
-
me: () => ["customer", "me"]
|
|
2233
|
-
};
|
|
2234
|
-
var productKeys = {
|
|
2235
|
-
listingGroups: (options) => ["products", "listing-groups", "list", options],
|
|
2236
|
-
listingGroupsInfinite: (options) => ["products", "listing-groups", "infinite", options],
|
|
2237
|
-
detail: (params) => ["products", "detail", params],
|
|
2238
|
-
detailAll: () => ["products", "detail"]
|
|
2239
1729
|
};
|
|
2240
1730
|
|
|
2241
|
-
// src/core/
|
|
2242
|
-
var
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
"product-options",
|
|
2246
|
-
"product-option-values",
|
|
2247
|
-
"product-categories",
|
|
2248
|
-
"product-tags",
|
|
2249
|
-
"product-collections",
|
|
2250
|
-
"brands",
|
|
2251
|
-
"brand-logos",
|
|
2252
|
-
"images"
|
|
2253
|
-
]);
|
|
2254
|
-
var DEFAULT_PAGE_SIZE = 20;
|
|
2255
|
-
var CollectionHooks = class {
|
|
2256
|
-
constructor(queryClient, collectionClient) {
|
|
2257
|
-
this.queryClient = queryClient;
|
|
2258
|
-
this.collectionClient = collectionClient;
|
|
2259
|
-
}
|
|
2260
|
-
// ===== useQuery =====
|
|
2261
|
-
useQuery(params, options) {
|
|
2262
|
-
const { collection, options: queryOptions } = params;
|
|
2263
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2264
|
-
return (0, import_react_query2.useQuery)({
|
|
2265
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2266
|
-
queryFn: async () => {
|
|
2267
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2268
|
-
},
|
|
2269
|
-
...restOptions,
|
|
2270
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2271
|
-
...placeholderData !== void 0 && {
|
|
2272
|
-
placeholderData
|
|
2273
|
-
}
|
|
2274
|
-
});
|
|
2275
|
-
}
|
|
2276
|
-
// ===== useSuspenseQuery =====
|
|
2277
|
-
useSuspenseQuery(params, options) {
|
|
2278
|
-
const { collection, options: queryOptions } = params;
|
|
2279
|
-
return (0, import_react_query2.useSuspenseQuery)({
|
|
2280
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2281
|
-
queryFn: async () => {
|
|
2282
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2283
|
-
},
|
|
2284
|
-
...options
|
|
2285
|
-
});
|
|
1731
|
+
// src/core/api/product-api.ts
|
|
1732
|
+
var ProductApi = class extends BaseApi {
|
|
1733
|
+
constructor(options) {
|
|
1734
|
+
super("ProductApi", options);
|
|
2286
1735
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2295
|
-
},
|
|
2296
|
-
...restOptions,
|
|
2297
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2298
|
-
...placeholderData !== void 0 && {
|
|
2299
|
-
placeholderData
|
|
2300
|
-
}
|
|
2301
|
-
});
|
|
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);
|
|
2302
1743
|
}
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
|
|
2307
|
-
|
|
2308
|
-
queryFn: async () => {
|
|
2309
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2310
|
-
},
|
|
2311
|
-
...options
|
|
2312
|
-
});
|
|
1744
|
+
listingGroups(params) {
|
|
1745
|
+
return this.request(
|
|
1746
|
+
"/api/products/listing-groups",
|
|
1747
|
+
params
|
|
1748
|
+
);
|
|
2313
1749
|
}
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
initialPageParam: 1,
|
|
2328
|
-
getNextPageParam: (lastPage) => {
|
|
2329
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2330
|
-
},
|
|
2331
|
-
...options
|
|
2332
|
-
});
|
|
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
|
+
}
|
|
2333
1763
|
}
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
return (
|
|
2342
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2343
|
-
queryFn: async ({ pageParam }) => {
|
|
2344
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2345
|
-
return response;
|
|
2346
|
-
},
|
|
2347
|
-
initialPageParam: 1,
|
|
2348
|
-
getNextPageParam: (lastPage) => {
|
|
2349
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2350
|
-
},
|
|
2351
|
-
...options
|
|
2352
|
-
});
|
|
2353
|
-
}
|
|
2354
|
-
// ===== prefetchQuery =====
|
|
2355
|
-
async prefetchQuery(params, options) {
|
|
2356
|
-
const { collection, options: queryOptions } = params;
|
|
2357
|
-
return this.queryClient.prefetchQuery({
|
|
2358
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2359
|
-
queryFn: async () => {
|
|
2360
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2361
|
-
},
|
|
2362
|
-
...options
|
|
2363
|
-
});
|
|
2364
|
-
}
|
|
2365
|
-
// ===== prefetchQueryById =====
|
|
2366
|
-
async prefetchQueryById(params, options) {
|
|
2367
|
-
const { collection, id, options: queryOptions } = params;
|
|
2368
|
-
return this.queryClient.prefetchQuery({
|
|
2369
|
-
queryKey: collectionKeys(collection).detail(id, queryOptions),
|
|
2370
|
-
queryFn: async () => {
|
|
2371
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2372
|
-
},
|
|
2373
|
-
...options
|
|
2374
|
-
});
|
|
2375
|
-
}
|
|
2376
|
-
// ===== prefetchInfiniteQuery =====
|
|
2377
|
-
async prefetchInfiniteQuery(params, options) {
|
|
2378
|
-
const {
|
|
2379
|
-
collection,
|
|
2380
|
-
options: queryOptions,
|
|
2381
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
2382
|
-
} = params;
|
|
2383
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2384
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2385
|
-
queryFn: async ({ pageParam }) => {
|
|
2386
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2387
|
-
return response;
|
|
2388
|
-
},
|
|
2389
|
-
initialPageParam: 1,
|
|
2390
|
-
getNextPageParam: (lastPage) => {
|
|
2391
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2392
|
-
},
|
|
2393
|
-
pages: options?.pages ?? 1,
|
|
2394
|
-
staleTime: options?.staleTime
|
|
2395
|
-
});
|
|
2396
|
-
}
|
|
2397
|
-
// ===== Mutation Hooks =====
|
|
2398
|
-
useCreate(params, options) {
|
|
2399
|
-
const { collection } = params;
|
|
2400
|
-
return (0, import_react_query2.useMutation)({
|
|
2401
|
-
mutationFn: async (variables) => {
|
|
2402
|
-
return await this.collectionClient.from(collection).create(
|
|
2403
|
-
variables.data,
|
|
2404
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2405
|
-
);
|
|
2406
|
-
},
|
|
2407
|
-
onSuccess: (data) => {
|
|
2408
|
-
this.queryClient.invalidateQueries({
|
|
2409
|
-
queryKey: collectionKeys(collection).all
|
|
2410
|
-
});
|
|
2411
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2412
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2413
|
-
}
|
|
2414
|
-
options?.onSuccess?.(data);
|
|
2415
|
-
},
|
|
2416
|
-
onError: options?.onError,
|
|
2417
|
-
onSettled: options?.onSettled
|
|
2418
|
-
});
|
|
2419
|
-
}
|
|
2420
|
-
useUpdate(params, options) {
|
|
2421
|
-
const { collection } = params;
|
|
2422
|
-
return (0, import_react_query2.useMutation)({
|
|
2423
|
-
mutationFn: async (variables) => {
|
|
2424
|
-
return await this.collectionClient.from(collection).update(
|
|
2425
|
-
variables.id,
|
|
2426
|
-
variables.data,
|
|
2427
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2428
|
-
);
|
|
2429
|
-
},
|
|
2430
|
-
onSuccess: (data) => {
|
|
2431
|
-
this.queryClient.invalidateQueries({
|
|
2432
|
-
queryKey: collectionKeys(collection).all
|
|
2433
|
-
});
|
|
2434
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2435
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2436
|
-
}
|
|
2437
|
-
options?.onSuccess?.(data);
|
|
2438
|
-
},
|
|
2439
|
-
onError: options?.onError,
|
|
2440
|
-
onSettled: options?.onSettled
|
|
2441
|
-
});
|
|
2442
|
-
}
|
|
2443
|
-
useRemove(params, options) {
|
|
2444
|
-
const { collection } = params;
|
|
2445
|
-
return (0, import_react_query2.useMutation)({
|
|
2446
|
-
mutationFn: async (id) => {
|
|
2447
|
-
return await this.collectionClient.from(collection).remove(id);
|
|
2448
|
-
},
|
|
2449
|
-
onSuccess: (data) => {
|
|
2450
|
-
this.queryClient.invalidateQueries({
|
|
2451
|
-
queryKey: collectionKeys(collection).all
|
|
2452
|
-
});
|
|
2453
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2454
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2455
|
-
}
|
|
2456
|
-
options?.onSuccess?.(data);
|
|
2457
|
-
},
|
|
2458
|
-
onError: options?.onError,
|
|
2459
|
-
onSettled: options?.onSettled
|
|
2460
|
-
});
|
|
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);
|
|
2461
1772
|
}
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
1773
|
+
};
|
|
1774
|
+
|
|
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;
|
|
1780
|
+
}
|
|
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;
|
|
2466
1794
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
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;
|
|
2472
1802
|
}
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
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;
|
|
2476
1817
|
}
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
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
|
+
);
|
|
1827
|
+
}
|
|
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
|
|
2482
1849
|
);
|
|
1850
|
+
if (!valid) {
|
|
1851
|
+
return new Response(
|
|
1852
|
+
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
1853
|
+
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
1854
|
+
);
|
|
1855
|
+
}
|
|
2483
1856
|
} else {
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
dataOrOptions
|
|
1857
|
+
console.warn(
|
|
1858
|
+
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
2487
1859
|
);
|
|
2488
1860
|
}
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2494
|
-
function createMutation(mutationFn, callbacks, onSuccessExtra) {
|
|
2495
|
-
return (0, import_react_query3.useMutation)({
|
|
2496
|
-
mutationFn,
|
|
2497
|
-
onSuccess: (data) => {
|
|
2498
|
-
onSuccessExtra?.(data);
|
|
2499
|
-
callbacks?.onSuccess?.(data);
|
|
2500
|
-
},
|
|
2501
|
-
onError: callbacks?.onError,
|
|
2502
|
-
onSettled: callbacks?.onSettled
|
|
2503
|
-
});
|
|
2504
|
-
}
|
|
2505
|
-
var CustomerHooks = class {
|
|
2506
|
-
constructor(queryClient, customerAuth) {
|
|
2507
|
-
this.invalidateMe = () => {
|
|
2508
|
-
this.queryClient.invalidateQueries({ queryKey: customerKeys.me() });
|
|
2509
|
-
};
|
|
2510
|
-
this.queryClient = queryClient;
|
|
2511
|
-
this.customerAuth = customerAuth;
|
|
2512
|
-
}
|
|
2513
|
-
ensureCustomerAuth() {
|
|
2514
|
-
if (!this.customerAuth) {
|
|
2515
|
-
throw createConfigError(
|
|
2516
|
-
"Customer hooks require Client. Use createClient() instead of createServerClient()."
|
|
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" } }
|
|
2517
1866
|
);
|
|
2518
1867
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
return (0, import_react_query3.useQuery)({
|
|
2524
|
-
queryKey: customerKeys.me(),
|
|
2525
|
-
queryFn: async () => {
|
|
2526
|
-
return await this.ensureCustomerAuth().me();
|
|
2527
|
-
},
|
|
2528
|
-
...options,
|
|
2529
|
-
enabled: (options?.enabled ?? true) && !!this.customerAuth?.isAuthenticated()
|
|
2530
|
-
});
|
|
2531
|
-
}
|
|
2532
|
-
// ===== Mutations =====
|
|
2533
|
-
useCustomerLogin(options) {
|
|
2534
|
-
return createMutation(
|
|
2535
|
-
(data) => this.ensureCustomerAuth().login(data),
|
|
2536
|
-
options,
|
|
2537
|
-
this.invalidateMe
|
|
2538
|
-
);
|
|
2539
|
-
}
|
|
2540
|
-
useCustomerRegister(options) {
|
|
2541
|
-
return createMutation(
|
|
2542
|
-
(data) => this.ensureCustomerAuth().register(data),
|
|
2543
|
-
options
|
|
2544
|
-
);
|
|
2545
|
-
}
|
|
2546
|
-
useCustomerLogout(options) {
|
|
2547
|
-
return (0, import_react_query3.useMutation)({
|
|
2548
|
-
mutationFn: async () => {
|
|
2549
|
-
this.ensureCustomerAuth().logout();
|
|
2550
|
-
},
|
|
2551
|
-
onSuccess: () => {
|
|
2552
|
-
this.queryClient.removeQueries({ queryKey: customerKeys.all });
|
|
2553
|
-
options?.onSuccess?.();
|
|
2554
|
-
},
|
|
2555
|
-
onError: options?.onError,
|
|
2556
|
-
onSettled: options?.onSettled
|
|
2557
|
-
});
|
|
2558
|
-
}
|
|
2559
|
-
useCustomerForgotPassword(options) {
|
|
2560
|
-
return createMutation(
|
|
2561
|
-
(email) => this.ensureCustomerAuth().forgotPassword(email).then(() => {
|
|
2562
|
-
}),
|
|
2563
|
-
options
|
|
2564
|
-
);
|
|
2565
|
-
}
|
|
2566
|
-
useCustomerResetPassword(options) {
|
|
2567
|
-
return createMutation(
|
|
2568
|
-
(data) => this.ensureCustomerAuth().resetPassword(data.token, data.password).then(() => {
|
|
2569
|
-
}),
|
|
2570
|
-
options
|
|
2571
|
-
);
|
|
2572
|
-
}
|
|
2573
|
-
useCustomerRefreshToken(options) {
|
|
2574
|
-
return createMutation(
|
|
2575
|
-
() => this.ensureCustomerAuth().refreshToken(),
|
|
2576
|
-
options,
|
|
2577
|
-
this.invalidateMe
|
|
2578
|
-
);
|
|
2579
|
-
}
|
|
2580
|
-
useCustomerUpdateProfile(options) {
|
|
2581
|
-
return createMutation(
|
|
2582
|
-
(data) => this.ensureCustomerAuth().updateProfile(data),
|
|
2583
|
-
options,
|
|
2584
|
-
this.invalidateMe
|
|
2585
|
-
);
|
|
2586
|
-
}
|
|
2587
|
-
useCustomerChangePassword(options) {
|
|
2588
|
-
return createMutation(
|
|
2589
|
-
(data) => this.ensureCustomerAuth().changePassword(data.currentPassword, data.newPassword).then(() => {
|
|
2590
|
-
}),
|
|
2591
|
-
options
|
|
2592
|
-
);
|
|
2593
|
-
}
|
|
2594
|
-
// ===== Customer Cache Utilities =====
|
|
2595
|
-
invalidateCustomerQueries() {
|
|
2596
|
-
return this.queryClient.invalidateQueries({ queryKey: customerKeys.all });
|
|
2597
|
-
}
|
|
2598
|
-
getCustomerData() {
|
|
2599
|
-
return this.queryClient.getQueryData(customerKeys.me());
|
|
2600
|
-
}
|
|
2601
|
-
setCustomerData(data) {
|
|
2602
|
-
this.queryClient.setQueryData(customerKeys.me(), data);
|
|
2603
|
-
}
|
|
2604
|
-
};
|
|
2605
|
-
|
|
2606
|
-
// src/core/query/query-hooks.ts
|
|
2607
|
-
var QueryHooks = class extends CollectionHooks {
|
|
2608
|
-
constructor(queryClient, collectionClient, customerAuth, commerceClient) {
|
|
2609
|
-
super(queryClient, collectionClient);
|
|
2610
|
-
// --- Customer hooks delegation ---
|
|
2611
|
-
this.useCustomerMe = (...args) => this._customer.useCustomerMe(...args);
|
|
2612
|
-
this.useCustomerLogin = (...args) => this._customer.useCustomerLogin(...args);
|
|
2613
|
-
this.useCustomerRegister = (...args) => this._customer.useCustomerRegister(...args);
|
|
2614
|
-
this.useCustomerLogout = (...args) => this._customer.useCustomerLogout(...args);
|
|
2615
|
-
this.useCustomerForgotPassword = (...args) => this._customer.useCustomerForgotPassword(...args);
|
|
2616
|
-
this.useCustomerResetPassword = (...args) => this._customer.useCustomerResetPassword(...args);
|
|
2617
|
-
this.useCustomerRefreshToken = (...args) => this._customer.useCustomerRefreshToken(...args);
|
|
2618
|
-
this.useCustomerUpdateProfile = (...args) => this._customer.useCustomerUpdateProfile(...args);
|
|
2619
|
-
this.useCustomerChangePassword = (...args) => this._customer.useCustomerChangePassword(...args);
|
|
2620
|
-
// --- Customer cache delegation ---
|
|
2621
|
-
this.invalidateCustomerQueries = () => this._customer.invalidateCustomerQueries();
|
|
2622
|
-
this.getCustomerData = () => this._customer.getCustomerData();
|
|
2623
|
-
this.setCustomerData = (data) => this._customer.setCustomerData(data);
|
|
2624
|
-
this._customer = new CustomerHooks(queryClient, customerAuth);
|
|
2625
|
-
this._commerce = commerceClient;
|
|
2626
|
-
}
|
|
2627
|
-
useProductListingGroupsQuery(params, options) {
|
|
2628
|
-
const queryOptions = params.options;
|
|
2629
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2630
|
-
return (0, import_react_query4.useQuery)({
|
|
2631
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2632
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2633
|
-
"/api/products/listing-groups/query",
|
|
2634
|
-
{ options: queryOptions }
|
|
2635
|
-
),
|
|
2636
|
-
...restOptions,
|
|
2637
|
-
...placeholderData !== void 0 && {
|
|
2638
|
-
placeholderData
|
|
2639
|
-
}
|
|
2640
|
-
});
|
|
2641
|
-
}
|
|
2642
|
-
useSuspenseProductListingGroupsQuery(params, options) {
|
|
2643
|
-
const queryOptions = params.options;
|
|
2644
|
-
return (0, import_react_query4.useSuspenseQuery)({
|
|
2645
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2646
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2647
|
-
"/api/products/listing-groups/query",
|
|
2648
|
-
{ options: queryOptions }
|
|
2649
|
-
),
|
|
2650
|
-
...options
|
|
2651
|
-
});
|
|
2652
|
-
}
|
|
2653
|
-
useInfiniteProductListingGroupsQuery(params, options) {
|
|
2654
|
-
const {
|
|
2655
|
-
options: queryOptions,
|
|
2656
|
-
pageSize = 20
|
|
2657
|
-
} = params;
|
|
2658
|
-
return (0, import_react_query4.useInfiniteQuery)({
|
|
2659
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2660
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2661
|
-
"/api/products/listing-groups/query",
|
|
2662
|
-
{
|
|
2663
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2664
|
-
}
|
|
2665
|
-
),
|
|
2666
|
-
initialPageParam: 1,
|
|
2667
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2668
|
-
...options
|
|
2669
|
-
});
|
|
2670
|
-
}
|
|
2671
|
-
useSuspenseInfiniteProductListingGroupsQuery(params, options) {
|
|
2672
|
-
const {
|
|
2673
|
-
options: queryOptions,
|
|
2674
|
-
pageSize = 20
|
|
2675
|
-
} = params;
|
|
2676
|
-
return (0, import_react_query4.useSuspenseInfiniteQuery)({
|
|
2677
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2678
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2679
|
-
"/api/products/listing-groups/query",
|
|
2680
|
-
{
|
|
2681
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2682
|
-
}
|
|
2683
|
-
),
|
|
2684
|
-
initialPageParam: 1,
|
|
2685
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2686
|
-
...options
|
|
2687
|
-
});
|
|
2688
|
-
}
|
|
2689
|
-
async prefetchProductListingGroupsQuery(params, options) {
|
|
2690
|
-
const queryOptions = params.options;
|
|
2691
|
-
return this.queryClient.prefetchQuery({
|
|
2692
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2693
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2694
|
-
"/api/products/listing-groups/query",
|
|
2695
|
-
{ options: queryOptions }
|
|
2696
|
-
),
|
|
2697
|
-
...options
|
|
2698
|
-
});
|
|
2699
|
-
}
|
|
2700
|
-
async prefetchInfiniteProductListingGroupsQuery(params, options) {
|
|
2701
|
-
const {
|
|
2702
|
-
options: queryOptions,
|
|
2703
|
-
pageSize = 20
|
|
2704
|
-
} = params;
|
|
2705
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2706
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2707
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2708
|
-
"/api/products/listing-groups/query",
|
|
2709
|
-
{
|
|
2710
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2711
|
-
}
|
|
2712
|
-
),
|
|
2713
|
-
initialPageParam: 1,
|
|
2714
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2715
|
-
pages: options?.pages ?? 1,
|
|
2716
|
-
staleTime: options?.staleTime
|
|
2717
|
-
});
|
|
2718
|
-
}
|
|
2719
|
-
useProductDetail(params, options) {
|
|
2720
|
-
const discriminator = "slug" in params ? params.slug : params.id;
|
|
2721
|
-
const enabled = options?.enabled !== false && Boolean(discriminator);
|
|
2722
|
-
return (0, import_react_query4.useQuery)({
|
|
2723
|
-
queryKey: productKeys.detail(params),
|
|
2724
|
-
queryFn: () => this._commerce.product.detail(params),
|
|
2725
|
-
enabled
|
|
2726
|
-
});
|
|
2727
|
-
}
|
|
2728
|
-
useProductDetailBySlug(slug, options) {
|
|
2729
|
-
return this.useProductDetail({ slug }, options);
|
|
2730
|
-
}
|
|
2731
|
-
useProductDetailById(id, options) {
|
|
2732
|
-
return this.useProductDetail({ id }, options);
|
|
2733
|
-
}
|
|
2734
|
-
};
|
|
2735
|
-
|
|
2736
|
-
// src/core/client/client.ts
|
|
2737
|
-
var Client = class {
|
|
2738
|
-
constructor(options) {
|
|
2739
|
-
this.lastRequestId = null;
|
|
2740
|
-
const publishableKey = options.publishableKey;
|
|
2741
|
-
if (!publishableKey) {
|
|
2742
|
-
throw createConfigError("publishableKey is required.");
|
|
2743
|
-
}
|
|
2744
|
-
this.config = { ...options, publishableKey };
|
|
2745
|
-
const metadata = {
|
|
2746
|
-
timestamp: Date.now(),
|
|
2747
|
-
userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
|
|
2748
|
-
};
|
|
2749
|
-
this.state = { metadata };
|
|
2750
|
-
this.queryClient = getQueryClient();
|
|
2751
|
-
this.customer = new CustomerNamespace(
|
|
2752
|
-
this.config.publishableKey,
|
|
2753
|
-
options.customer
|
|
1868
|
+
await handler(body);
|
|
1869
|
+
return new Response(
|
|
1870
|
+
JSON.stringify({ success: true, message: "Webhook processed" }),
|
|
1871
|
+
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
2754
1872
|
);
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
return null;
|
|
2761
|
-
}
|
|
2762
|
-
};
|
|
2763
|
-
const onRequestId = (id) => {
|
|
2764
|
-
this.lastRequestId = id;
|
|
2765
|
-
};
|
|
2766
|
-
this.commerce = new CommerceClient({
|
|
2767
|
-
publishableKey: this.config.publishableKey,
|
|
2768
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2769
|
-
onUnauthorized,
|
|
2770
|
-
onRequestId,
|
|
2771
|
-
customerAuth: this.customer.auth
|
|
2772
|
-
});
|
|
2773
|
-
this.community = new CommunityClient({
|
|
2774
|
-
publishableKey: this.config.publishableKey,
|
|
2775
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2776
|
-
onUnauthorized,
|
|
2777
|
-
onRequestId
|
|
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" }
|
|
2778
1878
|
});
|
|
2779
|
-
const collectionClient = new CollectionClient(
|
|
2780
|
-
this.config.publishableKey,
|
|
2781
|
-
void 0,
|
|
2782
|
-
() => this.customer.auth.getToken(),
|
|
2783
|
-
onUnauthorized,
|
|
2784
|
-
onRequestId
|
|
2785
|
-
);
|
|
2786
|
-
this.collections = new ReadOnlyCollectionClient(
|
|
2787
|
-
this.config.publishableKey,
|
|
2788
|
-
void 0,
|
|
2789
|
-
() => this.customer.auth.getToken(),
|
|
2790
|
-
onUnauthorized,
|
|
2791
|
-
onRequestId
|
|
2792
|
-
);
|
|
2793
|
-
this.query = new QueryHooks(
|
|
2794
|
-
this.queryClient,
|
|
2795
|
-
collectionClient,
|
|
2796
|
-
this.customer.auth,
|
|
2797
|
-
this.commerce
|
|
2798
|
-
);
|
|
2799
1879
|
}
|
|
2800
|
-
getState() {
|
|
2801
|
-
return { ...this.state };
|
|
2802
|
-
}
|
|
2803
|
-
getConfig() {
|
|
2804
|
-
return { ...this.config };
|
|
2805
|
-
}
|
|
2806
|
-
};
|
|
2807
|
-
function createClient(options) {
|
|
2808
|
-
return new Client(options);
|
|
2809
1880
|
}
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
if (typeof window !== "undefined") {
|
|
2816
|
-
throw createConfigError(
|
|
2817
|
-
"ServerClient must not be used in a browser environment. This risks exposing your secretKey in client bundles. Use createClient() for browser code instead."
|
|
2818
|
-
);
|
|
2819
|
-
}
|
|
2820
|
-
if (!options.secretKey) {
|
|
2821
|
-
throw createConfigError("secretKey is required.");
|
|
2822
|
-
}
|
|
2823
|
-
if (!options.publishableKey) {
|
|
2824
|
-
throw createConfigError(
|
|
2825
|
-
"publishableKey is required. It is used for rate limiting and monthly quota enforcement via the X-Publishable-Key header. Get it from Console > Settings > API Keys."
|
|
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}"`
|
|
2826
1886
|
);
|
|
2827
1887
|
}
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
timestamp: Date.now(),
|
|
2831
|
-
userAgent: "Node.js"
|
|
2832
|
-
};
|
|
2833
|
-
this.state = { metadata };
|
|
2834
|
-
const onRequestId = (id) => {
|
|
2835
|
-
this.lastRequestId = id;
|
|
2836
|
-
};
|
|
2837
|
-
const serverOptions = {
|
|
2838
|
-
publishableKey: this.config.publishableKey,
|
|
2839
|
-
secretKey: this.config.secretKey,
|
|
2840
|
-
onRequestId
|
|
2841
|
-
};
|
|
2842
|
-
this.commerce = new ServerCommerceClient(serverOptions);
|
|
2843
|
-
const communityClient = new CommunityClient(serverOptions);
|
|
2844
|
-
const moderationApi = new ModerationApi(serverOptions);
|
|
2845
|
-
this.community = Object.assign(communityClient, {
|
|
2846
|
-
moderation: {
|
|
2847
|
-
banCustomer: moderationApi.banCustomer.bind(moderationApi),
|
|
2848
|
-
unbanCustomer: moderationApi.unbanCustomer.bind(moderationApi)
|
|
2849
|
-
}
|
|
2850
|
-
});
|
|
2851
|
-
this.collections = new ServerCollectionClient(
|
|
2852
|
-
this.config.publishableKey,
|
|
2853
|
-
this.config.secretKey,
|
|
2854
|
-
void 0,
|
|
2855
|
-
void 0,
|
|
2856
|
-
onRequestId
|
|
2857
|
-
);
|
|
2858
|
-
this.queryClient = getQueryClient();
|
|
2859
|
-
this.query = new QueryHooks(this.queryClient, this.collections, void 0, this.commerce);
|
|
2860
|
-
}
|
|
2861
|
-
getState() {
|
|
2862
|
-
return { ...this.state };
|
|
2863
|
-
}
|
|
2864
|
-
getConfig() {
|
|
2865
|
-
const { secretKey: _, ...safeConfig } = this.config;
|
|
2866
|
-
return safeConfig;
|
|
2867
|
-
}
|
|
2868
|
-
};
|
|
2869
|
-
function createServerClient(options) {
|
|
2870
|
-
return new ServerClient(options);
|
|
1888
|
+
return handler(event);
|
|
1889
|
+
};
|
|
2871
1890
|
}
|
|
2872
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
|
+
];
|
|
2011
|
+
|
|
2873
2012
|
// src/core/query/realtime.ts
|
|
2874
2013
|
var INITIAL_RECONNECT_DELAY = 1e3;
|
|
2875
2014
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
@@ -3015,123 +2154,6 @@ var RealtimeConnection = class {
|
|
|
3015
2154
|
}
|
|
3016
2155
|
};
|
|
3017
2156
|
|
|
3018
|
-
// src/core/webhook/index.ts
|
|
3019
|
-
function isValidWebhookEvent(data) {
|
|
3020
|
-
if (typeof data !== "object" || data === null) return false;
|
|
3021
|
-
const obj = data;
|
|
3022
|
-
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
3023
|
-
}
|
|
3024
|
-
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
3025
|
-
function isRecord(value) {
|
|
3026
|
-
return typeof value === "object" && value !== null;
|
|
3027
|
-
}
|
|
3028
|
-
function hasString(value, key) {
|
|
3029
|
-
return typeof value[key] === "string";
|
|
3030
|
-
}
|
|
3031
|
-
function hasStringOrNumber(value, key) {
|
|
3032
|
-
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
3033
|
-
}
|
|
3034
|
-
function isCustomerPasswordResetWebhookEvent(event) {
|
|
3035
|
-
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
|
|
3036
|
-
return false;
|
|
3037
|
-
}
|
|
3038
|
-
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
3039
|
-
}
|
|
3040
|
-
function createCustomerAuthWebhookHandler(handlers) {
|
|
3041
|
-
return async (event) => {
|
|
3042
|
-
if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
|
|
3043
|
-
await handlers.passwordReset(event.data, event);
|
|
3044
|
-
return;
|
|
3045
|
-
}
|
|
3046
|
-
await handlers.unhandled?.(event);
|
|
3047
|
-
};
|
|
3048
|
-
}
|
|
3049
|
-
async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
|
|
3050
|
-
const encoder = new TextEncoder();
|
|
3051
|
-
const key = await crypto.subtle.importKey(
|
|
3052
|
-
"raw",
|
|
3053
|
-
encoder.encode(secret),
|
|
3054
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
3055
|
-
false,
|
|
3056
|
-
["verify"]
|
|
3057
|
-
);
|
|
3058
|
-
if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
|
|
3059
|
-
return false;
|
|
3060
|
-
}
|
|
3061
|
-
const sigBytes = new Uint8Array(
|
|
3062
|
-
(signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
|
|
3063
|
-
);
|
|
3064
|
-
return crypto.subtle.verify(
|
|
3065
|
-
"HMAC",
|
|
3066
|
-
key,
|
|
3067
|
-
sigBytes,
|
|
3068
|
-
encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
|
|
3069
|
-
);
|
|
3070
|
-
}
|
|
3071
|
-
function timestampIsFresh(timestamp, toleranceSeconds) {
|
|
3072
|
-
if (!/^\d+$/.test(timestamp)) return false;
|
|
3073
|
-
const timestampMs = Number(timestamp);
|
|
3074
|
-
if (!Number.isFinite(timestampMs)) return false;
|
|
3075
|
-
const skewMs = Math.abs(Date.now() - timestampMs);
|
|
3076
|
-
return skewMs <= toleranceSeconds * 1e3;
|
|
3077
|
-
}
|
|
3078
|
-
async function handleWebhook(request, handler, options) {
|
|
3079
|
-
try {
|
|
3080
|
-
const rawBody = await request.text();
|
|
3081
|
-
if (options?.secret) {
|
|
3082
|
-
const signature = request.headers.get("x-webhook-signature") || "";
|
|
3083
|
-
const timestamp = request.headers.get("x-webhook-timestamp") || "";
|
|
3084
|
-
const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
|
|
3085
|
-
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
3086
|
-
const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
|
|
3087
|
-
rawBody,
|
|
3088
|
-
options.secret,
|
|
3089
|
-
signature,
|
|
3090
|
-
timestamp,
|
|
3091
|
-
deliveryId
|
|
3092
|
-
);
|
|
3093
|
-
if (!valid) {
|
|
3094
|
-
return new Response(
|
|
3095
|
-
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
3096
|
-
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
3097
|
-
);
|
|
3098
|
-
}
|
|
3099
|
-
} else {
|
|
3100
|
-
console.warn(
|
|
3101
|
-
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
3102
|
-
);
|
|
3103
|
-
}
|
|
3104
|
-
const body = JSON.parse(rawBody);
|
|
3105
|
-
if (!isValidWebhookEvent(body)) {
|
|
3106
|
-
return new Response(
|
|
3107
|
-
JSON.stringify({ error: "Invalid webhook event format" }),
|
|
3108
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
3109
|
-
);
|
|
3110
|
-
}
|
|
3111
|
-
await handler(body);
|
|
3112
|
-
return new Response(
|
|
3113
|
-
JSON.stringify({ success: true, message: "Webhook processed" }),
|
|
3114
|
-
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
3115
|
-
);
|
|
3116
|
-
} catch (error) {
|
|
3117
|
-
console.error("Webhook processing error:", error);
|
|
3118
|
-
return new Response(JSON.stringify({ error: "Internal server error" }), {
|
|
3119
|
-
status: 500,
|
|
3120
|
-
headers: { "Content-Type": "application/json" }
|
|
3121
|
-
});
|
|
3122
|
-
}
|
|
3123
|
-
}
|
|
3124
|
-
function createTypedWebhookHandler(collection, handler) {
|
|
3125
|
-
return async (event) => {
|
|
3126
|
-
if (event.collection !== collection) {
|
|
3127
|
-
throw new Error(
|
|
3128
|
-
`Expected collection "${collection}", got "${event.collection}"`
|
|
3129
|
-
);
|
|
3130
|
-
}
|
|
3131
|
-
return handler(event);
|
|
3132
|
-
};
|
|
3133
|
-
}
|
|
3134
|
-
|
|
3135
2157
|
// src/utils/ecommerce.ts
|
|
3136
2158
|
var ProductSelectionCodecError = class extends Error {
|
|
3137
2159
|
constructor(message) {
|
|
@@ -3416,38 +2438,45 @@ function hasExplicitSelection(selection) {
|
|
|
3416
2438
|
);
|
|
3417
2439
|
}
|
|
3418
2440
|
function assignSelectedValue(matrix, selectedByOptionId, optionId, valueId) {
|
|
3419
|
-
if (valueId == null) return;
|
|
2441
|
+
if (valueId == null) return false;
|
|
3420
2442
|
const normalizedValueId = String(valueId);
|
|
3421
|
-
if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return;
|
|
2443
|
+
if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return false;
|
|
3422
2444
|
selectedByOptionId.set(optionId, normalizedValueId);
|
|
2445
|
+
return true;
|
|
3423
2446
|
}
|
|
3424
2447
|
function assignSelectedValueSlugByOptionId(matrix, selectedByOptionId, optionId, valueSlug) {
|
|
3425
|
-
if (!valueSlug) return;
|
|
2448
|
+
if (!valueSlug) return false;
|
|
3426
2449
|
const option = matrix.optionById.get(optionId);
|
|
3427
|
-
if (!option) return;
|
|
3428
|
-
const
|
|
3429
|
-
|
|
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;
|
|
3430
2461
|
selectedByOptionId.set(optionId, value.id);
|
|
2462
|
+
return true;
|
|
3431
2463
|
}
|
|
3432
2464
|
function assignSelectedValueSlugByOptionSlug(matrix, selectedByOptionId, optionSlug, valueSlug) {
|
|
3433
|
-
if (!valueSlug) return;
|
|
2465
|
+
if (!valueSlug) return false;
|
|
3434
2466
|
const option = matrix.optionBySlug.get(optionSlug);
|
|
3435
|
-
if (!option) return;
|
|
3436
|
-
const
|
|
3437
|
-
|
|
3438
|
-
selectedByOptionId.set(option.id, value.id);
|
|
3439
|
-
}
|
|
3440
|
-
function requireValueSlug(value) {
|
|
3441
|
-
if (value.slug) return value.slug;
|
|
3442
|
-
throw new ProductSelectionCodecError(
|
|
3443
|
-
`Option value "${value.id}" does not have a slug and cannot be used in product selection URLs.`
|
|
3444
|
-
);
|
|
3445
|
-
}
|
|
3446
|
-
function requireOptionSlug(option) {
|
|
3447
|
-
if (option.slug) return option.slug;
|
|
3448
|
-
throw new ProductSelectionCodecError(
|
|
3449
|
-
`Option "${option.id}" does not have a slug and cannot be used in product selection URLs.`
|
|
2467
|
+
if (!option) return false;
|
|
2468
|
+
const values = option.values.filter(
|
|
2469
|
+
(candidate) => candidate.slug === valueSlug
|
|
3450
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;
|
|
3451
2480
|
}
|
|
3452
2481
|
function toSearchParams(search) {
|
|
3453
2482
|
if (!search) return new URLSearchParams();
|
|
@@ -3471,6 +2500,15 @@ function slugLike(value) {
|
|
|
3471
2500
|
}
|
|
3472
2501
|
function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
3473
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
|
+
}
|
|
3474
2512
|
for (const option of matrix.options) {
|
|
3475
2513
|
knownSelectionKeys.add(slugLike(option.slug));
|
|
3476
2514
|
knownSelectionKeys.add(slugLike(option.title));
|
|
@@ -3483,12 +2521,25 @@ function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
|
3483
2521
|
const optionToken = key.slice(4);
|
|
3484
2522
|
if (!optionToken || !matrix.optionBySlug.has(optionToken) && !matrix.optionById.has(optionToken)) {
|
|
3485
2523
|
throw new ProductSelectionCodecError(
|
|
3486
|
-
`Unknown product selection query parameter "${key}". Use opt.<
|
|
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.`
|
|
3487
2530
|
);
|
|
3488
2531
|
}
|
|
2532
|
+
continue;
|
|
2533
|
+
}
|
|
2534
|
+
if (key === "variant") {
|
|
3489
2535
|
if (!value) {
|
|
3490
2536
|
throw new ProductSelectionCodecError(
|
|
3491
|
-
|
|
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}".`
|
|
3492
2543
|
);
|
|
3493
2544
|
}
|
|
3494
2545
|
continue;
|
|
@@ -3496,55 +2547,97 @@ function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
|
3496
2547
|
const keyToken = slugLike(key);
|
|
3497
2548
|
if (knownSelectionKeys.has(keyToken) || value === "" && knownSelectionKeys.has(keyToken)) {
|
|
3498
2549
|
throw new ProductSelectionCodecError(
|
|
3499
|
-
`Ambiguous product selection query parameter "${key}". Use opt.<
|
|
2550
|
+
`Ambiguous product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
|
|
3500
2551
|
);
|
|
3501
2552
|
}
|
|
3502
2553
|
}
|
|
3503
2554
|
}
|
|
3504
|
-
function
|
|
2555
|
+
function emitCompatibilityOptionIdParam(options, event) {
|
|
3505
2556
|
try {
|
|
2557
|
+
if (options?.onCompatibilityOptionIdParam) {
|
|
2558
|
+
options.onCompatibilityOptionIdParam(event);
|
|
2559
|
+
return;
|
|
2560
|
+
}
|
|
3506
2561
|
options?.onLegacyOptionIdParam?.(event);
|
|
3507
2562
|
} catch {
|
|
3508
2563
|
}
|
|
3509
2564
|
}
|
|
3510
2565
|
function assignSearchSelection(matrix, selectedByOptionId, search, options) {
|
|
3511
|
-
if (!search) return;
|
|
2566
|
+
if (!search) return null;
|
|
3512
2567
|
const params = toSearchParams(search);
|
|
3513
2568
|
assertNoAmbiguousSelectionParams(matrix, params);
|
|
3514
|
-
|
|
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()) {
|
|
3515
2583
|
if (!key.startsWith("opt.")) continue;
|
|
3516
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
|
+
}
|
|
3517
2614
|
const optionBySlug = matrix.optionBySlug.get(optionToken);
|
|
3518
2615
|
if (optionBySlug) {
|
|
3519
|
-
assignSelectedValueSlugByOptionSlug(
|
|
2616
|
+
if (assignSelectedValueSlugByOptionSlug(
|
|
3520
2617
|
matrix,
|
|
3521
2618
|
selectedByOptionId,
|
|
3522
2619
|
optionBySlug.slug,
|
|
3523
|
-
|
|
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.`
|
|
3524
2631
|
);
|
|
3525
|
-
continue;
|
|
3526
|
-
}
|
|
3527
|
-
const legacyOption = matrix.optionById.get(optionToken);
|
|
3528
|
-
if (!legacyOption) continue;
|
|
3529
|
-
const before = selectedByOptionId.get(legacyOption.id);
|
|
3530
|
-
assignSelectedValueSlugByOptionId(
|
|
3531
|
-
matrix,
|
|
3532
|
-
selectedByOptionId,
|
|
3533
|
-
legacyOption.id,
|
|
3534
|
-
valueSlug
|
|
3535
|
-
);
|
|
3536
|
-
if (selectedByOptionId.get(legacyOption.id) !== before) {
|
|
3537
|
-
emitLegacyOptionIdParam(options, {
|
|
3538
|
-
optionId: legacyOption.id,
|
|
3539
|
-
optionSlug: legacyOption.slug,
|
|
3540
|
-
valueSlug,
|
|
3541
|
-
searchParam: key
|
|
3542
|
-
});
|
|
3543
2632
|
}
|
|
3544
2633
|
}
|
|
2634
|
+
return null;
|
|
3545
2635
|
}
|
|
3546
2636
|
function normalizeProductSelection(detail, selection = {}, options) {
|
|
3547
2637
|
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2638
|
+
return normalizeProductSelectionFromMatrix(matrix, selection, options);
|
|
2639
|
+
}
|
|
2640
|
+
function normalizeProductSelectionFromMatrix(matrix, selection = {}, options) {
|
|
3548
2641
|
const selectedByOptionId = /* @__PURE__ */ new Map();
|
|
3549
2642
|
const variantSelection = getVariantSelection(matrix, selection.variantId);
|
|
3550
2643
|
const variantId = variantSelection?.id ?? null;
|
|
@@ -3560,7 +2653,12 @@ function normalizeProductSelection(detail, selection = {}, options) {
|
|
|
3560
2653
|
if (!optionId) continue;
|
|
3561
2654
|
selectedByOptionId.set(optionId, valueId);
|
|
3562
2655
|
}
|
|
3563
|
-
|
|
2656
|
+
const searchVariantId = assignSearchSelection(
|
|
2657
|
+
matrix,
|
|
2658
|
+
selectedByOptionId,
|
|
2659
|
+
selection.search,
|
|
2660
|
+
options
|
|
2661
|
+
);
|
|
3564
2662
|
for (const [rawOptionId, rawSelection] of Object.entries(
|
|
3565
2663
|
selection.byOptionId ?? {}
|
|
3566
2664
|
)) {
|
|
@@ -3637,7 +2735,7 @@ function normalizeProductSelection(detail, selection = {}, options) {
|
|
|
3637
2735
|
byOptionSlug,
|
|
3638
2736
|
byOptionId,
|
|
3639
2737
|
valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
|
|
3640
|
-
variantId
|
|
2738
|
+
variantId: searchVariantId ?? variantId
|
|
3641
2739
|
};
|
|
3642
2740
|
}
|
|
3643
2741
|
function parseProductSelection(detail, search, options) {
|
|
@@ -3645,15 +2743,31 @@ function parseProductSelection(detail, search, options) {
|
|
|
3645
2743
|
}
|
|
3646
2744
|
function stringifyProductSelection(detail, selection = {}, options) {
|
|
3647
2745
|
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
3648
|
-
const normalized =
|
|
2746
|
+
const normalized = normalizeProductSelectionFromMatrix(
|
|
2747
|
+
matrix,
|
|
2748
|
+
selection,
|
|
2749
|
+
options
|
|
2750
|
+
);
|
|
3649
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
|
+
}
|
|
3650
2764
|
for (const optionId of matrix.optionIds) {
|
|
3651
2765
|
const valueId = normalized.byOptionId[optionId];
|
|
3652
2766
|
if (!valueId) continue;
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
|
|
3656
|
-
params.append(`opt.${
|
|
2767
|
+
if (!matrix.optionById.has(optionId) || !matrix.valueById.has(valueId)) {
|
|
2768
|
+
continue;
|
|
2769
|
+
}
|
|
2770
|
+
params.append(`opt.${optionId}`, valueId);
|
|
3657
2771
|
}
|
|
3658
2772
|
return params.toString();
|
|
3659
2773
|
}
|
|
@@ -3754,11 +2868,39 @@ function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
|
3754
2868
|
availableStock: null
|
|
3755
2869
|
};
|
|
3756
2870
|
}
|
|
3757
|
-
function
|
|
3758
|
-
const
|
|
3759
|
-
const
|
|
3760
|
-
const
|
|
3761
|
-
|
|
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,
|
|
3762
2904
|
effectiveSelection,
|
|
3763
2905
|
options
|
|
3764
2906
|
);
|
|
@@ -3793,9 +2935,17 @@ function resolveProductSelection(detail, selection = {}, options) {
|
|
|
3793
2935
|
option.values.map((value) => ({
|
|
3794
2936
|
valueId: value.id,
|
|
3795
2937
|
value: value.label,
|
|
3796
|
-
slug:
|
|
2938
|
+
slug: value.slug ?? "",
|
|
3797
2939
|
selected: normalizedSelection.byOptionId[option.id] === value.id,
|
|
3798
2940
|
available: availableValueIds.has(value.id),
|
|
2941
|
+
...buildAvailableValueStock(
|
|
2942
|
+
getCandidateVariantsForValue(
|
|
2943
|
+
matrix,
|
|
2944
|
+
option.id,
|
|
2945
|
+
value.id,
|
|
2946
|
+
normalizedSelection.valueIds
|
|
2947
|
+
)
|
|
2948
|
+
),
|
|
3799
2949
|
swatchColor: value.swatchColor ?? null,
|
|
3800
2950
|
thumbnail: value.thumbnail ?? null,
|
|
3801
2951
|
images: value.images ?? null
|
|
@@ -3823,7 +2973,12 @@ function resolveProductSelection(detail, selection = {}, options) {
|
|
|
3823
2973
|
allOptionsSelected,
|
|
3824
2974
|
price: buildSelectionPrice(priceVariants),
|
|
3825
2975
|
media: buildSelectionMedia(
|
|
3826
|
-
|
|
2976
|
+
{
|
|
2977
|
+
images,
|
|
2978
|
+
listing: {
|
|
2979
|
+
primaryImage: listing.primaryImage ?? null
|
|
2980
|
+
}
|
|
2981
|
+
},
|
|
3827
2982
|
selectedVariant,
|
|
3828
2983
|
matchingVariants,
|
|
3829
2984
|
selectedValues
|
|
@@ -3831,6 +2986,100 @@ function resolveProductSelection(detail, selection = {}, options) {
|
|
|
3831
2986
|
stock: buildSelectionStock(selectedVariant, matchingVariants)
|
|
3832
2987
|
};
|
|
3833
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
|
+
}
|
|
3834
3083
|
function compareVariantOrder(a, b) {
|
|
3835
3084
|
const aOrder = Number(a._order ?? Number.MAX_SAFE_INTEGER);
|
|
3836
3085
|
const bOrder = Number(b._order ?? Number.MAX_SAFE_INTEGER);
|
|
@@ -4210,4 +3459,9 @@ function createAnalytics(config) {
|
|
|
4210
3459
|
}
|
|
4211
3460
|
};
|
|
4212
3461
|
}
|
|
3462
|
+
|
|
3463
|
+
// src/index.ts
|
|
3464
|
+
function createClient2(options) {
|
|
3465
|
+
return createClient(options);
|
|
3466
|
+
}
|
|
4213
3467
|
//# sourceMappingURL=index.cjs.map
|