@01.software/sdk 0.28.0 → 0.30.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +273 -73
- package/dist/analytics/react.cjs +4 -1
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js +4 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs +4 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js +4 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +1476 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +28 -0
- package/dist/client.d.ts +28 -0
- package/dist/client.js +1453 -0
- package/dist/client.js.map +1 -0
- package/dist/collection-client-B9d9kr1d.d.ts +218 -0
- package/dist/collection-client-QPbwimkU.d.cts +218 -0
- package/dist/{const-mdQQtIOz.d.ts → const-B75IFDRi.d.ts} +2 -4
- package/dist/{const-Cz9Ki_I7.d.cts → const-VZuk2tWc.d.cts} +2 -4
- package/dist/index-B2WbhEgT.d.cts +106 -0
- package/dist/index-B2WbhEgT.d.ts +106 -0
- package/dist/index.cjs +1291 -1501
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -115
- package/dist/index.d.ts +11 -115
- package/dist/index.js +1292 -1520
- package/dist/index.js.map +1 -1
- package/dist/metadata.cjs +91 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +58 -0
- package/dist/metadata.d.ts +58 -0
- package/dist/metadata.js +68 -0
- package/dist/metadata.js.map +1 -0
- package/dist/{payload-types-BrSYb-sh.d.cts → payload-types-DPjO_IbQ.d.cts} +17 -6
- package/dist/{payload-types-BrSYb-sh.d.ts → payload-types-DPjO_IbQ.d.ts} +17 -6
- package/dist/query.cjs +1791 -0
- package/dist/query.cjs.map +1 -0
- package/dist/query.d.cts +244 -0
- package/dist/query.d.ts +244 -0
- package/dist/query.js +1786 -0
- package/dist/query.js.map +1 -0
- package/dist/realtime.cjs +4 -1
- package/dist/realtime.cjs.map +1 -1
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/realtime.js +4 -1
- package/dist/realtime.js.map +1 -1
- package/dist/{server-BINWywT8.d.cts → server-CrsPyqEc.d.cts} +14 -31
- package/dist/{server-BINWywT8.d.ts → server-CrsPyqEc.d.ts} +14 -31
- package/dist/server.cjs +300 -844
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +112 -7
- package/dist/server.d.ts +112 -7
- package/dist/server.js +300 -862
- package/dist/server.js.map +1 -1
- package/dist/{types-BLUb4cYq.d.ts → types-1fBLrYU7.d.ts} +1 -1
- package/dist/{types-CW4PaIL7.d.cts → types-BwT0eeaz.d.cts} +1 -1
- package/dist/types-Dlb2mwpX.d.cts +1249 -0
- package/dist/types-DuSKPiY5.d.ts +1249 -0
- package/dist/ui/canvas/server.cjs +7 -6
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +1 -3
- package/dist/ui/canvas/server.d.ts +1 -3
- package/dist/ui/canvas/server.js +7 -6
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +11 -10
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +29 -6
- package/dist/ui/canvas.d.ts +29 -6
- package/dist/ui/canvas.js +11 -10
- package/dist/ui/canvas.js.map +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +82 -13
- package/dist/server-C2Q9R-Lu.d.ts +0 -1662
- package/dist/server-D369bCVJ.d.cts +0 -1662
package/dist/index.js
CHANGED
|
@@ -1,242 +1,3 @@
|
|
|
1
|
-
// src/utils/types.ts
|
|
2
|
-
var resolveRelation = (ref) => {
|
|
3
|
-
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
4
|
-
return null;
|
|
5
|
-
return ref;
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
// src/core/metadata/index.ts
|
|
9
|
-
function extractSeo(doc) {
|
|
10
|
-
const seo = doc.seo ?? {};
|
|
11
|
-
const og = seo.openGraph ?? {};
|
|
12
|
-
return {
|
|
13
|
-
title: seo.title ?? doc.title ?? null,
|
|
14
|
-
description: seo.description ?? null,
|
|
15
|
-
noIndex: seo.noIndex ?? null,
|
|
16
|
-
canonical: seo.canonical ?? null,
|
|
17
|
-
openGraph: {
|
|
18
|
-
title: og.title ?? null,
|
|
19
|
-
description: og.description ?? null,
|
|
20
|
-
image: og.image ?? null
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
function generateMetadata(input, options) {
|
|
25
|
-
const title = input.title ?? void 0;
|
|
26
|
-
const description = input.description ?? void 0;
|
|
27
|
-
const ogTitle = input.openGraph?.title ?? title;
|
|
28
|
-
const ogDescription = input.openGraph?.description ?? description;
|
|
29
|
-
const image = resolveMetaImage(input.openGraph?.image);
|
|
30
|
-
return {
|
|
31
|
-
title,
|
|
32
|
-
description,
|
|
33
|
-
...input.noIndex && { robots: { index: false, follow: false } },
|
|
34
|
-
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
35
|
-
openGraph: {
|
|
36
|
-
...ogTitle && { title: ogTitle },
|
|
37
|
-
...ogDescription && { description: ogDescription },
|
|
38
|
-
...options?.siteName && { siteName: options.siteName },
|
|
39
|
-
...image && { images: [image] }
|
|
40
|
-
},
|
|
41
|
-
twitter: {
|
|
42
|
-
card: image ? "summary_large_image" : "summary",
|
|
43
|
-
...ogTitle && { title: ogTitle },
|
|
44
|
-
...ogDescription && { description: ogDescription },
|
|
45
|
-
...image && { images: [image.url] }
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
function resolveMetaImage(ref) {
|
|
50
|
-
const image = resolveRelation(ref);
|
|
51
|
-
if (!image) return null;
|
|
52
|
-
const sized = image.sizes?.["1536"];
|
|
53
|
-
const url = sized?.url || image.url;
|
|
54
|
-
if (!url) return null;
|
|
55
|
-
const width = sized?.url ? sized.width : image.width;
|
|
56
|
-
const height = sized?.url ? sized.height : image.height;
|
|
57
|
-
return {
|
|
58
|
-
url,
|
|
59
|
-
...width && { width },
|
|
60
|
-
...height && { height },
|
|
61
|
-
...image.alt && { alt: image.alt }
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// src/core/collection/query-builder.ts
|
|
66
|
-
var ReadOnlyCollectionQueryBuilder = class {
|
|
67
|
-
constructor(api, collection) {
|
|
68
|
-
this.api = api;
|
|
69
|
-
this.collection = collection;
|
|
70
|
-
}
|
|
71
|
-
async find(options) {
|
|
72
|
-
return this.api.requestFind(
|
|
73
|
-
`/api/${String(this.collection)}`,
|
|
74
|
-
options
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
async findById(id, options) {
|
|
78
|
-
return this.api.requestFindById(
|
|
79
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
80
|
-
options
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
async count(options) {
|
|
84
|
-
return this.api.requestCount(
|
|
85
|
-
`/api/${String(this.collection)}/count`,
|
|
86
|
-
options
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
async findMetadata(options, metadataOptions) {
|
|
90
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
91
|
-
const doc = docs[0];
|
|
92
|
-
if (!doc) return null;
|
|
93
|
-
return generateMetadata(
|
|
94
|
-
extractSeo(doc),
|
|
95
|
-
metadataOptions
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
async findMetadataById(id, metadataOptions) {
|
|
99
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
100
|
-
return generateMetadata(
|
|
101
|
-
extractSeo(doc),
|
|
102
|
-
metadataOptions
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
var CollectionQueryBuilder = class {
|
|
107
|
-
constructor(api, collection) {
|
|
108
|
-
this.api = api;
|
|
109
|
-
this.collection = collection;
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Find documents (list query)
|
|
113
|
-
* GET /api/{collection}
|
|
114
|
-
* @returns Payload CMS find response with docs array and pagination
|
|
115
|
-
*/
|
|
116
|
-
async find(options) {
|
|
117
|
-
return this.api.requestFind(
|
|
118
|
-
`/api/${String(this.collection)}`,
|
|
119
|
-
options
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Find document by ID
|
|
124
|
-
* GET /api/{collection}/{id}
|
|
125
|
-
* @returns Document object directly (no wrapper)
|
|
126
|
-
*/
|
|
127
|
-
async findById(id, options) {
|
|
128
|
-
return this.api.requestFindById(
|
|
129
|
-
`/api/${String(this.collection)}/${String(id)}`,
|
|
130
|
-
options
|
|
131
|
-
);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Create a new document
|
|
135
|
-
* POST /api/{collection}
|
|
136
|
-
* @returns Payload CMS mutation response with doc and message
|
|
137
|
-
*/
|
|
138
|
-
async create(data, options) {
|
|
139
|
-
const endpoint = `/api/${String(this.collection)}`;
|
|
140
|
-
if (options?.file) {
|
|
141
|
-
return this.api.requestCreateWithFile(
|
|
142
|
-
endpoint,
|
|
143
|
-
data,
|
|
144
|
-
options.file,
|
|
145
|
-
options.filename
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
return this.api.requestCreate(endpoint, data);
|
|
149
|
-
}
|
|
150
|
-
/**
|
|
151
|
-
* Update a document by ID
|
|
152
|
-
* PATCH /api/{collection}/{id}
|
|
153
|
-
* @returns Payload CMS mutation response with doc and message
|
|
154
|
-
*/
|
|
155
|
-
async update(id, data, options) {
|
|
156
|
-
const endpoint = `/api/${String(this.collection)}/${String(id)}`;
|
|
157
|
-
if (options?.file) {
|
|
158
|
-
return this.api.requestUpdateWithFile(
|
|
159
|
-
endpoint,
|
|
160
|
-
data,
|
|
161
|
-
options.file,
|
|
162
|
-
options.filename
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
return this.api.requestUpdate(endpoint, data);
|
|
166
|
-
}
|
|
167
|
-
/**
|
|
168
|
-
* Count documents
|
|
169
|
-
* GET /api/{collection}/count
|
|
170
|
-
* @returns Count response with totalDocs
|
|
171
|
-
*/
|
|
172
|
-
async count(options) {
|
|
173
|
-
return this.api.requestCount(
|
|
174
|
-
`/api/${String(this.collection)}/count`,
|
|
175
|
-
options
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Find first matching document and return its Next.js Metadata.
|
|
180
|
-
* Applies depth: 1 (SEO image populate) and limit: 1 automatically.
|
|
181
|
-
* @returns Metadata or null if no document matches
|
|
182
|
-
*/
|
|
183
|
-
async findMetadata(options, metadataOptions) {
|
|
184
|
-
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
185
|
-
const doc = docs[0];
|
|
186
|
-
if (!doc) return null;
|
|
187
|
-
return generateMetadata(
|
|
188
|
-
extractSeo(doc),
|
|
189
|
-
metadataOptions
|
|
190
|
-
);
|
|
191
|
-
}
|
|
192
|
-
/**
|
|
193
|
-
* Find document by ID and return its Next.js Metadata.
|
|
194
|
-
* Applies depth: 1 (SEO image populate) automatically.
|
|
195
|
-
* @returns Metadata (throws on 404)
|
|
196
|
-
*/
|
|
197
|
-
async findMetadataById(id, metadataOptions) {
|
|
198
|
-
const doc = await this.findById(id, { depth: 1 });
|
|
199
|
-
return generateMetadata(
|
|
200
|
-
extractSeo(doc),
|
|
201
|
-
metadataOptions
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Update multiple documents (bulk update)
|
|
206
|
-
* PATCH /api/{collection}
|
|
207
|
-
* @returns Payload CMS find response with updated docs
|
|
208
|
-
*/
|
|
209
|
-
async updateMany(where, data) {
|
|
210
|
-
return this.api.requestUpdateMany(
|
|
211
|
-
`/api/${String(this.collection)}`,
|
|
212
|
-
{ where, data }
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* Delete a document by ID
|
|
217
|
-
* DELETE /api/{collection}/{id}
|
|
218
|
-
* @returns Deleted document object directly (no wrapper)
|
|
219
|
-
*/
|
|
220
|
-
async remove(id) {
|
|
221
|
-
return this.api.requestDelete(
|
|
222
|
-
`/api/${String(this.collection)}/${String(id)}`
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Delete multiple documents (bulk delete)
|
|
227
|
-
* DELETE /api/{collection}
|
|
228
|
-
* @returns Payload CMS find response with deleted docs
|
|
229
|
-
*/
|
|
230
|
-
async removeMany(where) {
|
|
231
|
-
return this.api.requestDeleteMany(
|
|
232
|
-
`/api/${String(this.collection)}`,
|
|
233
|
-
{ where }
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
};
|
|
237
|
-
var ServerCollectionQueryBuilder = class extends CollectionQueryBuilder {
|
|
238
|
-
};
|
|
239
|
-
|
|
240
1
|
// src/core/collection/http-client.ts
|
|
241
2
|
import { stringify } from "qs-esm";
|
|
242
3
|
|
|
@@ -430,7 +191,10 @@ function requirePublishableKeyForSecret(apiName, publishableKey, secretKey) {
|
|
|
430
191
|
}
|
|
431
192
|
|
|
432
193
|
// src/core/client/types.ts
|
|
433
|
-
function resolveApiUrl() {
|
|
194
|
+
function resolveApiUrl(apiUrl) {
|
|
195
|
+
if (apiUrl) {
|
|
196
|
+
return apiUrl.replace(/\/$/, "");
|
|
197
|
+
}
|
|
434
198
|
if (typeof process !== "undefined" && process.env) {
|
|
435
199
|
const envUrl = process.env.SOFTWARE_API_URL || process.env.NEXT_PUBLIC_SOFTWARE_API_URL;
|
|
436
200
|
if (envUrl) {
|
|
@@ -611,6 +375,7 @@ function createHttpStatusError(status, parsed, details, requestId) {
|
|
|
611
375
|
}
|
|
612
376
|
async function httpFetch(url, options) {
|
|
613
377
|
const {
|
|
378
|
+
apiUrl,
|
|
614
379
|
publishableKey,
|
|
615
380
|
secretKey,
|
|
616
381
|
customerToken,
|
|
@@ -620,7 +385,7 @@ async function httpFetch(url, options) {
|
|
|
620
385
|
onUnauthorized,
|
|
621
386
|
...requestInit
|
|
622
387
|
} = options || {};
|
|
623
|
-
const baseUrl = resolveApiUrl();
|
|
388
|
+
const baseUrl = resolveApiUrl(apiUrl);
|
|
624
389
|
const retryConfig = {
|
|
625
390
|
maxRetries: retry?.maxRetries ?? 3,
|
|
626
391
|
retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
|
|
@@ -809,7 +574,7 @@ async function httpFetch(url, options) {
|
|
|
809
574
|
|
|
810
575
|
// src/core/collection/http-client.ts
|
|
811
576
|
var HttpClient = class {
|
|
812
|
-
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId) {
|
|
577
|
+
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
813
578
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
814
579
|
"CollectionClient",
|
|
815
580
|
publishableKey,
|
|
@@ -819,9 +584,11 @@ var HttpClient = class {
|
|
|
819
584
|
this.getCustomerToken = getCustomerToken;
|
|
820
585
|
this.onUnauthorized = onUnauthorized;
|
|
821
586
|
this.onRequestId = onRequestId;
|
|
587
|
+
this.apiUrl = apiUrl;
|
|
822
588
|
}
|
|
823
589
|
get defaultOptions() {
|
|
824
590
|
const opts = {
|
|
591
|
+
apiUrl: this.apiUrl,
|
|
825
592
|
publishableKey: this.publishableKey,
|
|
826
593
|
secretKey: this.secretKey
|
|
827
594
|
};
|
|
@@ -939,159 +706,113 @@ var HttpClient = class {
|
|
|
939
706
|
}
|
|
940
707
|
};
|
|
941
708
|
|
|
942
|
-
// src/
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
709
|
+
// src/utils/types.ts
|
|
710
|
+
var resolveRelation = (ref) => {
|
|
711
|
+
if (typeof ref === "string" || typeof ref === "number" || ref === null || ref === void 0)
|
|
712
|
+
return null;
|
|
713
|
+
return ref;
|
|
714
|
+
};
|
|
715
|
+
|
|
716
|
+
// src/core/metadata/index.ts
|
|
717
|
+
function extractSeo(doc) {
|
|
718
|
+
const seo = doc.seo ?? {};
|
|
719
|
+
const og = seo.openGraph ?? {};
|
|
720
|
+
return {
|
|
721
|
+
title: seo.title ?? doc.title ?? null,
|
|
722
|
+
description: seo.description ?? null,
|
|
723
|
+
noIndex: seo.noIndex ?? null,
|
|
724
|
+
canonical: seo.canonical ?? null,
|
|
725
|
+
openGraph: {
|
|
726
|
+
title: og.title ?? null,
|
|
727
|
+
description: og.description ?? null,
|
|
728
|
+
image: og.image ?? null
|
|
729
|
+
}
|
|
730
|
+
};
|
|
950
731
|
}
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
732
|
+
function generateMetadata(input, options) {
|
|
733
|
+
const title = input.title ?? void 0;
|
|
734
|
+
const description = input.description ?? void 0;
|
|
735
|
+
const ogTitle = input.openGraph?.title ?? title;
|
|
736
|
+
const ogDescription = input.openGraph?.description ?? description;
|
|
737
|
+
const image = resolveMetaImage(input.openGraph?.image);
|
|
738
|
+
return {
|
|
739
|
+
title,
|
|
740
|
+
description,
|
|
741
|
+
...input.noIndex && { robots: { index: false, follow: false } },
|
|
742
|
+
...input.canonical && { alternates: { canonical: input.canonical } },
|
|
743
|
+
openGraph: {
|
|
744
|
+
...ogTitle && { title: ogTitle },
|
|
745
|
+
...ogDescription && { description: ogDescription },
|
|
746
|
+
...options?.siteName && { siteName: options.siteName },
|
|
747
|
+
...image && { images: [image] }
|
|
748
|
+
},
|
|
749
|
+
twitter: {
|
|
750
|
+
card: image ? "summary_large_image" : "summary",
|
|
751
|
+
...ogTitle && { title: ogTitle },
|
|
752
|
+
...ogDescription && { description: ogDescription },
|
|
753
|
+
...image && { images: [image.url] }
|
|
754
|
+
}
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
function resolveMetaImage(ref) {
|
|
758
|
+
const image = resolveRelation(ref);
|
|
759
|
+
if (!image) return null;
|
|
760
|
+
const sized = image.sizes?.["1536"];
|
|
761
|
+
const url = sized?.url || image.url;
|
|
762
|
+
if (!url) return null;
|
|
763
|
+
const width = sized?.url ? sized.width : image.width;
|
|
764
|
+
const height = sized?.url ? sized.height : image.height;
|
|
765
|
+
return {
|
|
766
|
+
url,
|
|
767
|
+
...width && { width },
|
|
768
|
+
...height && { height },
|
|
769
|
+
...image.alt && { alt: image.alt }
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// src/core/collection/query-builder.ts
|
|
774
|
+
var ReadOnlyCollectionQueryBuilder = class {
|
|
775
|
+
constructor(api, collection) {
|
|
776
|
+
this.api = api;
|
|
777
|
+
this.collection = collection;
|
|
954
778
|
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
* GET /api/{collection}
|
|
961
|
-
*/
|
|
962
|
-
async requestFind(endpoint, options) {
|
|
963
|
-
const url = this.buildUrl(endpoint, options);
|
|
964
|
-
const response = await this.fetchWithTracking(url, {
|
|
965
|
-
...this.defaultOptions,
|
|
966
|
-
method: "GET"
|
|
967
|
-
});
|
|
968
|
-
return this.parseFindResponse(response);
|
|
969
|
-
}
|
|
970
|
-
/**
|
|
971
|
-
* Find-like response from a custom endpoint
|
|
972
|
-
* POST /api/...custom-endpoint
|
|
973
|
-
*/
|
|
974
|
-
async requestFindEndpoint(endpoint, data) {
|
|
975
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
976
|
-
...this.defaultOptions,
|
|
977
|
-
method: "POST",
|
|
978
|
-
body: data ? JSON.stringify(data) : void 0
|
|
979
|
-
});
|
|
980
|
-
return this.parseFindResponse(response);
|
|
981
|
-
}
|
|
982
|
-
/**
|
|
983
|
-
* Find document by ID
|
|
984
|
-
* GET /api/{collection}/{id}
|
|
985
|
-
*/
|
|
986
|
-
async requestFindById(endpoint, options) {
|
|
987
|
-
const url = this.buildUrl(endpoint, options);
|
|
988
|
-
const response = await this.fetchWithTracking(url, {
|
|
989
|
-
...this.defaultOptions,
|
|
990
|
-
method: "GET"
|
|
991
|
-
});
|
|
992
|
-
return this.parseDocumentResponse(response);
|
|
993
|
-
}
|
|
994
|
-
/**
|
|
995
|
-
* Create document
|
|
996
|
-
* POST /api/{collection}
|
|
997
|
-
*/
|
|
998
|
-
async requestCreate(endpoint, data) {
|
|
999
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1000
|
-
...this.defaultOptions,
|
|
1001
|
-
method: "POST",
|
|
1002
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1003
|
-
});
|
|
1004
|
-
return this.parseMutationResponse(response);
|
|
1005
|
-
}
|
|
1006
|
-
/**
|
|
1007
|
-
* Update document
|
|
1008
|
-
* PATCH /api/{collection}/{id}
|
|
1009
|
-
*/
|
|
1010
|
-
async requestUpdate(endpoint, data) {
|
|
1011
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1012
|
-
...this.defaultOptions,
|
|
1013
|
-
method: "PATCH",
|
|
1014
|
-
body: data ? JSON.stringify(data) : void 0
|
|
1015
|
-
});
|
|
1016
|
-
return this.parseMutationResponse(response);
|
|
1017
|
-
}
|
|
1018
|
-
/**
|
|
1019
|
-
* Count documents
|
|
1020
|
-
* GET /api/{collection}/count
|
|
1021
|
-
*/
|
|
1022
|
-
async requestCount(endpoint, options) {
|
|
1023
|
-
const url = this.buildUrl(endpoint, options);
|
|
1024
|
-
const response = await this.fetchWithTracking(url, {
|
|
1025
|
-
...this.defaultOptions,
|
|
1026
|
-
method: "GET"
|
|
1027
|
-
});
|
|
1028
|
-
return this.parseDocumentResponse(response);
|
|
1029
|
-
}
|
|
1030
|
-
/**
|
|
1031
|
-
* Update multiple documents (bulk update)
|
|
1032
|
-
* PATCH /api/{collection}
|
|
1033
|
-
*/
|
|
1034
|
-
async requestUpdateMany(endpoint, data) {
|
|
1035
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1036
|
-
...this.defaultOptions,
|
|
1037
|
-
method: "PATCH",
|
|
1038
|
-
body: JSON.stringify(data)
|
|
1039
|
-
});
|
|
1040
|
-
return this.parseFindResponse(response);
|
|
1041
|
-
}
|
|
1042
|
-
/**
|
|
1043
|
-
* Delete document
|
|
1044
|
-
* DELETE /api/{collection}/{id}
|
|
1045
|
-
*/
|
|
1046
|
-
async requestDelete(endpoint) {
|
|
1047
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1048
|
-
...this.defaultOptions,
|
|
1049
|
-
method: "DELETE"
|
|
1050
|
-
});
|
|
1051
|
-
return this.parseDocumentResponse(response);
|
|
779
|
+
async find(options) {
|
|
780
|
+
return this.api.requestFind(
|
|
781
|
+
`/api/${String(this.collection)}`,
|
|
782
|
+
options
|
|
783
|
+
);
|
|
1052
784
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1059
|
-
...this.defaultOptions,
|
|
1060
|
-
method: "DELETE",
|
|
1061
|
-
body: JSON.stringify(data)
|
|
1062
|
-
});
|
|
1063
|
-
return this.parseFindResponse(response);
|
|
785
|
+
async findById(id, options) {
|
|
786
|
+
return this.api.requestFindById(
|
|
787
|
+
`/api/${String(this.collection)}/${String(id)}`,
|
|
788
|
+
options
|
|
789
|
+
);
|
|
1064
790
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
const response = await this.fetchWithTracking(endpoint, {
|
|
1071
|
-
...this.defaultOptions,
|
|
1072
|
-
method: "POST",
|
|
1073
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1074
|
-
});
|
|
1075
|
-
return this.parseMutationResponse(response);
|
|
791
|
+
async count(options) {
|
|
792
|
+
return this.api.requestCount(
|
|
793
|
+
`/api/${String(this.collection)}/count`,
|
|
794
|
+
options
|
|
795
|
+
);
|
|
1076
796
|
}
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
body: buildPayloadFormData(data, file, filename)
|
|
1086
|
-
});
|
|
1087
|
-
return this.parseMutationResponse(response);
|
|
797
|
+
async findMetadata(options, metadataOptions) {
|
|
798
|
+
const { docs } = await this.find({ ...options, limit: 1, depth: 1 });
|
|
799
|
+
const doc = docs[0];
|
|
800
|
+
if (!doc) return null;
|
|
801
|
+
return generateMetadata(
|
|
802
|
+
extractSeo(doc),
|
|
803
|
+
metadataOptions
|
|
804
|
+
);
|
|
1088
805
|
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
806
|
+
async findMetadataById(id, metadataOptions) {
|
|
807
|
+
const doc = await this.findById(id, { depth: 1 });
|
|
808
|
+
return generateMetadata(
|
|
809
|
+
extractSeo(doc),
|
|
810
|
+
metadataOptions
|
|
811
|
+
);
|
|
1093
812
|
}
|
|
1094
813
|
};
|
|
814
|
+
|
|
815
|
+
// src/core/collection/collection-client.ts
|
|
1095
816
|
var ReadOnlyCollectionClient = class extends HttpClient {
|
|
1096
817
|
from(collection) {
|
|
1097
818
|
return new ReadOnlyCollectionQueryBuilder(this, collection);
|
|
@@ -1122,126 +843,6 @@ var ReadOnlyCollectionClient = class extends HttpClient {
|
|
|
1122
843
|
}
|
|
1123
844
|
};
|
|
1124
845
|
|
|
1125
|
-
// src/core/collection/const.ts
|
|
1126
|
-
var INTERNAL_COLLECTIONS = [
|
|
1127
|
-
"users",
|
|
1128
|
-
"payload-kv",
|
|
1129
|
-
"payload-locked-documents",
|
|
1130
|
-
"payload-preferences",
|
|
1131
|
-
"payload-migrations",
|
|
1132
|
-
"payload-folders",
|
|
1133
|
-
"field-configs",
|
|
1134
|
-
"system-media",
|
|
1135
|
-
"track-assets",
|
|
1136
|
-
"audiences",
|
|
1137
|
-
"email-logs",
|
|
1138
|
-
"api-usage",
|
|
1139
|
-
"tenant-analytics-daily",
|
|
1140
|
-
"tenant-web-analytics-config",
|
|
1141
|
-
"analytics-event-schemas",
|
|
1142
|
-
"subscriptions",
|
|
1143
|
-
"billing-history",
|
|
1144
|
-
"inventory-reservations",
|
|
1145
|
-
"order-status-logs",
|
|
1146
|
-
"api-keys",
|
|
1147
|
-
"personal-access-tokens",
|
|
1148
|
-
"tenant-entitlements",
|
|
1149
|
-
"tenant-purge-jobs",
|
|
1150
|
-
"direct-upload-sessions",
|
|
1151
|
-
"webhook-events",
|
|
1152
|
-
"webhook-deliveries",
|
|
1153
|
-
"audit-logs",
|
|
1154
|
-
"plans",
|
|
1155
|
-
"webhooks",
|
|
1156
|
-
"event-registrations"
|
|
1157
|
-
];
|
|
1158
|
-
var COLLECTIONS = [
|
|
1159
|
-
"tenants",
|
|
1160
|
-
"tenant-metadata",
|
|
1161
|
-
"tenant-logos",
|
|
1162
|
-
"products",
|
|
1163
|
-
"product-variants",
|
|
1164
|
-
"product-options",
|
|
1165
|
-
"product-option-values",
|
|
1166
|
-
"product-categories",
|
|
1167
|
-
"product-tags",
|
|
1168
|
-
"product-collections",
|
|
1169
|
-
"brands",
|
|
1170
|
-
"brand-logos",
|
|
1171
|
-
"orders",
|
|
1172
|
-
"order-items",
|
|
1173
|
-
"returns",
|
|
1174
|
-
"return-items",
|
|
1175
|
-
"fulfillments",
|
|
1176
|
-
"fulfillment-items",
|
|
1177
|
-
"transactions",
|
|
1178
|
-
"customers",
|
|
1179
|
-
"customer-profiles",
|
|
1180
|
-
"customer-profile-lists",
|
|
1181
|
-
"customer-addresses",
|
|
1182
|
-
"carts",
|
|
1183
|
-
"cart-items",
|
|
1184
|
-
"discounts",
|
|
1185
|
-
"shipping-policies",
|
|
1186
|
-
"shipping-zones",
|
|
1187
|
-
"documents",
|
|
1188
|
-
"document-categories",
|
|
1189
|
-
"document-types",
|
|
1190
|
-
"articles",
|
|
1191
|
-
"article-authors",
|
|
1192
|
-
"article-categories",
|
|
1193
|
-
"article-tags",
|
|
1194
|
-
"playlists",
|
|
1195
|
-
"playlist-categories",
|
|
1196
|
-
"playlist-tags",
|
|
1197
|
-
"tracks",
|
|
1198
|
-
"track-categories",
|
|
1199
|
-
"track-tags",
|
|
1200
|
-
"galleries",
|
|
1201
|
-
"gallery-categories",
|
|
1202
|
-
"gallery-tags",
|
|
1203
|
-
"gallery-items",
|
|
1204
|
-
"links",
|
|
1205
|
-
"link-categories",
|
|
1206
|
-
"link-tags",
|
|
1207
|
-
"canvases",
|
|
1208
|
-
"canvas-node-types",
|
|
1209
|
-
"canvas-edge-types",
|
|
1210
|
-
"canvas-categories",
|
|
1211
|
-
"canvas-tags",
|
|
1212
|
-
"canvas-nodes",
|
|
1213
|
-
"canvas-edges",
|
|
1214
|
-
"videos",
|
|
1215
|
-
"video-categories",
|
|
1216
|
-
"video-tags",
|
|
1217
|
-
"live-streams",
|
|
1218
|
-
"images",
|
|
1219
|
-
"forms",
|
|
1220
|
-
"form-submissions",
|
|
1221
|
-
// Community
|
|
1222
|
-
"posts",
|
|
1223
|
-
"comments",
|
|
1224
|
-
"reactions",
|
|
1225
|
-
"reaction-types",
|
|
1226
|
-
"bookmarks",
|
|
1227
|
-
"post-categories",
|
|
1228
|
-
// Events
|
|
1229
|
-
"event-calendars",
|
|
1230
|
-
"events",
|
|
1231
|
-
"event-categories",
|
|
1232
|
-
"event-occurrences",
|
|
1233
|
-
"event-tags"
|
|
1234
|
-
];
|
|
1235
|
-
var SERVER_ONLY_COLLECTIONS = [
|
|
1236
|
-
"customer-groups",
|
|
1237
|
-
"reports",
|
|
1238
|
-
"community-bans"
|
|
1239
|
-
];
|
|
1240
|
-
var SERVER_COLLECTIONS = [
|
|
1241
|
-
...COLLECTIONS,
|
|
1242
|
-
...SERVER_ONLY_COLLECTIONS
|
|
1243
|
-
];
|
|
1244
|
-
|
|
1245
846
|
// src/core/api/parse-response.ts
|
|
1246
847
|
async function parseApiResponse(response, endpoint) {
|
|
1247
848
|
let data;
|
|
@@ -1297,6 +898,7 @@ var CommunityClient = class {
|
|
|
1297
898
|
options.secretKey
|
|
1298
899
|
);
|
|
1299
900
|
this.secretKey = options.secretKey;
|
|
901
|
+
this.apiUrl = options.apiUrl;
|
|
1300
902
|
this.customerToken = options.customerToken;
|
|
1301
903
|
this.onUnauthorized = options.onUnauthorized;
|
|
1302
904
|
this.onRequestId = options.onRequestId;
|
|
@@ -1311,6 +913,7 @@ var CommunityClient = class {
|
|
|
1311
913
|
try {
|
|
1312
914
|
const response = await httpFetch(endpoint, {
|
|
1313
915
|
method,
|
|
916
|
+
apiUrl: this.apiUrl,
|
|
1314
917
|
publishableKey: this.publishableKey,
|
|
1315
918
|
secretKey: this.secretKey,
|
|
1316
919
|
customerToken: token ?? void 0,
|
|
@@ -1459,67 +1062,20 @@ var CommunityClient = class {
|
|
|
1459
1062
|
}
|
|
1460
1063
|
};
|
|
1461
1064
|
|
|
1462
|
-
// src/core/
|
|
1463
|
-
var
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
apiName,
|
|
1470
|
-
options.publishableKey,
|
|
1471
|
-
options.secretKey
|
|
1472
|
-
);
|
|
1473
|
-
this.secretKey = options.secretKey;
|
|
1474
|
-
this.onRequestId = options.onRequestId;
|
|
1475
|
-
}
|
|
1476
|
-
async request(endpoint, body, options) {
|
|
1477
|
-
const method = options?.method ?? "POST";
|
|
1478
|
-
try {
|
|
1479
|
-
const response = await httpFetch(endpoint, {
|
|
1480
|
-
method,
|
|
1481
|
-
publishableKey: this.publishableKey,
|
|
1482
|
-
secretKey: this.secretKey,
|
|
1483
|
-
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1484
|
-
...options?.headers && { headers: options.headers }
|
|
1485
|
-
});
|
|
1486
|
-
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1487
|
-
return parseApiResponse(response, endpoint);
|
|
1488
|
-
} catch (err) {
|
|
1489
|
-
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1490
|
-
this.onRequestId?.(id);
|
|
1491
|
-
throw err;
|
|
1492
|
-
}
|
|
1493
|
-
}
|
|
1494
|
-
};
|
|
1495
|
-
|
|
1496
|
-
// src/core/community/moderation-api.ts
|
|
1497
|
-
var ModerationApi = class extends BaseApi {
|
|
1498
|
-
constructor(options) {
|
|
1499
|
-
super("ModerationApi", options);
|
|
1500
|
-
}
|
|
1501
|
-
banCustomer(params) {
|
|
1502
|
-
return this.request("/api/community-bans/ban", params);
|
|
1503
|
-
}
|
|
1504
|
-
unbanCustomer(params) {
|
|
1505
|
-
return this.request("/api/community-bans/unban", params);
|
|
1506
|
-
}
|
|
1507
|
-
};
|
|
1508
|
-
|
|
1509
|
-
// src/core/customer/customer-auth.ts
|
|
1510
|
-
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1511
|
-
function safeGetItem(key) {
|
|
1512
|
-
try {
|
|
1513
|
-
return localStorage.getItem(key);
|
|
1514
|
-
} catch {
|
|
1515
|
-
return null;
|
|
1065
|
+
// src/core/customer/customer-auth.ts
|
|
1066
|
+
var DEFAULT_TIMEOUT2 = 15e3;
|
|
1067
|
+
function safeGetItem(key) {
|
|
1068
|
+
try {
|
|
1069
|
+
return localStorage.getItem(key);
|
|
1070
|
+
} catch {
|
|
1071
|
+
return null;
|
|
1516
1072
|
}
|
|
1517
1073
|
}
|
|
1518
1074
|
var CustomerAuth = class {
|
|
1519
|
-
constructor(publishableKey, options) {
|
|
1075
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1520
1076
|
this.refreshPromise = null;
|
|
1521
1077
|
this.publishableKey = publishableKey;
|
|
1522
|
-
this.baseUrl = resolveApiUrl();
|
|
1078
|
+
this.baseUrl = resolveApiUrl(apiUrl);
|
|
1523
1079
|
const persist = options?.persist ?? true;
|
|
1524
1080
|
if (persist) {
|
|
1525
1081
|
const key = typeof persist === "string" ? persist : "customer-token";
|
|
@@ -1750,8 +1306,8 @@ var CustomerAuth = class {
|
|
|
1750
1306
|
|
|
1751
1307
|
// src/core/customer/customer-namespace.ts
|
|
1752
1308
|
var CustomerNamespace = class {
|
|
1753
|
-
constructor(publishableKey, options) {
|
|
1754
|
-
this.auth = new CustomerAuth(publishableKey, options);
|
|
1309
|
+
constructor(publishableKey, options, apiUrl) {
|
|
1310
|
+
this.auth = new CustomerAuth(publishableKey, options, apiUrl);
|
|
1755
1311
|
}
|
|
1756
1312
|
};
|
|
1757
1313
|
|
|
@@ -1769,6 +1325,7 @@ var CartApi = class {
|
|
|
1769
1325
|
options.secretKey
|
|
1770
1326
|
);
|
|
1771
1327
|
this.secretKey = options.secretKey;
|
|
1328
|
+
this.apiUrl = options.apiUrl;
|
|
1772
1329
|
this.customerToken = options.customerToken;
|
|
1773
1330
|
this.onUnauthorized = options.onUnauthorized;
|
|
1774
1331
|
this.onRequestId = options.onRequestId;
|
|
@@ -1778,6 +1335,7 @@ var CartApi = class {
|
|
|
1778
1335
|
try {
|
|
1779
1336
|
const response = await httpFetch(endpoint, {
|
|
1780
1337
|
method,
|
|
1338
|
+
apiUrl: this.apiUrl,
|
|
1781
1339
|
publishableKey: this.publishableKey,
|
|
1782
1340
|
secretKey: this.secretKey,
|
|
1783
1341
|
customerToken: token ?? void 0,
|
|
@@ -1828,6 +1386,7 @@ var CommerceClient = class {
|
|
|
1828
1386
|
constructor(options) {
|
|
1829
1387
|
const cartApi = new CartApi({
|
|
1830
1388
|
publishableKey: options.publishableKey,
|
|
1389
|
+
apiUrl: options.apiUrl,
|
|
1831
1390
|
customerToken: options.customerToken,
|
|
1832
1391
|
onUnauthorized: options.onUnauthorized,
|
|
1833
1392
|
onRequestId: options.onRequestId
|
|
@@ -1837,6 +1396,7 @@ var CommerceClient = class {
|
|
|
1837
1396
|
try {
|
|
1838
1397
|
const response = await httpFetch(endpoint, {
|
|
1839
1398
|
method: "POST",
|
|
1399
|
+
apiUrl: options.apiUrl,
|
|
1840
1400
|
publishableKey: options.publishableKey,
|
|
1841
1401
|
customerToken: token ?? void 0,
|
|
1842
1402
|
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
|
|
@@ -1884,70 +1444,104 @@ var CommerceClient = class {
|
|
|
1884
1444
|
}
|
|
1885
1445
|
};
|
|
1886
1446
|
|
|
1887
|
-
// src/core/
|
|
1888
|
-
var
|
|
1447
|
+
// src/core/client/client.ts
|
|
1448
|
+
var Client = class {
|
|
1889
1449
|
constructor(options) {
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
1895
|
-
* to remain available by the time an order is placed.
|
|
1896
|
-
*/
|
|
1897
|
-
stockCheck(params) {
|
|
1898
|
-
return this.request("/api/products/stock-check", params);
|
|
1899
|
-
}
|
|
1900
|
-
listingGroups(params) {
|
|
1901
|
-
return this.request(
|
|
1902
|
-
"/api/products/listing-groups",
|
|
1903
|
-
params
|
|
1904
|
-
);
|
|
1905
|
-
}
|
|
1906
|
-
/**
|
|
1907
|
-
* Fetch full product detail by slug or id.
|
|
1908
|
-
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
1909
|
-
* `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
|
|
1910
|
-
* inspect `client.lastRequestId` against backend logs.
|
|
1911
|
-
*/
|
|
1912
|
-
async detail(params) {
|
|
1913
|
-
try {
|
|
1914
|
-
return await this.request("/api/products/detail", params);
|
|
1915
|
-
} catch (err) {
|
|
1916
|
-
if (err instanceof NotFoundError) return null;
|
|
1917
|
-
throw err;
|
|
1450
|
+
this.lastRequestId = null;
|
|
1451
|
+
const publishableKey = options.publishableKey;
|
|
1452
|
+
if (!publishableKey) {
|
|
1453
|
+
throw createConfigError("publishableKey is required.");
|
|
1918
1454
|
}
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1455
|
+
this.config = { ...options, publishableKey };
|
|
1456
|
+
const metadata = {
|
|
1457
|
+
timestamp: Date.now(),
|
|
1458
|
+
userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
|
|
1459
|
+
};
|
|
1460
|
+
this.state = { metadata };
|
|
1461
|
+
this.customer = new CustomerNamespace(
|
|
1462
|
+
this.config.publishableKey,
|
|
1463
|
+
options.customer,
|
|
1464
|
+
this.config.apiUrl
|
|
1465
|
+
);
|
|
1466
|
+
const onUnauthorized = async () => {
|
|
1467
|
+
try {
|
|
1468
|
+
const result = await this.customer.auth.refreshToken();
|
|
1469
|
+
return result.token ?? null;
|
|
1470
|
+
} catch {
|
|
1471
|
+
return null;
|
|
1472
|
+
}
|
|
1473
|
+
};
|
|
1474
|
+
const onRequestId = (id) => {
|
|
1475
|
+
this.lastRequestId = id;
|
|
1476
|
+
};
|
|
1477
|
+
this.commerce = new CommerceClient({
|
|
1478
|
+
publishableKey: this.config.publishableKey,
|
|
1479
|
+
apiUrl: this.config.apiUrl,
|
|
1480
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1481
|
+
onUnauthorized,
|
|
1482
|
+
onRequestId,
|
|
1483
|
+
customerAuth: this.customer.auth
|
|
1484
|
+
});
|
|
1485
|
+
this.community = new CommunityClient({
|
|
1486
|
+
publishableKey: this.config.publishableKey,
|
|
1487
|
+
apiUrl: this.config.apiUrl,
|
|
1488
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
1489
|
+
onUnauthorized,
|
|
1490
|
+
onRequestId
|
|
1491
|
+
});
|
|
1492
|
+
this.collections = new ReadOnlyCollectionClient(
|
|
1493
|
+
this.config.publishableKey,
|
|
1494
|
+
void 0,
|
|
1495
|
+
() => this.customer.auth.getToken(),
|
|
1496
|
+
onUnauthorized,
|
|
1497
|
+
onRequestId,
|
|
1498
|
+
this.config.apiUrl
|
|
1930
1499
|
);
|
|
1931
1500
|
}
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
// src/core/api/discount-api.ts
|
|
1935
|
-
var DiscountApi = class extends BaseApi {
|
|
1936
|
-
constructor(options) {
|
|
1937
|
-
super("DiscountApi", options);
|
|
1501
|
+
getState() {
|
|
1502
|
+
return { ...this.state };
|
|
1938
1503
|
}
|
|
1939
|
-
|
|
1940
|
-
return this.
|
|
1504
|
+
getConfig() {
|
|
1505
|
+
return { ...this.config };
|
|
1941
1506
|
}
|
|
1942
1507
|
};
|
|
1508
|
+
function createClient(options) {
|
|
1509
|
+
return new Client(options);
|
|
1510
|
+
}
|
|
1943
1511
|
|
|
1944
|
-
// src/core/api/
|
|
1945
|
-
var
|
|
1946
|
-
constructor(options) {
|
|
1947
|
-
|
|
1512
|
+
// src/core/api/base-api.ts
|
|
1513
|
+
var BaseApi = class {
|
|
1514
|
+
constructor(apiName, options) {
|
|
1515
|
+
if (!options.secretKey) {
|
|
1516
|
+
throw createConfigError(`secretKey is required for ${apiName}.`);
|
|
1517
|
+
}
|
|
1518
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
1519
|
+
apiName,
|
|
1520
|
+
options.publishableKey,
|
|
1521
|
+
options.secretKey
|
|
1522
|
+
);
|
|
1523
|
+
this.secretKey = options.secretKey;
|
|
1524
|
+
this.apiUrl = options.apiUrl;
|
|
1525
|
+
this.onRequestId = options.onRequestId;
|
|
1948
1526
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1527
|
+
async request(endpoint, body, options) {
|
|
1528
|
+
const method = options?.method ?? "POST";
|
|
1529
|
+
try {
|
|
1530
|
+
const response = await httpFetch(endpoint, {
|
|
1531
|
+
method,
|
|
1532
|
+
apiUrl: this.apiUrl,
|
|
1533
|
+
publishableKey: this.publishableKey,
|
|
1534
|
+
secretKey: this.secretKey,
|
|
1535
|
+
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1536
|
+
...options?.headers && { headers: options.headers }
|
|
1537
|
+
});
|
|
1538
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1539
|
+
return parseApiResponse(response, endpoint);
|
|
1540
|
+
} catch (err) {
|
|
1541
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1542
|
+
this.onRequestId?.(id);
|
|
1543
|
+
throw err;
|
|
1544
|
+
}
|
|
1951
1545
|
}
|
|
1952
1546
|
};
|
|
1953
1547
|
|
|
@@ -2001,769 +1595,306 @@ var OrderApi = class extends BaseApi {
|
|
|
2001
1595
|
}
|
|
2002
1596
|
};
|
|
2003
1597
|
|
|
2004
|
-
// src/core/
|
|
2005
|
-
var
|
|
1598
|
+
// src/core/api/discount-api.ts
|
|
1599
|
+
var DiscountApi = class extends BaseApi {
|
|
2006
1600
|
constructor(options) {
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
);
|
|
2012
|
-
const serverOptions = {
|
|
2013
|
-
publishableKey,
|
|
2014
|
-
secretKey: options.secretKey,
|
|
2015
|
-
onRequestId: options.onRequestId
|
|
2016
|
-
};
|
|
2017
|
-
const productApi = new ProductApi(serverOptions);
|
|
2018
|
-
const cartApi = new CartApi(serverOptions);
|
|
2019
|
-
const discountApi = new DiscountApi(serverOptions);
|
|
2020
|
-
const shippingApi = new ShippingApi(serverOptions);
|
|
2021
|
-
const orderApi = new OrderApi(serverOptions);
|
|
2022
|
-
this.product = {
|
|
2023
|
-
stockCheck: productApi.stockCheck.bind(productApi),
|
|
2024
|
-
listingGroups: productApi.listingGroups.bind(productApi),
|
|
2025
|
-
detail: productApi.detail.bind(productApi),
|
|
2026
|
-
upsert: productApi.upsert.bind(productApi)
|
|
2027
|
-
};
|
|
2028
|
-
this.cart = {
|
|
2029
|
-
get: cartApi.getCart.bind(cartApi),
|
|
2030
|
-
addItem: cartApi.addItem.bind(cartApi),
|
|
2031
|
-
updateItem: cartApi.updateItem.bind(cartApi),
|
|
2032
|
-
removeItem: cartApi.removeItem.bind(cartApi),
|
|
2033
|
-
applyDiscount: cartApi.applyDiscount.bind(cartApi),
|
|
2034
|
-
removeDiscount: cartApi.removeDiscount.bind(cartApi),
|
|
2035
|
-
clear: cartApi.clearCart.bind(cartApi)
|
|
2036
|
-
};
|
|
2037
|
-
this.orders = {
|
|
2038
|
-
checkout: orderApi.checkout.bind(orderApi),
|
|
2039
|
-
create: orderApi.createOrder.bind(orderApi),
|
|
2040
|
-
update: orderApi.updateOrder.bind(orderApi),
|
|
2041
|
-
updateTransaction: orderApi.updateTransaction.bind(orderApi),
|
|
2042
|
-
confirmPayment: orderApi.confirmPayment.bind(orderApi),
|
|
2043
|
-
createFulfillment: orderApi.createFulfillment.bind(orderApi),
|
|
2044
|
-
updateFulfillment: orderApi.updateFulfillment.bind(orderApi),
|
|
2045
|
-
bulkImportFulfillments: orderApi.bulkImportFulfillments.bind(orderApi),
|
|
2046
|
-
createReturn: orderApi.createReturn.bind(orderApi),
|
|
2047
|
-
updateReturn: orderApi.updateReturn.bind(orderApi),
|
|
2048
|
-
returnWithRefund: orderApi.returnWithRefund.bind(orderApi)
|
|
2049
|
-
};
|
|
2050
|
-
this.discounts = {
|
|
2051
|
-
validate: discountApi.validate.bind(discountApi)
|
|
2052
|
-
};
|
|
2053
|
-
this.shipping = {
|
|
2054
|
-
calculate: shippingApi.calculate.bind(shippingApi)
|
|
2055
|
-
};
|
|
1601
|
+
super("DiscountApi", options);
|
|
1602
|
+
}
|
|
1603
|
+
validate(params) {
|
|
1604
|
+
return this.request("/api/discounts/validate", params);
|
|
2056
1605
|
}
|
|
2057
1606
|
};
|
|
2058
1607
|
|
|
2059
|
-
// src/core/
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
defaultShouldDehydrateQuery
|
|
2064
|
-
} from "@tanstack/react-query";
|
|
2065
|
-
function makeQueryClient() {
|
|
2066
|
-
return new QueryClient({
|
|
2067
|
-
defaultOptions: {
|
|
2068
|
-
queries: {
|
|
2069
|
-
// Infinite staleTime: server-fetched data persists until explicitly invalidated.
|
|
2070
|
-
// For browser clients needing fresher data, override per-query:
|
|
2071
|
-
// useQuery({ ..., staleTime: 5 * 60 * 1000 })
|
|
2072
|
-
staleTime: Number.POSITIVE_INFINITY,
|
|
2073
|
-
refetchOnWindowFocus: false
|
|
2074
|
-
},
|
|
2075
|
-
dehydrate: {
|
|
2076
|
-
shouldDehydrateQuery: (query) => defaultShouldDehydrateQuery(query) || query.state.status === "pending",
|
|
2077
|
-
shouldRedactErrors: () => false
|
|
2078
|
-
}
|
|
2079
|
-
}
|
|
2080
|
-
});
|
|
2081
|
-
}
|
|
2082
|
-
var browserQueryClient;
|
|
2083
|
-
function getQueryClient() {
|
|
2084
|
-
if (isServer) {
|
|
2085
|
-
return makeQueryClient();
|
|
1608
|
+
// src/core/api/shipping-api.ts
|
|
1609
|
+
var ShippingApi = class extends BaseApi {
|
|
1610
|
+
constructor(options) {
|
|
1611
|
+
super("ShippingApi", options);
|
|
2086
1612
|
}
|
|
2087
|
-
|
|
2088
|
-
|
|
1613
|
+
calculate(params) {
|
|
1614
|
+
return this.request("/api/shipping-policies/calculate", params);
|
|
2089
1615
|
}
|
|
2090
|
-
return browserQueryClient;
|
|
2091
|
-
}
|
|
2092
|
-
|
|
2093
|
-
// src/core/query/query-hooks.ts
|
|
2094
|
-
import {
|
|
2095
|
-
useInfiniteQuery as useInfiniteQueryOriginal2,
|
|
2096
|
-
useQuery as useQueryOriginal3,
|
|
2097
|
-
useSuspenseInfiniteQuery as useSuspenseInfiniteQueryOriginal2,
|
|
2098
|
-
useSuspenseQuery as useSuspenseQueryOriginal2
|
|
2099
|
-
} from "@tanstack/react-query";
|
|
2100
|
-
|
|
2101
|
-
// src/core/query/collection-hooks.ts
|
|
2102
|
-
import {
|
|
2103
|
-
useQuery as useQueryOriginal,
|
|
2104
|
-
useSuspenseQuery as useSuspenseQueryOriginal,
|
|
2105
|
-
useInfiniteQuery as useInfiniteQueryOriginal,
|
|
2106
|
-
useSuspenseInfiniteQuery as useSuspenseInfiniteQueryOriginal,
|
|
2107
|
-
useMutation as useMutationOriginal
|
|
2108
|
-
} from "@tanstack/react-query";
|
|
2109
|
-
|
|
2110
|
-
// src/core/query/query-keys.ts
|
|
2111
|
-
function collectionKeys(collection) {
|
|
2112
|
-
return {
|
|
2113
|
-
all: [collection],
|
|
2114
|
-
lists: () => [collection, "list"],
|
|
2115
|
-
list: (options) => [collection, "list", options],
|
|
2116
|
-
details: () => [collection, "detail"],
|
|
2117
|
-
detail: (id, options) => [collection, "detail", id, options],
|
|
2118
|
-
infinites: () => [collection, "infinite"],
|
|
2119
|
-
infinite: (options) => [collection, "infinite", options]
|
|
2120
|
-
};
|
|
2121
|
-
}
|
|
2122
|
-
var customerKeys = {
|
|
2123
|
-
all: ["customer"],
|
|
2124
|
-
me: () => ["customer", "me"]
|
|
2125
|
-
};
|
|
2126
|
-
var productKeys = {
|
|
2127
|
-
listingGroups: (options) => ["products", "listing-groups", "list", options],
|
|
2128
|
-
listingGroupsInfinite: (options) => ["products", "listing-groups", "infinite", options],
|
|
2129
|
-
detail: (params) => ["products", "detail", params],
|
|
2130
|
-
detailAll: () => ["products", "detail"]
|
|
2131
1616
|
};
|
|
2132
1617
|
|
|
2133
|
-
// src/core/
|
|
2134
|
-
var
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
"product-options",
|
|
2138
|
-
"product-option-values",
|
|
2139
|
-
"product-categories",
|
|
2140
|
-
"product-tags",
|
|
2141
|
-
"product-collections",
|
|
2142
|
-
"brands",
|
|
2143
|
-
"brand-logos",
|
|
2144
|
-
"images"
|
|
2145
|
-
]);
|
|
2146
|
-
var DEFAULT_PAGE_SIZE = 20;
|
|
2147
|
-
var CollectionHooks = class {
|
|
2148
|
-
constructor(queryClient, collectionClient) {
|
|
2149
|
-
this.queryClient = queryClient;
|
|
2150
|
-
this.collectionClient = collectionClient;
|
|
2151
|
-
}
|
|
2152
|
-
// ===== useQuery =====
|
|
2153
|
-
useQuery(params, options) {
|
|
2154
|
-
const { collection, options: queryOptions } = params;
|
|
2155
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2156
|
-
return useQueryOriginal({
|
|
2157
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2158
|
-
queryFn: async () => {
|
|
2159
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2160
|
-
},
|
|
2161
|
-
...restOptions,
|
|
2162
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2163
|
-
...placeholderData !== void 0 && {
|
|
2164
|
-
placeholderData
|
|
2165
|
-
}
|
|
2166
|
-
});
|
|
1618
|
+
// src/core/api/product-api.ts
|
|
1619
|
+
var ProductApi = class extends BaseApi {
|
|
1620
|
+
constructor(options) {
|
|
1621
|
+
super("ProductApi", options);
|
|
2167
1622
|
}
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
},
|
|
2176
|
-
...options
|
|
2177
|
-
});
|
|
1623
|
+
/**
|
|
1624
|
+
* Check point-in-time stock availability for one or more product variants.
|
|
1625
|
+
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
1626
|
+
* to remain available by the time an order is placed.
|
|
1627
|
+
*/
|
|
1628
|
+
stockCheck(params) {
|
|
1629
|
+
return this.request("/api/products/stock-check", params);
|
|
2178
1630
|
}
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
queryKey: collectionKeys(collection).detail(id, queryOptions),
|
|
2185
|
-
queryFn: async () => {
|
|
2186
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2187
|
-
},
|
|
2188
|
-
...restOptions,
|
|
2189
|
-
// NonFunctionGuard<T> incompatible with generic union types — safe cast
|
|
2190
|
-
...placeholderData !== void 0 && {
|
|
2191
|
-
placeholderData
|
|
2192
|
-
}
|
|
2193
|
-
});
|
|
1631
|
+
listingGroups(params) {
|
|
1632
|
+
return this.request(
|
|
1633
|
+
"/api/products/listing-groups",
|
|
1634
|
+
params
|
|
1635
|
+
);
|
|
2194
1636
|
}
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
})
|
|
1637
|
+
/**
|
|
1638
|
+
* Fetch full product detail by slug or id.
|
|
1639
|
+
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
1640
|
+
* `tenant_mismatch` / `feature_disabled`). For the reason behind a null,
|
|
1641
|
+
* inspect `client.lastRequestId` against backend logs.
|
|
1642
|
+
*/
|
|
1643
|
+
async detail(params) {
|
|
1644
|
+
try {
|
|
1645
|
+
return await this.request("/api/products/detail", params);
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
if (err instanceof NotFoundError) return null;
|
|
1648
|
+
throw err;
|
|
1649
|
+
}
|
|
2205
1650
|
}
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
return
|
|
2214
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2215
|
-
queryFn: async ({ pageParam }) => {
|
|
2216
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2217
|
-
return response;
|
|
2218
|
-
},
|
|
2219
|
-
initialPageParam: 1,
|
|
2220
|
-
getNextPageParam: (lastPage) => {
|
|
2221
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2222
|
-
},
|
|
2223
|
-
...options
|
|
2224
|
-
});
|
|
2225
|
-
}
|
|
2226
|
-
// ===== useSuspenseInfiniteQuery =====
|
|
2227
|
-
useSuspenseInfiniteQuery(params, options) {
|
|
2228
|
-
const {
|
|
2229
|
-
collection,
|
|
2230
|
-
options: queryOptions,
|
|
2231
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
2232
|
-
} = params;
|
|
2233
|
-
return useSuspenseInfiniteQueryOriginal({
|
|
2234
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2235
|
-
queryFn: async ({ pageParam }) => {
|
|
2236
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2237
|
-
return response;
|
|
2238
|
-
},
|
|
2239
|
-
initialPageParam: 1,
|
|
2240
|
-
getNextPageParam: (lastPage) => {
|
|
2241
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2242
|
-
},
|
|
2243
|
-
...options
|
|
2244
|
-
});
|
|
2245
|
-
}
|
|
2246
|
-
// ===== prefetchQuery =====
|
|
2247
|
-
async prefetchQuery(params, options) {
|
|
2248
|
-
const { collection, options: queryOptions } = params;
|
|
2249
|
-
return this.queryClient.prefetchQuery({
|
|
2250
|
-
queryKey: collectionKeys(collection).list(queryOptions),
|
|
2251
|
-
queryFn: async () => {
|
|
2252
|
-
return await this.collectionClient.from(collection).find(queryOptions);
|
|
2253
|
-
},
|
|
2254
|
-
...options
|
|
2255
|
-
});
|
|
2256
|
-
}
|
|
2257
|
-
// ===== prefetchQueryById =====
|
|
2258
|
-
async prefetchQueryById(params, options) {
|
|
2259
|
-
const { collection, id, options: queryOptions } = params;
|
|
2260
|
-
return this.queryClient.prefetchQuery({
|
|
2261
|
-
queryKey: collectionKeys(collection).detail(id, queryOptions),
|
|
2262
|
-
queryFn: async () => {
|
|
2263
|
-
return await this.collectionClient.from(collection).findById(id, queryOptions);
|
|
2264
|
-
},
|
|
2265
|
-
...options
|
|
2266
|
-
});
|
|
2267
|
-
}
|
|
2268
|
-
// ===== prefetchInfiniteQuery =====
|
|
2269
|
-
async prefetchInfiniteQuery(params, options) {
|
|
2270
|
-
const {
|
|
2271
|
-
collection,
|
|
2272
|
-
options: queryOptions,
|
|
2273
|
-
pageSize = DEFAULT_PAGE_SIZE
|
|
2274
|
-
} = params;
|
|
2275
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2276
|
-
queryKey: collectionKeys(collection).infinite(queryOptions),
|
|
2277
|
-
queryFn: async ({ pageParam }) => {
|
|
2278
|
-
const response = await this.collectionClient.from(collection).find({ ...queryOptions, page: pageParam, limit: pageSize });
|
|
2279
|
-
return response;
|
|
2280
|
-
},
|
|
2281
|
-
initialPageParam: 1,
|
|
2282
|
-
getNextPageParam: (lastPage) => {
|
|
2283
|
-
return lastPage.hasNextPage ? lastPage.nextPage : void 0;
|
|
2284
|
-
},
|
|
2285
|
-
pages: options?.pages ?? 1,
|
|
2286
|
-
staleTime: options?.staleTime
|
|
2287
|
-
});
|
|
2288
|
-
}
|
|
2289
|
-
// ===== Mutation Hooks =====
|
|
2290
|
-
useCreate(params, options) {
|
|
2291
|
-
const { collection } = params;
|
|
2292
|
-
return useMutationOriginal({
|
|
2293
|
-
mutationFn: async (variables) => {
|
|
2294
|
-
return await this.collectionClient.from(collection).create(
|
|
2295
|
-
variables.data,
|
|
2296
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2297
|
-
);
|
|
2298
|
-
},
|
|
2299
|
-
onSuccess: (data) => {
|
|
2300
|
-
this.queryClient.invalidateQueries({
|
|
2301
|
-
queryKey: collectionKeys(collection).all
|
|
2302
|
-
});
|
|
2303
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2304
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2305
|
-
}
|
|
2306
|
-
options?.onSuccess?.(data);
|
|
2307
|
-
},
|
|
2308
|
-
onError: options?.onError,
|
|
2309
|
-
onSettled: options?.onSettled
|
|
2310
|
-
});
|
|
2311
|
-
}
|
|
2312
|
-
useUpdate(params, options) {
|
|
2313
|
-
const { collection } = params;
|
|
2314
|
-
return useMutationOriginal({
|
|
2315
|
-
mutationFn: async (variables) => {
|
|
2316
|
-
return await this.collectionClient.from(collection).update(
|
|
2317
|
-
variables.id,
|
|
2318
|
-
variables.data,
|
|
2319
|
-
variables.file ? { file: variables.file, filename: variables.filename } : void 0
|
|
2320
|
-
);
|
|
2321
|
-
},
|
|
2322
|
-
onSuccess: (data) => {
|
|
2323
|
-
this.queryClient.invalidateQueries({
|
|
2324
|
-
queryKey: collectionKeys(collection).all
|
|
2325
|
-
});
|
|
2326
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2327
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2328
|
-
}
|
|
2329
|
-
options?.onSuccess?.(data);
|
|
2330
|
-
},
|
|
2331
|
-
onError: options?.onError,
|
|
2332
|
-
onSettled: options?.onSettled
|
|
2333
|
-
});
|
|
2334
|
-
}
|
|
2335
|
-
useRemove(params, options) {
|
|
2336
|
-
const { collection } = params;
|
|
2337
|
-
return useMutationOriginal({
|
|
2338
|
-
mutationFn: async (id) => {
|
|
2339
|
-
return await this.collectionClient.from(collection).remove(id);
|
|
2340
|
-
},
|
|
2341
|
-
onSuccess: (data) => {
|
|
2342
|
-
this.queryClient.invalidateQueries({
|
|
2343
|
-
queryKey: collectionKeys(collection).all
|
|
2344
|
-
});
|
|
2345
|
-
if (PRODUCT_DETAIL_INVALIDATING_COLLECTIONS.has(collection)) {
|
|
2346
|
-
this.queryClient.invalidateQueries({ queryKey: ["products", "detail"] });
|
|
2347
|
-
}
|
|
2348
|
-
options?.onSuccess?.(data);
|
|
2349
|
-
},
|
|
2350
|
-
onError: options?.onError,
|
|
2351
|
-
onSettled: options?.onSettled
|
|
2352
|
-
});
|
|
2353
|
-
}
|
|
2354
|
-
// ===== Cache Utilities =====
|
|
2355
|
-
invalidateQueries(collection, type) {
|
|
2356
|
-
const queryKey = type ? [collection, type] : [collection];
|
|
2357
|
-
return this.queryClient.invalidateQueries({ queryKey });
|
|
2358
|
-
}
|
|
2359
|
-
getQueryData(collection, type, idOrOptions, options) {
|
|
2360
|
-
if (type === "list") {
|
|
2361
|
-
return this.queryClient.getQueryData(
|
|
2362
|
-
collectionKeys(collection).list(idOrOptions)
|
|
2363
|
-
);
|
|
2364
|
-
}
|
|
2365
|
-
return this.queryClient.getQueryData(
|
|
2366
|
-
collectionKeys(collection).detail(idOrOptions, options)
|
|
2367
|
-
);
|
|
2368
|
-
}
|
|
2369
|
-
setQueryData(collection, type, dataOrId, dataOrOptions, options) {
|
|
2370
|
-
if (type === "list") {
|
|
2371
|
-
this.queryClient.setQueryData(
|
|
2372
|
-
collectionKeys(collection).list(dataOrOptions),
|
|
2373
|
-
dataOrId
|
|
2374
|
-
);
|
|
2375
|
-
} else {
|
|
2376
|
-
this.queryClient.setQueryData(
|
|
2377
|
-
collectionKeys(collection).detail(dataOrId, options),
|
|
2378
|
-
dataOrOptions
|
|
2379
|
-
);
|
|
2380
|
-
}
|
|
1651
|
+
/**
|
|
1652
|
+
* Atomically create or update a product together with its options,
|
|
1653
|
+
* option-values, and variants in a single transaction. Mirrors Shopify's
|
|
1654
|
+
* `productSet` shape and is the canonical write path for the MCP
|
|
1655
|
+
* `product-upsert` tool.
|
|
1656
|
+
*/
|
|
1657
|
+
upsert(params) {
|
|
1658
|
+
return this.request("/api/products/upsert", params);
|
|
2381
1659
|
}
|
|
2382
1660
|
};
|
|
2383
1661
|
|
|
2384
|
-
// src/core/
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
function createMutation(mutationFn, callbacks, onSuccessExtra) {
|
|
2390
|
-
return useMutationOriginal2({
|
|
2391
|
-
mutationFn,
|
|
2392
|
-
onSuccess: (data) => {
|
|
2393
|
-
onSuccessExtra?.(data);
|
|
2394
|
-
callbacks?.onSuccess?.(data);
|
|
2395
|
-
},
|
|
2396
|
-
onError: callbacks?.onError,
|
|
2397
|
-
onSettled: callbacks?.onSettled
|
|
2398
|
-
});
|
|
1662
|
+
// src/core/webhook/index.ts
|
|
1663
|
+
function isValidWebhookEvent(data) {
|
|
1664
|
+
if (typeof data !== "object" || data === null) return false;
|
|
1665
|
+
const obj = data;
|
|
1666
|
+
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
2399
1667
|
}
|
|
2400
|
-
var
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
}
|
|
2414
|
-
return this.customerAuth;
|
|
2415
|
-
}
|
|
2416
|
-
// ===== useCustomerMe =====
|
|
2417
|
-
useCustomerMe(options) {
|
|
2418
|
-
return useQueryOriginal2({
|
|
2419
|
-
queryKey: customerKeys.me(),
|
|
2420
|
-
queryFn: async () => {
|
|
2421
|
-
return await this.ensureCustomerAuth().me();
|
|
2422
|
-
},
|
|
2423
|
-
...options,
|
|
2424
|
-
enabled: (options?.enabled ?? true) && !!this.customerAuth?.isAuthenticated()
|
|
2425
|
-
});
|
|
2426
|
-
}
|
|
2427
|
-
// ===== Mutations =====
|
|
2428
|
-
useCustomerLogin(options) {
|
|
2429
|
-
return createMutation(
|
|
2430
|
-
(data) => this.ensureCustomerAuth().login(data),
|
|
2431
|
-
options,
|
|
2432
|
-
this.invalidateMe
|
|
2433
|
-
);
|
|
2434
|
-
}
|
|
2435
|
-
useCustomerRegister(options) {
|
|
2436
|
-
return createMutation(
|
|
2437
|
-
(data) => this.ensureCustomerAuth().register(data),
|
|
2438
|
-
options
|
|
2439
|
-
);
|
|
2440
|
-
}
|
|
2441
|
-
useCustomerLogout(options) {
|
|
2442
|
-
return useMutationOriginal2({
|
|
2443
|
-
mutationFn: async () => {
|
|
2444
|
-
this.ensureCustomerAuth().logout();
|
|
2445
|
-
},
|
|
2446
|
-
onSuccess: () => {
|
|
2447
|
-
this.queryClient.removeQueries({ queryKey: customerKeys.all });
|
|
2448
|
-
options?.onSuccess?.();
|
|
2449
|
-
},
|
|
2450
|
-
onError: options?.onError,
|
|
2451
|
-
onSettled: options?.onSettled
|
|
2452
|
-
});
|
|
2453
|
-
}
|
|
2454
|
-
useCustomerForgotPassword(options) {
|
|
2455
|
-
return createMutation(
|
|
2456
|
-
(email) => this.ensureCustomerAuth().forgotPassword(email).then(() => {
|
|
2457
|
-
}),
|
|
2458
|
-
options
|
|
2459
|
-
);
|
|
2460
|
-
}
|
|
2461
|
-
useCustomerResetPassword(options) {
|
|
2462
|
-
return createMutation(
|
|
2463
|
-
(data) => this.ensureCustomerAuth().resetPassword(data.token, data.password).then(() => {
|
|
2464
|
-
}),
|
|
2465
|
-
options
|
|
2466
|
-
);
|
|
2467
|
-
}
|
|
2468
|
-
useCustomerRefreshToken(options) {
|
|
2469
|
-
return createMutation(
|
|
2470
|
-
() => this.ensureCustomerAuth().refreshToken(),
|
|
2471
|
-
options,
|
|
2472
|
-
this.invalidateMe
|
|
2473
|
-
);
|
|
2474
|
-
}
|
|
2475
|
-
useCustomerUpdateProfile(options) {
|
|
2476
|
-
return createMutation(
|
|
2477
|
-
(data) => this.ensureCustomerAuth().updateProfile(data),
|
|
2478
|
-
options,
|
|
2479
|
-
this.invalidateMe
|
|
2480
|
-
);
|
|
2481
|
-
}
|
|
2482
|
-
useCustomerChangePassword(options) {
|
|
2483
|
-
return createMutation(
|
|
2484
|
-
(data) => this.ensureCustomerAuth().changePassword(data.currentPassword, data.newPassword).then(() => {
|
|
2485
|
-
}),
|
|
2486
|
-
options
|
|
2487
|
-
);
|
|
2488
|
-
}
|
|
2489
|
-
// ===== Customer Cache Utilities =====
|
|
2490
|
-
invalidateCustomerQueries() {
|
|
2491
|
-
return this.queryClient.invalidateQueries({ queryKey: customerKeys.all });
|
|
2492
|
-
}
|
|
2493
|
-
getCustomerData() {
|
|
2494
|
-
return this.queryClient.getQueryData(customerKeys.me());
|
|
2495
|
-
}
|
|
2496
|
-
setCustomerData(data) {
|
|
2497
|
-
this.queryClient.setQueryData(customerKeys.me(), data);
|
|
2498
|
-
}
|
|
2499
|
-
};
|
|
2500
|
-
|
|
2501
|
-
// src/core/query/query-hooks.ts
|
|
2502
|
-
var QueryHooks = class extends CollectionHooks {
|
|
2503
|
-
constructor(queryClient, collectionClient, customerAuth, commerceClient) {
|
|
2504
|
-
super(queryClient, collectionClient);
|
|
2505
|
-
// --- Customer hooks delegation ---
|
|
2506
|
-
this.useCustomerMe = (...args) => this._customer.useCustomerMe(...args);
|
|
2507
|
-
this.useCustomerLogin = (...args) => this._customer.useCustomerLogin(...args);
|
|
2508
|
-
this.useCustomerRegister = (...args) => this._customer.useCustomerRegister(...args);
|
|
2509
|
-
this.useCustomerLogout = (...args) => this._customer.useCustomerLogout(...args);
|
|
2510
|
-
this.useCustomerForgotPassword = (...args) => this._customer.useCustomerForgotPassword(...args);
|
|
2511
|
-
this.useCustomerResetPassword = (...args) => this._customer.useCustomerResetPassword(...args);
|
|
2512
|
-
this.useCustomerRefreshToken = (...args) => this._customer.useCustomerRefreshToken(...args);
|
|
2513
|
-
this.useCustomerUpdateProfile = (...args) => this._customer.useCustomerUpdateProfile(...args);
|
|
2514
|
-
this.useCustomerChangePassword = (...args) => this._customer.useCustomerChangePassword(...args);
|
|
2515
|
-
// --- Customer cache delegation ---
|
|
2516
|
-
this.invalidateCustomerQueries = () => this._customer.invalidateCustomerQueries();
|
|
2517
|
-
this.getCustomerData = () => this._customer.getCustomerData();
|
|
2518
|
-
this.setCustomerData = (data) => this._customer.setCustomerData(data);
|
|
2519
|
-
this._customer = new CustomerHooks(queryClient, customerAuth);
|
|
2520
|
-
this._commerce = commerceClient;
|
|
2521
|
-
}
|
|
2522
|
-
useProductListingGroupsQuery(params, options) {
|
|
2523
|
-
const queryOptions = params.options;
|
|
2524
|
-
const { placeholderData, ...restOptions } = options ?? {};
|
|
2525
|
-
return useQueryOriginal3({
|
|
2526
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2527
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2528
|
-
"/api/products/listing-groups/query",
|
|
2529
|
-
{ options: queryOptions }
|
|
2530
|
-
),
|
|
2531
|
-
...restOptions,
|
|
2532
|
-
...placeholderData !== void 0 && {
|
|
2533
|
-
placeholderData
|
|
2534
|
-
}
|
|
2535
|
-
});
|
|
2536
|
-
}
|
|
2537
|
-
useSuspenseProductListingGroupsQuery(params, options) {
|
|
2538
|
-
const queryOptions = params.options;
|
|
2539
|
-
return useSuspenseQueryOriginal2({
|
|
2540
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2541
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2542
|
-
"/api/products/listing-groups/query",
|
|
2543
|
-
{ options: queryOptions }
|
|
2544
|
-
),
|
|
2545
|
-
...options
|
|
2546
|
-
});
|
|
2547
|
-
}
|
|
2548
|
-
useInfiniteProductListingGroupsQuery(params, options) {
|
|
2549
|
-
const {
|
|
2550
|
-
options: queryOptions,
|
|
2551
|
-
pageSize = 20
|
|
2552
|
-
} = params;
|
|
2553
|
-
return useInfiniteQueryOriginal2({
|
|
2554
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2555
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2556
|
-
"/api/products/listing-groups/query",
|
|
2557
|
-
{
|
|
2558
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2559
|
-
}
|
|
2560
|
-
),
|
|
2561
|
-
initialPageParam: 1,
|
|
2562
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2563
|
-
...options
|
|
2564
|
-
});
|
|
2565
|
-
}
|
|
2566
|
-
useSuspenseInfiniteProductListingGroupsQuery(params, options) {
|
|
2567
|
-
const {
|
|
2568
|
-
options: queryOptions,
|
|
2569
|
-
pageSize = 20
|
|
2570
|
-
} = params;
|
|
2571
|
-
return useSuspenseInfiniteQueryOriginal2({
|
|
2572
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2573
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2574
|
-
"/api/products/listing-groups/query",
|
|
2575
|
-
{
|
|
2576
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2577
|
-
}
|
|
2578
|
-
),
|
|
2579
|
-
initialPageParam: 1,
|
|
2580
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2581
|
-
...options
|
|
2582
|
-
});
|
|
2583
|
-
}
|
|
2584
|
-
async prefetchProductListingGroupsQuery(params, options) {
|
|
2585
|
-
const queryOptions = params.options;
|
|
2586
|
-
return this.queryClient.prefetchQuery({
|
|
2587
|
-
queryKey: productKeys.listingGroups(queryOptions),
|
|
2588
|
-
queryFn: async () => this.collectionClient.requestFindEndpoint(
|
|
2589
|
-
"/api/products/listing-groups/query",
|
|
2590
|
-
{ options: queryOptions }
|
|
2591
|
-
),
|
|
2592
|
-
...options
|
|
2593
|
-
});
|
|
2594
|
-
}
|
|
2595
|
-
async prefetchInfiniteProductListingGroupsQuery(params, options) {
|
|
2596
|
-
const {
|
|
2597
|
-
options: queryOptions,
|
|
2598
|
-
pageSize = 20
|
|
2599
|
-
} = params;
|
|
2600
|
-
return this.queryClient.prefetchInfiniteQuery({
|
|
2601
|
-
queryKey: productKeys.listingGroupsInfinite(queryOptions),
|
|
2602
|
-
queryFn: async ({ pageParam }) => this.collectionClient.requestFindEndpoint(
|
|
2603
|
-
"/api/products/listing-groups/query",
|
|
2604
|
-
{
|
|
2605
|
-
options: { ...queryOptions, page: pageParam, limit: pageSize }
|
|
2606
|
-
}
|
|
2607
|
-
),
|
|
2608
|
-
initialPageParam: 1,
|
|
2609
|
-
getNextPageParam: (lastPage) => lastPage.hasNextPage ? lastPage.nextPage : void 0,
|
|
2610
|
-
pages: options?.pages ?? 1,
|
|
2611
|
-
staleTime: options?.staleTime
|
|
2612
|
-
});
|
|
2613
|
-
}
|
|
2614
|
-
useProductDetail(params, options) {
|
|
2615
|
-
const discriminator = "slug" in params ? params.slug : params.id;
|
|
2616
|
-
const enabled = options?.enabled !== false && Boolean(discriminator);
|
|
2617
|
-
return useQueryOriginal3({
|
|
2618
|
-
queryKey: productKeys.detail(params),
|
|
2619
|
-
queryFn: () => this._commerce.product.detail(params),
|
|
2620
|
-
enabled
|
|
2621
|
-
});
|
|
2622
|
-
}
|
|
2623
|
-
useProductDetailBySlug(slug, options) {
|
|
2624
|
-
return this.useProductDetail({ slug }, options);
|
|
2625
|
-
}
|
|
2626
|
-
useProductDetailById(id, options) {
|
|
2627
|
-
return this.useProductDetail({ id }, options);
|
|
1668
|
+
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
1669
|
+
function isRecord(value) {
|
|
1670
|
+
return typeof value === "object" && value !== null;
|
|
1671
|
+
}
|
|
1672
|
+
function hasString(value, key) {
|
|
1673
|
+
return typeof value[key] === "string";
|
|
1674
|
+
}
|
|
1675
|
+
function hasStringOrNumber(value, key) {
|
|
1676
|
+
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
1677
|
+
}
|
|
1678
|
+
function isCustomerPasswordResetWebhookEvent(event) {
|
|
1679
|
+
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
|
|
1680
|
+
return false;
|
|
2628
1681
|
}
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
if (!publishableKey) {
|
|
2637
|
-
throw createConfigError("publishableKey is required.");
|
|
1682
|
+
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
1683
|
+
}
|
|
1684
|
+
function createCustomerAuthWebhookHandler(handlers) {
|
|
1685
|
+
return async (event) => {
|
|
1686
|
+
if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
|
|
1687
|
+
await handlers.passwordReset(event.data, event);
|
|
1688
|
+
return;
|
|
2638
1689
|
}
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
return result.token ?? null;
|
|
2654
|
-
} catch {
|
|
2655
|
-
return null;
|
|
2656
|
-
}
|
|
2657
|
-
};
|
|
2658
|
-
const onRequestId = (id) => {
|
|
2659
|
-
this.lastRequestId = id;
|
|
2660
|
-
};
|
|
2661
|
-
this.commerce = new CommerceClient({
|
|
2662
|
-
publishableKey: this.config.publishableKey,
|
|
2663
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2664
|
-
onUnauthorized,
|
|
2665
|
-
onRequestId,
|
|
2666
|
-
customerAuth: this.customer.auth
|
|
2667
|
-
});
|
|
2668
|
-
this.community = new CommunityClient({
|
|
2669
|
-
publishableKey: this.config.publishableKey,
|
|
2670
|
-
customerToken: () => this.customer.auth.getToken(),
|
|
2671
|
-
onUnauthorized,
|
|
2672
|
-
onRequestId
|
|
2673
|
-
});
|
|
2674
|
-
const collectionClient = new CollectionClient(
|
|
2675
|
-
this.config.publishableKey,
|
|
2676
|
-
void 0,
|
|
2677
|
-
() => this.customer.auth.getToken(),
|
|
2678
|
-
onUnauthorized,
|
|
2679
|
-
onRequestId
|
|
2680
|
-
);
|
|
2681
|
-
this.collections = new ReadOnlyCollectionClient(
|
|
2682
|
-
this.config.publishableKey,
|
|
2683
|
-
void 0,
|
|
2684
|
-
() => this.customer.auth.getToken(),
|
|
2685
|
-
onUnauthorized,
|
|
2686
|
-
onRequestId
|
|
2687
|
-
);
|
|
2688
|
-
this.query = new QueryHooks(
|
|
2689
|
-
this.queryClient,
|
|
2690
|
-
collectionClient,
|
|
2691
|
-
this.customer.auth,
|
|
2692
|
-
this.commerce
|
|
2693
|
-
);
|
|
2694
|
-
}
|
|
2695
|
-
getState() {
|
|
2696
|
-
return { ...this.state };
|
|
2697
|
-
}
|
|
2698
|
-
getConfig() {
|
|
2699
|
-
return { ...this.config };
|
|
1690
|
+
await handlers.unhandled?.(event);
|
|
1691
|
+
};
|
|
1692
|
+
}
|
|
1693
|
+
async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
|
|
1694
|
+
const encoder = new TextEncoder();
|
|
1695
|
+
const key = await crypto.subtle.importKey(
|
|
1696
|
+
"raw",
|
|
1697
|
+
encoder.encode(secret),
|
|
1698
|
+
{ name: "HMAC", hash: "SHA-256" },
|
|
1699
|
+
false,
|
|
1700
|
+
["verify"]
|
|
1701
|
+
);
|
|
1702
|
+
if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
|
|
1703
|
+
return false;
|
|
2700
1704
|
}
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
1705
|
+
const sigBytes = new Uint8Array(
|
|
1706
|
+
(signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
|
|
1707
|
+
);
|
|
1708
|
+
return crypto.subtle.verify(
|
|
1709
|
+
"HMAC",
|
|
1710
|
+
key,
|
|
1711
|
+
sigBytes,
|
|
1712
|
+
encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
|
|
1713
|
+
);
|
|
2704
1714
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
1715
|
+
function timestampIsFresh(timestamp, toleranceSeconds) {
|
|
1716
|
+
if (!/^\d+$/.test(timestamp)) return false;
|
|
1717
|
+
const timestampMs = Number(timestamp);
|
|
1718
|
+
if (!Number.isFinite(timestampMs)) return false;
|
|
1719
|
+
const skewMs = Math.abs(Date.now() - timestampMs);
|
|
1720
|
+
return skewMs <= toleranceSeconds * 1e3;
|
|
1721
|
+
}
|
|
1722
|
+
async function handleWebhook(request, handler, options) {
|
|
1723
|
+
try {
|
|
1724
|
+
const rawBody = await request.text();
|
|
1725
|
+
if (options?.secret) {
|
|
1726
|
+
const signature = request.headers.get("x-webhook-signature") || "";
|
|
1727
|
+
const timestamp = request.headers.get("x-webhook-timestamp") || "";
|
|
1728
|
+
const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
|
|
1729
|
+
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
1730
|
+
const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
|
|
1731
|
+
rawBody,
|
|
1732
|
+
options.secret,
|
|
1733
|
+
signature,
|
|
1734
|
+
timestamp,
|
|
1735
|
+
deliveryId
|
|
2713
1736
|
);
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
1737
|
+
if (!valid) {
|
|
1738
|
+
return new Response(
|
|
1739
|
+
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
1740
|
+
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
} else {
|
|
1744
|
+
console.warn(
|
|
1745
|
+
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
2721
1746
|
);
|
|
2722
1747
|
}
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
secretKey: this.config.secretKey,
|
|
2735
|
-
onRequestId
|
|
2736
|
-
};
|
|
2737
|
-
this.commerce = new ServerCommerceClient(serverOptions);
|
|
2738
|
-
const communityClient = new CommunityClient(serverOptions);
|
|
2739
|
-
const moderationApi = new ModerationApi(serverOptions);
|
|
2740
|
-
this.community = Object.assign(communityClient, {
|
|
2741
|
-
moderation: {
|
|
2742
|
-
banCustomer: moderationApi.banCustomer.bind(moderationApi),
|
|
2743
|
-
unbanCustomer: moderationApi.unbanCustomer.bind(moderationApi)
|
|
2744
|
-
}
|
|
2745
|
-
});
|
|
2746
|
-
this.collections = new ServerCollectionClient(
|
|
2747
|
-
this.config.publishableKey,
|
|
2748
|
-
this.config.secretKey,
|
|
2749
|
-
void 0,
|
|
2750
|
-
void 0,
|
|
2751
|
-
onRequestId
|
|
1748
|
+
const body = JSON.parse(rawBody);
|
|
1749
|
+
if (!isValidWebhookEvent(body)) {
|
|
1750
|
+
return new Response(
|
|
1751
|
+
JSON.stringify({ error: "Invalid webhook event format" }),
|
|
1752
|
+
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
1753
|
+
);
|
|
1754
|
+
}
|
|
1755
|
+
await handler(body);
|
|
1756
|
+
return new Response(
|
|
1757
|
+
JSON.stringify({ success: true, message: "Webhook processed" }),
|
|
1758
|
+
{ status: 200, headers: { "Content-Type": "application/json" } }
|
|
2752
1759
|
);
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2757
|
-
|
|
2758
|
-
|
|
2759
|
-
getConfig() {
|
|
2760
|
-
const { secretKey: _, ...safeConfig } = this.config;
|
|
2761
|
-
return safeConfig;
|
|
1760
|
+
} catch (error) {
|
|
1761
|
+
console.error("Webhook processing error:", error);
|
|
1762
|
+
return new Response(JSON.stringify({ error: "Internal server error" }), {
|
|
1763
|
+
status: 500,
|
|
1764
|
+
headers: { "Content-Type": "application/json" }
|
|
1765
|
+
});
|
|
2762
1766
|
}
|
|
2763
|
-
};
|
|
2764
|
-
function createServerClient(options) {
|
|
2765
|
-
return new ServerClient(options);
|
|
2766
1767
|
}
|
|
1768
|
+
function createTypedWebhookHandler(collection, handler) {
|
|
1769
|
+
return async (event) => {
|
|
1770
|
+
if (event.collection !== collection) {
|
|
1771
|
+
throw new Error(
|
|
1772
|
+
`Expected collection "${collection}", got "${event.collection}"`
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
return handler(event);
|
|
1776
|
+
};
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
// src/core/collection/const.ts
|
|
1780
|
+
var INTERNAL_COLLECTIONS = [
|
|
1781
|
+
"users",
|
|
1782
|
+
"payload-kv",
|
|
1783
|
+
"payload-locked-documents",
|
|
1784
|
+
"payload-preferences",
|
|
1785
|
+
"payload-migrations",
|
|
1786
|
+
"payload-folders",
|
|
1787
|
+
"field-configs",
|
|
1788
|
+
"system-media",
|
|
1789
|
+
"track-assets",
|
|
1790
|
+
"audiences",
|
|
1791
|
+
"email-logs",
|
|
1792
|
+
"api-usage",
|
|
1793
|
+
"tenant-analytics-daily",
|
|
1794
|
+
"tenant-web-analytics-config",
|
|
1795
|
+
"analytics-event-schemas",
|
|
1796
|
+
"subscriptions",
|
|
1797
|
+
"billing-history",
|
|
1798
|
+
"inventory-reservations",
|
|
1799
|
+
"order-status-logs",
|
|
1800
|
+
"api-keys",
|
|
1801
|
+
"personal-access-tokens",
|
|
1802
|
+
"tenant-entitlements",
|
|
1803
|
+
"tenant-purge-jobs",
|
|
1804
|
+
"direct-upload-sessions",
|
|
1805
|
+
"webhook-events",
|
|
1806
|
+
"webhook-deliveries",
|
|
1807
|
+
"audit-logs",
|
|
1808
|
+
"plans",
|
|
1809
|
+
"webhooks",
|
|
1810
|
+
"event-registrations"
|
|
1811
|
+
];
|
|
1812
|
+
var COLLECTIONS = [
|
|
1813
|
+
"tenants",
|
|
1814
|
+
"tenant-metadata",
|
|
1815
|
+
"tenant-logos",
|
|
1816
|
+
"products",
|
|
1817
|
+
"product-variants",
|
|
1818
|
+
"product-options",
|
|
1819
|
+
"product-option-values",
|
|
1820
|
+
"product-categories",
|
|
1821
|
+
"product-tags",
|
|
1822
|
+
"product-collections",
|
|
1823
|
+
"brands",
|
|
1824
|
+
"brand-logos",
|
|
1825
|
+
"orders",
|
|
1826
|
+
"order-items",
|
|
1827
|
+
"returns",
|
|
1828
|
+
"return-items",
|
|
1829
|
+
"fulfillments",
|
|
1830
|
+
"fulfillment-items",
|
|
1831
|
+
"transactions",
|
|
1832
|
+
"customers",
|
|
1833
|
+
"customer-profiles",
|
|
1834
|
+
"customer-profile-lists",
|
|
1835
|
+
"customer-addresses",
|
|
1836
|
+
"carts",
|
|
1837
|
+
"cart-items",
|
|
1838
|
+
"discounts",
|
|
1839
|
+
"shipping-policies",
|
|
1840
|
+
"shipping-zones",
|
|
1841
|
+
"documents",
|
|
1842
|
+
"document-categories",
|
|
1843
|
+
"document-types",
|
|
1844
|
+
"articles",
|
|
1845
|
+
"article-authors",
|
|
1846
|
+
"article-categories",
|
|
1847
|
+
"article-tags",
|
|
1848
|
+
"playlists",
|
|
1849
|
+
"playlist-categories",
|
|
1850
|
+
"playlist-tags",
|
|
1851
|
+
"tracks",
|
|
1852
|
+
"track-categories",
|
|
1853
|
+
"track-tags",
|
|
1854
|
+
"galleries",
|
|
1855
|
+
"gallery-categories",
|
|
1856
|
+
"gallery-tags",
|
|
1857
|
+
"gallery-items",
|
|
1858
|
+
"links",
|
|
1859
|
+
"link-categories",
|
|
1860
|
+
"link-tags",
|
|
1861
|
+
"canvases",
|
|
1862
|
+
"canvas-node-types",
|
|
1863
|
+
"canvas-edge-types",
|
|
1864
|
+
"canvas-categories",
|
|
1865
|
+
"canvas-tags",
|
|
1866
|
+
"canvas-nodes",
|
|
1867
|
+
"canvas-edges",
|
|
1868
|
+
"videos",
|
|
1869
|
+
"video-categories",
|
|
1870
|
+
"video-tags",
|
|
1871
|
+
"live-streams",
|
|
1872
|
+
"images",
|
|
1873
|
+
"forms",
|
|
1874
|
+
"form-submissions",
|
|
1875
|
+
// Community
|
|
1876
|
+
"posts",
|
|
1877
|
+
"comments",
|
|
1878
|
+
"reactions",
|
|
1879
|
+
"reaction-types",
|
|
1880
|
+
"bookmarks",
|
|
1881
|
+
"post-categories",
|
|
1882
|
+
// Events
|
|
1883
|
+
"event-calendars",
|
|
1884
|
+
"events",
|
|
1885
|
+
"event-categories",
|
|
1886
|
+
"event-occurrences",
|
|
1887
|
+
"event-tags"
|
|
1888
|
+
];
|
|
1889
|
+
var SERVER_ONLY_COLLECTIONS = [
|
|
1890
|
+
"customer-groups",
|
|
1891
|
+
"reports",
|
|
1892
|
+
"community-bans"
|
|
1893
|
+
];
|
|
1894
|
+
var SERVER_COLLECTIONS = [
|
|
1895
|
+
...COLLECTIONS,
|
|
1896
|
+
...SERVER_ONLY_COLLECTIONS
|
|
1897
|
+
];
|
|
2767
1898
|
|
|
2768
1899
|
// src/core/query/realtime.ts
|
|
2769
1900
|
var INITIAL_RECONNECT_DELAY = 1e3;
|
|
@@ -2883,151 +2014,41 @@ var RealtimeConnection = class {
|
|
|
2883
2014
|
}
|
|
2884
2015
|
currentEvent = "";
|
|
2885
2016
|
currentData = "";
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
}
|
|
2889
|
-
} catch {
|
|
2890
|
-
} finally {
|
|
2891
|
-
reader.releaseLock();
|
|
2892
|
-
this._connected = false;
|
|
2893
|
-
if (!signal.aborted) {
|
|
2894
|
-
this.scheduleReconnect();
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
}
|
|
2898
|
-
scheduleReconnect() {
|
|
2899
|
-
if (this.reconnectTimer) return;
|
|
2900
|
-
const delay2 = Math.min(
|
|
2901
|
-
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
2902
|
-
MAX_RECONNECT_DELAY
|
|
2903
|
-
);
|
|
2904
|
-
this.reconnectAttempt++;
|
|
2905
|
-
this.reconnectTimer = setTimeout(() => {
|
|
2906
|
-
this.reconnectTimer = null;
|
|
2907
|
-
this.abortController = new AbortController();
|
|
2908
|
-
this.startStream(this.abortController.signal);
|
|
2909
|
-
}, delay2);
|
|
2910
|
-
}
|
|
2911
|
-
};
|
|
2912
|
-
|
|
2913
|
-
// src/core/webhook/index.ts
|
|
2914
|
-
function isValidWebhookEvent(data) {
|
|
2915
|
-
if (typeof data !== "object" || data === null) return false;
|
|
2916
|
-
const obj = data;
|
|
2917
|
-
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
2918
|
-
}
|
|
2919
|
-
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
2920
|
-
function isRecord(value) {
|
|
2921
|
-
return typeof value === "object" && value !== null;
|
|
2922
|
-
}
|
|
2923
|
-
function hasString(value, key) {
|
|
2924
|
-
return typeof value[key] === "string";
|
|
2925
|
-
}
|
|
2926
|
-
function hasStringOrNumber(value, key) {
|
|
2927
|
-
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
2928
|
-
}
|
|
2929
|
-
function isCustomerPasswordResetWebhookEvent(event) {
|
|
2930
|
-
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord(event.data)) {
|
|
2931
|
-
return false;
|
|
2932
|
-
}
|
|
2933
|
-
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
2934
|
-
}
|
|
2935
|
-
function createCustomerAuthWebhookHandler(handlers) {
|
|
2936
|
-
return async (event) => {
|
|
2937
|
-
if (isCustomerPasswordResetWebhookEvent(event) && handlers.passwordReset) {
|
|
2938
|
-
await handlers.passwordReset(event.data, event);
|
|
2939
|
-
return;
|
|
2940
|
-
}
|
|
2941
|
-
await handlers.unhandled?.(event);
|
|
2942
|
-
};
|
|
2943
|
-
}
|
|
2944
|
-
async function verifySignature(payload, secret, signature, timestamp, deliveryId) {
|
|
2945
|
-
const encoder = new TextEncoder();
|
|
2946
|
-
const key = await crypto.subtle.importKey(
|
|
2947
|
-
"raw",
|
|
2948
|
-
encoder.encode(secret),
|
|
2949
|
-
{ name: "HMAC", hash: "SHA-256" },
|
|
2950
|
-
false,
|
|
2951
|
-
["verify"]
|
|
2952
|
-
);
|
|
2953
|
-
if (signature.length % 2 !== 0 || !/^[0-9a-fA-F]*$/.test(signature)) {
|
|
2954
|
-
return false;
|
|
2955
|
-
}
|
|
2956
|
-
const sigBytes = new Uint8Array(
|
|
2957
|
-
(signature.match(/.{2}/g) ?? []).map((byte) => parseInt(byte, 16))
|
|
2958
|
-
);
|
|
2959
|
-
return crypto.subtle.verify(
|
|
2960
|
-
"HMAC",
|
|
2961
|
-
key,
|
|
2962
|
-
sigBytes,
|
|
2963
|
-
encoder.encode(`${timestamp}.${deliveryId}.${payload}`)
|
|
2964
|
-
);
|
|
2965
|
-
}
|
|
2966
|
-
function timestampIsFresh(timestamp, toleranceSeconds) {
|
|
2967
|
-
if (!/^\d+$/.test(timestamp)) return false;
|
|
2968
|
-
const timestampMs = Number(timestamp);
|
|
2969
|
-
if (!Number.isFinite(timestampMs)) return false;
|
|
2970
|
-
const skewMs = Math.abs(Date.now() - timestampMs);
|
|
2971
|
-
return skewMs <= toleranceSeconds * 1e3;
|
|
2972
|
-
}
|
|
2973
|
-
async function handleWebhook(request, handler, options) {
|
|
2974
|
-
try {
|
|
2975
|
-
const rawBody = await request.text();
|
|
2976
|
-
if (options?.secret) {
|
|
2977
|
-
const signature = request.headers.get("x-webhook-signature") || "";
|
|
2978
|
-
const timestamp = request.headers.get("x-webhook-timestamp") || "";
|
|
2979
|
-
const deliveryId = request.headers.get("x-webhook-delivery-id") || "";
|
|
2980
|
-
const toleranceSeconds = options.toleranceSeconds ?? 300;
|
|
2981
|
-
const valid = Boolean(timestamp && deliveryId) && timestampIsFresh(timestamp, toleranceSeconds) && await verifySignature(
|
|
2982
|
-
rawBody,
|
|
2983
|
-
options.secret,
|
|
2984
|
-
signature,
|
|
2985
|
-
timestamp,
|
|
2986
|
-
deliveryId
|
|
2987
|
-
);
|
|
2988
|
-
if (!valid) {
|
|
2989
|
-
return new Response(
|
|
2990
|
-
JSON.stringify({ error: "Invalid webhook signature" }),
|
|
2991
|
-
{ status: 401, headers: { "Content-Type": "application/json" } }
|
|
2992
|
-
);
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
2019
|
+
}
|
|
2020
|
+
} catch {
|
|
2021
|
+
} finally {
|
|
2022
|
+
reader.releaseLock();
|
|
2023
|
+
this._connected = false;
|
|
2024
|
+
if (!signal.aborted) {
|
|
2025
|
+
this.scheduleReconnect();
|
|
2993
2026
|
}
|
|
2994
|
-
} else {
|
|
2995
|
-
console.warn(
|
|
2996
|
-
"[@01.software/sdk] Webhook signature verification is disabled. Set { secret } in handleWebhook() options to enable HMAC-SHA256 verification."
|
|
2997
|
-
);
|
|
2998
|
-
}
|
|
2999
|
-
const body = JSON.parse(rawBody);
|
|
3000
|
-
if (!isValidWebhookEvent(body)) {
|
|
3001
|
-
return new Response(
|
|
3002
|
-
JSON.stringify({ error: "Invalid webhook event format" }),
|
|
3003
|
-
{ status: 400, headers: { "Content-Type": "application/json" } }
|
|
3004
|
-
);
|
|
3005
2027
|
}
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
|
|
3009
|
-
|
|
2028
|
+
}
|
|
2029
|
+
scheduleReconnect() {
|
|
2030
|
+
if (this.reconnectTimer) return;
|
|
2031
|
+
const delay2 = Math.min(
|
|
2032
|
+
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
2033
|
+
MAX_RECONNECT_DELAY
|
|
3010
2034
|
);
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
});
|
|
2035
|
+
this.reconnectAttempt++;
|
|
2036
|
+
this.reconnectTimer = setTimeout(() => {
|
|
2037
|
+
this.reconnectTimer = null;
|
|
2038
|
+
this.abortController = new AbortController();
|
|
2039
|
+
this.startStream(this.abortController.signal);
|
|
2040
|
+
}, delay2);
|
|
3017
2041
|
}
|
|
3018
|
-
}
|
|
3019
|
-
function createTypedWebhookHandler(collection, handler) {
|
|
3020
|
-
return async (event) => {
|
|
3021
|
-
if (event.collection !== collection) {
|
|
3022
|
-
throw new Error(
|
|
3023
|
-
`Expected collection "${collection}", got "${event.collection}"`
|
|
3024
|
-
);
|
|
3025
|
-
}
|
|
3026
|
-
return handler(event);
|
|
3027
|
-
};
|
|
3028
|
-
}
|
|
2042
|
+
};
|
|
3029
2043
|
|
|
3030
2044
|
// src/utils/ecommerce.ts
|
|
2045
|
+
var ProductSelectionCodecError = class extends Error {
|
|
2046
|
+
constructor(message) {
|
|
2047
|
+
super(message);
|
|
2048
|
+
this.code = "ambiguous_product_selection_query";
|
|
2049
|
+
this.name = "ProductSelectionCodecError";
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
3031
2052
|
function getRelationID(value) {
|
|
3032
2053
|
if (typeof value === "string") return value;
|
|
3033
2054
|
if (typeof value === "number") return String(value);
|
|
@@ -3074,10 +2095,11 @@ function getFirstAvailableVariantPrimaryImage(variants) {
|
|
|
3074
2095
|
}
|
|
3075
2096
|
return null;
|
|
3076
2097
|
}
|
|
3077
|
-
function normalizeOptionValue(value, fallbackOptionId) {
|
|
2098
|
+
function normalizeOptionValue(value, fallbackOptionId, fallbackOptionSlug) {
|
|
3078
2099
|
return {
|
|
3079
2100
|
id: String(value.id),
|
|
3080
2101
|
optionId: getRelationID(value.option) ?? fallbackOptionId,
|
|
2102
|
+
optionSlug: fallbackOptionSlug,
|
|
3081
2103
|
label: value.value || value.slug || String(value.id),
|
|
3082
2104
|
slug: value.slug ?? null,
|
|
3083
2105
|
swatchColor: value.swatchColor ?? null,
|
|
@@ -3086,20 +2108,26 @@ function normalizeOptionValue(value, fallbackOptionId) {
|
|
|
3086
2108
|
order: value._order ?? value["_product-option-values_values_order"] ?? ""
|
|
3087
2109
|
};
|
|
3088
2110
|
}
|
|
3089
|
-
function normalizeVariantOptionValues(variant, valueToOptionId, optionIds) {
|
|
2111
|
+
function normalizeVariantOptionValues(variant, optionById, valueToOptionId, optionIds) {
|
|
3090
2112
|
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
2113
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
3091
2114
|
for (const rawValue of Array.isArray(variant.optionValues) ? variant.optionValues : []) {
|
|
3092
2115
|
const valueId = getRelationID(rawValue);
|
|
3093
2116
|
if (!valueId) continue;
|
|
3094
2117
|
const optionId = valueToOptionId.get(valueId) ?? (isProductOptionValueDoc(rawValue) ? getRelationID(rawValue.option) : void 0);
|
|
3095
2118
|
if (!optionId || optionValueByOptionId.has(optionId)) continue;
|
|
3096
2119
|
optionValueByOptionId.set(optionId, valueId);
|
|
2120
|
+
const optionSlug = optionById.get(optionId)?.slug;
|
|
2121
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
2122
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
2123
|
+
}
|
|
3097
2124
|
}
|
|
3098
2125
|
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
3099
2126
|
return {
|
|
3100
2127
|
id: String(variant.id),
|
|
3101
2128
|
optionValueIds,
|
|
3102
2129
|
optionValueByOptionId,
|
|
2130
|
+
optionValueByOptionSlug,
|
|
3103
2131
|
source: variant
|
|
3104
2132
|
};
|
|
3105
2133
|
}
|
|
@@ -3109,17 +2137,20 @@ function buildProductOptionMatrix({
|
|
|
3109
2137
|
}) {
|
|
3110
2138
|
const normalizedOptions = options.map((option) => {
|
|
3111
2139
|
const valuesById = /* @__PURE__ */ new Map();
|
|
2140
|
+
const optionSlug = option.slug ?? String(option.id);
|
|
3112
2141
|
for (const rawValue of option.values?.docs ?? []) {
|
|
3113
2142
|
if (!isProductOptionValueDoc(rawValue)) continue;
|
|
3114
2143
|
const normalizedValue = normalizeOptionValue(
|
|
3115
2144
|
rawValue,
|
|
3116
|
-
String(option.id)
|
|
2145
|
+
String(option.id),
|
|
2146
|
+
optionSlug
|
|
3117
2147
|
);
|
|
3118
2148
|
valuesById.set(normalizedValue.id, normalizedValue);
|
|
3119
2149
|
}
|
|
3120
2150
|
return {
|
|
3121
2151
|
id: String(option.id),
|
|
3122
2152
|
title: option.title ?? String(option.id),
|
|
2153
|
+
slug: optionSlug,
|
|
3123
2154
|
order: option._order ?? option["_product-options_options_order"] ?? "",
|
|
3124
2155
|
values: Array.from(valuesById.values()).sort(
|
|
3125
2156
|
(left, right) => compareOrder(left.order, right.order)
|
|
@@ -3129,24 +2160,113 @@ function buildProductOptionMatrix({
|
|
|
3129
2160
|
const optionById = new Map(
|
|
3130
2161
|
normalizedOptions.map((option) => [option.id, option])
|
|
3131
2162
|
);
|
|
2163
|
+
const optionBySlug = new Map(
|
|
2164
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
2165
|
+
);
|
|
3132
2166
|
const valueById = /* @__PURE__ */ new Map();
|
|
3133
2167
|
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
2168
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
3134
2169
|
for (const option of normalizedOptions) {
|
|
3135
2170
|
for (const value of option.values) {
|
|
3136
2171
|
valueById.set(value.id, value);
|
|
3137
2172
|
valueToOptionId.set(value.id, option.id);
|
|
2173
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
3138
2174
|
}
|
|
3139
2175
|
}
|
|
3140
2176
|
const optionIds = normalizedOptions.map((option) => option.id);
|
|
2177
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
3141
2178
|
const normalizedVariants = variants.map(
|
|
3142
|
-
(variant) => normalizeVariantOptionValues(
|
|
2179
|
+
(variant) => normalizeVariantOptionValues(
|
|
2180
|
+
variant,
|
|
2181
|
+
optionById,
|
|
2182
|
+
valueToOptionId,
|
|
2183
|
+
optionIds
|
|
2184
|
+
)
|
|
2185
|
+
);
|
|
2186
|
+
return {
|
|
2187
|
+
options: normalizedOptions,
|
|
2188
|
+
optionIds,
|
|
2189
|
+
optionSlugs,
|
|
2190
|
+
optionById,
|
|
2191
|
+
optionBySlug,
|
|
2192
|
+
valueById,
|
|
2193
|
+
valueToOptionId,
|
|
2194
|
+
valueToOptionSlug,
|
|
2195
|
+
variants: normalizedVariants
|
|
2196
|
+
};
|
|
2197
|
+
}
|
|
2198
|
+
function matrixOrder(index) {
|
|
2199
|
+
return String(index).padStart(6, "0");
|
|
2200
|
+
}
|
|
2201
|
+
function buildProductOptionMatrixFromDetail(detail) {
|
|
2202
|
+
const normalizedOptions = detail.options.map((option, optionIndex) => ({
|
|
2203
|
+
id: String(option.id),
|
|
2204
|
+
title: option.title || String(option.id),
|
|
2205
|
+
slug: option.slug,
|
|
2206
|
+
order: matrixOrder(optionIndex),
|
|
2207
|
+
values: option.values.map((value, valueIndex) => ({
|
|
2208
|
+
id: String(value.id),
|
|
2209
|
+
optionId: String(option.id),
|
|
2210
|
+
optionSlug: option.slug,
|
|
2211
|
+
label: value.value || value.slug || String(value.id),
|
|
2212
|
+
slug: value.slug,
|
|
2213
|
+
swatchColor: value.swatchColor ?? null,
|
|
2214
|
+
thumbnail: value.thumbnail ?? null,
|
|
2215
|
+
images: value.images ?? null,
|
|
2216
|
+
order: matrixOrder(valueIndex)
|
|
2217
|
+
}))
|
|
2218
|
+
}));
|
|
2219
|
+
const optionById = new Map(
|
|
2220
|
+
normalizedOptions.map((option) => [option.id, option])
|
|
3143
2221
|
);
|
|
2222
|
+
const optionBySlug = new Map(
|
|
2223
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
2224
|
+
);
|
|
2225
|
+
const valueById = /* @__PURE__ */ new Map();
|
|
2226
|
+
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
2227
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
2228
|
+
for (const option of normalizedOptions) {
|
|
2229
|
+
for (const value of option.values) {
|
|
2230
|
+
valueById.set(value.id, value);
|
|
2231
|
+
valueToOptionId.set(value.id, option.id);
|
|
2232
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
const optionIds = normalizedOptions.map((option) => option.id);
|
|
2236
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
2237
|
+
const normalizedVariants = detail.variants.map((variant) => {
|
|
2238
|
+
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
2239
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
2240
|
+
for (const rawValue of variant.optionValues) {
|
|
2241
|
+
const optionId = String(rawValue.optionId);
|
|
2242
|
+
const valueId = String(rawValue.valueId);
|
|
2243
|
+
const optionSlug = rawValue.optionSlug;
|
|
2244
|
+
if (!optionById.has(optionId)) continue;
|
|
2245
|
+
if (valueToOptionId.get(valueId) !== optionId) continue;
|
|
2246
|
+
if (optionValueByOptionId.has(optionId)) continue;
|
|
2247
|
+
optionValueByOptionId.set(optionId, valueId);
|
|
2248
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
2249
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
2250
|
+
}
|
|
2251
|
+
}
|
|
2252
|
+
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
2253
|
+
return {
|
|
2254
|
+
id: String(variant.id),
|
|
2255
|
+
optionValueIds,
|
|
2256
|
+
optionValueByOptionId,
|
|
2257
|
+
optionValueByOptionSlug,
|
|
2258
|
+
source: variant
|
|
2259
|
+
};
|
|
2260
|
+
});
|
|
3144
2261
|
return {
|
|
3145
2262
|
options: normalizedOptions,
|
|
3146
2263
|
optionIds,
|
|
2264
|
+
optionSlugs,
|
|
3147
2265
|
optionById,
|
|
2266
|
+
optionBySlug,
|
|
3148
2267
|
valueById,
|
|
3149
2268
|
valueToOptionId,
|
|
2269
|
+
valueToOptionSlug,
|
|
3150
2270
|
variants: normalizedVariants
|
|
3151
2271
|
};
|
|
3152
2272
|
}
|
|
@@ -3179,7 +2299,7 @@ function getAvailableOptionValues(matrix, optionId, selectedValueIds) {
|
|
|
3179
2299
|
)
|
|
3180
2300
|
);
|
|
3181
2301
|
const availableValueIds = new Set(
|
|
3182
|
-
matchingVariants.map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
2302
|
+
matchingVariants.filter((variant) => variant.source.isActive !== false).map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
3183
2303
|
);
|
|
3184
2304
|
return option.values.filter((value) => availableValueIds.has(value.id));
|
|
3185
2305
|
}
|
|
@@ -3194,6 +2314,659 @@ function resolveVariantForSelection(matrix, selectedValueIds) {
|
|
|
3194
2314
|
)
|
|
3195
2315
|
);
|
|
3196
2316
|
}
|
|
2317
|
+
function getVariantSelection(matrix, variantId) {
|
|
2318
|
+
if (variantId == null) return void 0;
|
|
2319
|
+
const id = String(variantId);
|
|
2320
|
+
return matrix.variants.find((variant) => variant.id === id);
|
|
2321
|
+
}
|
|
2322
|
+
function hasExplicitSelection(selection) {
|
|
2323
|
+
return Boolean(
|
|
2324
|
+
selection.variantId != null || selection.search || selection.valueIds || Object.keys(selection.byOptionId ?? {}).length > 0 || Object.keys(selection.byOptionSlug ?? {}).length > 0
|
|
2325
|
+
);
|
|
2326
|
+
}
|
|
2327
|
+
function assignSelectedValue(matrix, selectedByOptionId, optionId, valueId) {
|
|
2328
|
+
if (valueId == null) return false;
|
|
2329
|
+
const normalizedValueId = String(valueId);
|
|
2330
|
+
if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return false;
|
|
2331
|
+
selectedByOptionId.set(optionId, normalizedValueId);
|
|
2332
|
+
return true;
|
|
2333
|
+
}
|
|
2334
|
+
function assignSelectedValueSlugByOptionId(matrix, selectedByOptionId, optionId, valueSlug) {
|
|
2335
|
+
if (!valueSlug) return false;
|
|
2336
|
+
const option = matrix.optionById.get(optionId);
|
|
2337
|
+
if (!option) return false;
|
|
2338
|
+
const values = option.values.filter(
|
|
2339
|
+
(candidate) => candidate.slug === valueSlug
|
|
2340
|
+
);
|
|
2341
|
+
if (values.length > 1) {
|
|
2342
|
+
throw new ProductSelectionCodecError(
|
|
2343
|
+
`Ambiguous product selection value slug "${valueSlug}" for option "${optionId}". Use opt.<optionId>=<valueId>.`
|
|
2344
|
+
);
|
|
2345
|
+
}
|
|
2346
|
+
const value = values[0];
|
|
2347
|
+
if (!value) return false;
|
|
2348
|
+
selectedByOptionId.set(optionId, value.id);
|
|
2349
|
+
return true;
|
|
2350
|
+
}
|
|
2351
|
+
function assignSelectedValueSlugByOptionSlug(matrix, selectedByOptionId, optionSlug, valueSlug) {
|
|
2352
|
+
if (!valueSlug) return false;
|
|
2353
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
2354
|
+
if (!option) return false;
|
|
2355
|
+
const values = option.values.filter(
|
|
2356
|
+
(candidate) => candidate.slug === valueSlug
|
|
2357
|
+
);
|
|
2358
|
+
if (values.length > 1) {
|
|
2359
|
+
throw new ProductSelectionCodecError(
|
|
2360
|
+
`Ambiguous product selection value slug "${valueSlug}" for option "${optionSlug}". Use opt.<optionId>=<valueId>.`
|
|
2361
|
+
);
|
|
2362
|
+
}
|
|
2363
|
+
const value = values[0];
|
|
2364
|
+
if (!value) return false;
|
|
2365
|
+
selectedByOptionId.set(option.id, value.id);
|
|
2366
|
+
return true;
|
|
2367
|
+
}
|
|
2368
|
+
function toSearchParams(search) {
|
|
2369
|
+
if (!search) return new URLSearchParams();
|
|
2370
|
+
if (search instanceof URLSearchParams) return new URLSearchParams(search);
|
|
2371
|
+
if (search instanceof URL) return new URLSearchParams(search.searchParams);
|
|
2372
|
+
const trimmed = search.trim();
|
|
2373
|
+
if (!trimmed) return new URLSearchParams();
|
|
2374
|
+
try {
|
|
2375
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
2376
|
+
return new URL(trimmed).searchParams;
|
|
2377
|
+
}
|
|
2378
|
+
} catch {
|
|
2379
|
+
return new URLSearchParams();
|
|
2380
|
+
}
|
|
2381
|
+
return new URLSearchParams(
|
|
2382
|
+
trimmed.startsWith("?") ? trimmed.slice(1) : trimmed
|
|
2383
|
+
);
|
|
2384
|
+
}
|
|
2385
|
+
function slugLike(value) {
|
|
2386
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
2387
|
+
}
|
|
2388
|
+
function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
2389
|
+
const knownSelectionKeys = /* @__PURE__ */ new Set();
|
|
2390
|
+
const hasVariantParam = params.has("variant");
|
|
2391
|
+
const hasOptionParams = Array.from(params.keys()).some(
|
|
2392
|
+
(key) => key.startsWith("opt.")
|
|
2393
|
+
);
|
|
2394
|
+
if (hasVariantParam && hasOptionParams) {
|
|
2395
|
+
throw new ProductSelectionCodecError(
|
|
2396
|
+
"Product selection URL cannot mix variant=<variantId> with opt.<optionId>=<valueId> params."
|
|
2397
|
+
);
|
|
2398
|
+
}
|
|
2399
|
+
for (const option of matrix.options) {
|
|
2400
|
+
knownSelectionKeys.add(slugLike(option.slug));
|
|
2401
|
+
knownSelectionKeys.add(slugLike(option.title));
|
|
2402
|
+
for (const value of option.values) {
|
|
2403
|
+
if (value.slug) knownSelectionKeys.add(slugLike(value.slug));
|
|
2404
|
+
}
|
|
2405
|
+
}
|
|
2406
|
+
for (const [key, value] of params.entries()) {
|
|
2407
|
+
if (key.startsWith("opt.")) {
|
|
2408
|
+
const optionToken = key.slice(4);
|
|
2409
|
+
if (!optionToken || !matrix.optionBySlug.has(optionToken) && !matrix.optionById.has(optionToken)) {
|
|
2410
|
+
throw new ProductSelectionCodecError(
|
|
2411
|
+
`Unknown product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
|
|
2412
|
+
);
|
|
2413
|
+
}
|
|
2414
|
+
if (!value) {
|
|
2415
|
+
throw new ProductSelectionCodecError(
|
|
2416
|
+
`Product selection query parameter "${key}" requires a value ID or compatibility value slug.`
|
|
2417
|
+
);
|
|
2418
|
+
}
|
|
2419
|
+
continue;
|
|
2420
|
+
}
|
|
2421
|
+
if (key === "variant") {
|
|
2422
|
+
if (!value) {
|
|
2423
|
+
throw new ProductSelectionCodecError(
|
|
2424
|
+
'Product selection query parameter "variant" requires a variant ID.'
|
|
2425
|
+
);
|
|
2426
|
+
}
|
|
2427
|
+
if (!getVariantSelection(matrix, value)) {
|
|
2428
|
+
throw new ProductSelectionCodecError(
|
|
2429
|
+
`Unknown product selection variant "${value}".`
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
continue;
|
|
2433
|
+
}
|
|
2434
|
+
const keyToken = slugLike(key);
|
|
2435
|
+
if (knownSelectionKeys.has(keyToken) || value === "" && knownSelectionKeys.has(keyToken)) {
|
|
2436
|
+
throw new ProductSelectionCodecError(
|
|
2437
|
+
`Ambiguous product selection query parameter "${key}". Use opt.<optionId>=<valueId>.`
|
|
2438
|
+
);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
function emitCompatibilityOptionIdParam(options, event) {
|
|
2443
|
+
try {
|
|
2444
|
+
if (options?.onCompatibilityOptionIdParam) {
|
|
2445
|
+
options.onCompatibilityOptionIdParam(event);
|
|
2446
|
+
return;
|
|
2447
|
+
}
|
|
2448
|
+
options?.onLegacyOptionIdParam?.(event);
|
|
2449
|
+
} catch {
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
function assignSearchSelection(matrix, selectedByOptionId, search, options) {
|
|
2453
|
+
if (!search) return null;
|
|
2454
|
+
const params = toSearchParams(search);
|
|
2455
|
+
assertNoAmbiguousSelectionParams(matrix, params);
|
|
2456
|
+
const variantParam = params.get("variant");
|
|
2457
|
+
if (variantParam != null) {
|
|
2458
|
+
const variantSelection = getVariantSelection(matrix, variantParam);
|
|
2459
|
+
if (!variantSelection) {
|
|
2460
|
+
throw new ProductSelectionCodecError(
|
|
2461
|
+
`Unknown product selection variant "${variantParam}".`
|
|
2462
|
+
);
|
|
2463
|
+
}
|
|
2464
|
+
for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
|
|
2465
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2466
|
+
}
|
|
2467
|
+
return variantSelection.id;
|
|
2468
|
+
}
|
|
2469
|
+
for (const [key, valueToken] of params.entries()) {
|
|
2470
|
+
if (!key.startsWith("opt.")) continue;
|
|
2471
|
+
const optionToken = key.slice(4);
|
|
2472
|
+
const optionById = matrix.optionById.get(optionToken);
|
|
2473
|
+
if (optionById) {
|
|
2474
|
+
if (assignSelectedValue(
|
|
2475
|
+
matrix,
|
|
2476
|
+
selectedByOptionId,
|
|
2477
|
+
optionById.id,
|
|
2478
|
+
valueToken
|
|
2479
|
+
)) {
|
|
2480
|
+
continue;
|
|
2481
|
+
}
|
|
2482
|
+
const before = selectedByOptionId.get(optionById.id);
|
|
2483
|
+
if (assignSelectedValueSlugByOptionId(
|
|
2484
|
+
matrix,
|
|
2485
|
+
selectedByOptionId,
|
|
2486
|
+
optionById.id,
|
|
2487
|
+
valueToken
|
|
2488
|
+
) && selectedByOptionId.get(optionById.id) !== before) {
|
|
2489
|
+
emitCompatibilityOptionIdParam(options, {
|
|
2490
|
+
optionId: optionById.id,
|
|
2491
|
+
optionSlug: optionById.slug,
|
|
2492
|
+
valueSlug: valueToken,
|
|
2493
|
+
searchParam: key
|
|
2494
|
+
});
|
|
2495
|
+
continue;
|
|
2496
|
+
}
|
|
2497
|
+
throw new ProductSelectionCodecError(
|
|
2498
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
|
|
2499
|
+
);
|
|
2500
|
+
}
|
|
2501
|
+
const optionBySlug = matrix.optionBySlug.get(optionToken);
|
|
2502
|
+
if (optionBySlug) {
|
|
2503
|
+
if (assignSelectedValueSlugByOptionSlug(
|
|
2504
|
+
matrix,
|
|
2505
|
+
selectedByOptionId,
|
|
2506
|
+
optionBySlug.slug,
|
|
2507
|
+
valueToken
|
|
2508
|
+
)) {
|
|
2509
|
+
continue;
|
|
2510
|
+
}
|
|
2511
|
+
if (matrix.valueById.has(valueToken)) {
|
|
2512
|
+
throw new ProductSelectionCodecError(
|
|
2513
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionId>=<valueId>.`
|
|
2514
|
+
);
|
|
2515
|
+
}
|
|
2516
|
+
throw new ProductSelectionCodecError(
|
|
2517
|
+
`Unknown product selection value "${valueToken}" for option "${optionToken}". Use opt.<optionSlug>=<valueSlug> for compatibility URLs.`
|
|
2518
|
+
);
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
return null;
|
|
2522
|
+
}
|
|
2523
|
+
function normalizeProductSelection(detail, selection = {}, options) {
|
|
2524
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2525
|
+
return normalizeProductSelectionFromMatrix(matrix, selection, options);
|
|
2526
|
+
}
|
|
2527
|
+
function normalizeProductSelectionFromMatrix(matrix, selection = {}, options) {
|
|
2528
|
+
const selectedByOptionId = /* @__PURE__ */ new Map();
|
|
2529
|
+
const variantSelection = getVariantSelection(matrix, selection.variantId);
|
|
2530
|
+
const variantId = variantSelection?.id ?? null;
|
|
2531
|
+
if (variantSelection) {
|
|
2532
|
+
for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
|
|
2533
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2534
|
+
}
|
|
2535
|
+
}
|
|
2536
|
+
for (const rawValueId of selection.valueIds ?? []) {
|
|
2537
|
+
const valueId = getRelationID(rawValueId);
|
|
2538
|
+
if (!valueId) continue;
|
|
2539
|
+
const optionId = matrix.valueToOptionId.get(valueId);
|
|
2540
|
+
if (!optionId) continue;
|
|
2541
|
+
selectedByOptionId.set(optionId, valueId);
|
|
2542
|
+
}
|
|
2543
|
+
const searchVariantId = assignSearchSelection(
|
|
2544
|
+
matrix,
|
|
2545
|
+
selectedByOptionId,
|
|
2546
|
+
selection.search,
|
|
2547
|
+
options
|
|
2548
|
+
);
|
|
2549
|
+
for (const [rawOptionId, rawSelection] of Object.entries(
|
|
2550
|
+
selection.byOptionId ?? {}
|
|
2551
|
+
)) {
|
|
2552
|
+
const optionId = String(rawOptionId);
|
|
2553
|
+
if (!matrix.optionById.has(optionId)) continue;
|
|
2554
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
2555
|
+
assignSelectedValue(
|
|
2556
|
+
matrix,
|
|
2557
|
+
selectedByOptionId,
|
|
2558
|
+
optionId,
|
|
2559
|
+
rawSelection.valueId
|
|
2560
|
+
);
|
|
2561
|
+
continue;
|
|
2562
|
+
}
|
|
2563
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
2564
|
+
assignSelectedValue(matrix, selectedByOptionId, optionId, rawSelection);
|
|
2565
|
+
continue;
|
|
2566
|
+
}
|
|
2567
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
2568
|
+
assignSelectedValueSlugByOptionId(
|
|
2569
|
+
matrix,
|
|
2570
|
+
selectedByOptionId,
|
|
2571
|
+
optionId,
|
|
2572
|
+
rawSelection.valueSlug
|
|
2573
|
+
);
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
for (const [rawOptionSlug, rawSelection] of Object.entries(
|
|
2577
|
+
selection.byOptionSlug ?? {}
|
|
2578
|
+
)) {
|
|
2579
|
+
const optionSlug = String(rawOptionSlug);
|
|
2580
|
+
if (!matrix.optionBySlug.has(optionSlug)) continue;
|
|
2581
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
2582
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
2583
|
+
if (option) {
|
|
2584
|
+
assignSelectedValue(
|
|
2585
|
+
matrix,
|
|
2586
|
+
selectedByOptionId,
|
|
2587
|
+
option.id,
|
|
2588
|
+
rawSelection.valueId
|
|
2589
|
+
);
|
|
2590
|
+
}
|
|
2591
|
+
continue;
|
|
2592
|
+
}
|
|
2593
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
2594
|
+
assignSelectedValueSlugByOptionSlug(
|
|
2595
|
+
matrix,
|
|
2596
|
+
selectedByOptionId,
|
|
2597
|
+
optionSlug,
|
|
2598
|
+
rawSelection.valueSlug
|
|
2599
|
+
);
|
|
2600
|
+
continue;
|
|
2601
|
+
}
|
|
2602
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
2603
|
+
assignSelectedValueSlugByOptionSlug(
|
|
2604
|
+
matrix,
|
|
2605
|
+
selectedByOptionId,
|
|
2606
|
+
optionSlug,
|
|
2607
|
+
String(rawSelection)
|
|
2608
|
+
);
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
const byOptionId = Object.fromEntries(
|
|
2612
|
+
matrix.optionIds.map((optionId) => [optionId, selectedByOptionId.get(optionId)]).filter((entry) => Boolean(entry[1]))
|
|
2613
|
+
);
|
|
2614
|
+
const byOptionSlug = Object.fromEntries(
|
|
2615
|
+
matrix.options.map((option) => {
|
|
2616
|
+
const valueId = selectedByOptionId.get(option.id);
|
|
2617
|
+
const value = valueId ? matrix.valueById.get(valueId) : void 0;
|
|
2618
|
+
return [option.slug, value?.slug ?? void 0];
|
|
2619
|
+
}).filter((entry) => Boolean(entry[1]))
|
|
2620
|
+
);
|
|
2621
|
+
return {
|
|
2622
|
+
byOptionSlug,
|
|
2623
|
+
byOptionId,
|
|
2624
|
+
valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
|
|
2625
|
+
variantId: searchVariantId ?? variantId
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
function parseProductSelection(detail, search, options) {
|
|
2629
|
+
return normalizeProductSelection(detail, { search }, options);
|
|
2630
|
+
}
|
|
2631
|
+
function stringifyProductSelection(detail, selection = {}, options) {
|
|
2632
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2633
|
+
const normalized = normalizeProductSelectionFromMatrix(
|
|
2634
|
+
matrix,
|
|
2635
|
+
selection,
|
|
2636
|
+
options
|
|
2637
|
+
);
|
|
2638
|
+
const params = new URLSearchParams();
|
|
2639
|
+
if (hasExplicitSelection(selection)) {
|
|
2640
|
+
const matchingVariants = getMatchingVariantEntries(matrix, normalized);
|
|
2641
|
+
const exactVariant = getExactSelectedVariantEntry(
|
|
2642
|
+
matrix,
|
|
2643
|
+
normalized,
|
|
2644
|
+
matchingVariants
|
|
2645
|
+
);
|
|
2646
|
+
if (exactVariant) {
|
|
2647
|
+
params.set("variant", exactVariant.id);
|
|
2648
|
+
return params.toString();
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
for (const optionId of matrix.optionIds) {
|
|
2652
|
+
const valueId = normalized.byOptionId[optionId];
|
|
2653
|
+
if (!valueId) continue;
|
|
2654
|
+
if (!matrix.optionById.has(optionId) || !matrix.valueById.has(valueId)) {
|
|
2655
|
+
continue;
|
|
2656
|
+
}
|
|
2657
|
+
params.append(`opt.${optionId}`, valueId);
|
|
2658
|
+
}
|
|
2659
|
+
return params.toString();
|
|
2660
|
+
}
|
|
2661
|
+
function createProductSelectionCodec(detail, options) {
|
|
2662
|
+
return {
|
|
2663
|
+
parse: (search) => parseProductSelection(detail, search, options),
|
|
2664
|
+
stringify: (selection = {}) => stringifyProductSelection(detail, selection, options)
|
|
2665
|
+
};
|
|
2666
|
+
}
|
|
2667
|
+
function selectedEntries(selection) {
|
|
2668
|
+
return Object.entries(selection.byOptionId);
|
|
2669
|
+
}
|
|
2670
|
+
function getMatchingVariantEntries(matrix, selection) {
|
|
2671
|
+
const entries = selectedEntries(selection);
|
|
2672
|
+
if (entries.length === 0) return matrix.variants;
|
|
2673
|
+
return matrix.variants.filter(
|
|
2674
|
+
(variant) => entries.every(
|
|
2675
|
+
([optionId, valueId]) => variant.optionValueByOptionId.get(optionId) === valueId
|
|
2676
|
+
)
|
|
2677
|
+
);
|
|
2678
|
+
}
|
|
2679
|
+
function activeVariantEntries(variants) {
|
|
2680
|
+
return variants.filter((variant) => variant.source.isActive !== false);
|
|
2681
|
+
}
|
|
2682
|
+
function getExactSelectedVariantEntry(matrix, selection, matchingVariants) {
|
|
2683
|
+
if (matrix.optionIds.length === 0) {
|
|
2684
|
+
return getVariantSelection(matrix, selection.variantId) ?? (matchingVariants.length === 1 ? matchingVariants[0] ?? null : null);
|
|
2685
|
+
}
|
|
2686
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
2687
|
+
(optionId) => Boolean(selection.byOptionId[optionId])
|
|
2688
|
+
);
|
|
2689
|
+
if (!allOptionsSelected) return null;
|
|
2690
|
+
return matchingVariants.find(
|
|
2691
|
+
(variant) => matrix.optionIds.every(
|
|
2692
|
+
(optionId) => variant.optionValueByOptionId.get(optionId) === selection.byOptionId[optionId]
|
|
2693
|
+
)
|
|
2694
|
+
) ?? null;
|
|
2695
|
+
}
|
|
2696
|
+
function buildSelectionPrice(variants) {
|
|
2697
|
+
const { min, max } = getMinMax(variants.map((variant) => variant.price));
|
|
2698
|
+
const { min: compareAtMin, max: compareAtMax } = getMinMax(
|
|
2699
|
+
variants.map((variant) => variant.compareAtPrice)
|
|
2700
|
+
);
|
|
2701
|
+
return {
|
|
2702
|
+
min,
|
|
2703
|
+
max,
|
|
2704
|
+
compareAtMin,
|
|
2705
|
+
compareAtMax,
|
|
2706
|
+
isRange: min !== null && max !== null ? min !== max : false
|
|
2707
|
+
};
|
|
2708
|
+
}
|
|
2709
|
+
function firstMedia(value) {
|
|
2710
|
+
if (value == null) return null;
|
|
2711
|
+
if (Array.isArray(value)) return firstMedia(value[0]);
|
|
2712
|
+
return value;
|
|
2713
|
+
}
|
|
2714
|
+
function isPresentMedia(value) {
|
|
2715
|
+
return value != null;
|
|
2716
|
+
}
|
|
2717
|
+
function mediaArray(values) {
|
|
2718
|
+
if (!Array.isArray(values)) return [];
|
|
2719
|
+
return values.filter(isPresentMedia);
|
|
2720
|
+
}
|
|
2721
|
+
function buildSelectionMedia(detail, selectedVariant, matchingVariants, selectedValues) {
|
|
2722
|
+
const selectedValueImages = selectedValues.flatMap(
|
|
2723
|
+
(value) => mediaArray(value.images)
|
|
2724
|
+
);
|
|
2725
|
+
const selectedValuePrimary = selectedValues.map((value) => firstMedia(value.thumbnail) ?? firstMedia(value.images)).find((value) => value != null) ?? null;
|
|
2726
|
+
const selectedVariantPrimary = firstMedia(selectedVariant?.thumbnail) ?? firstMedia(selectedVariant?.images);
|
|
2727
|
+
const matchingVariantPrimary = matchingVariants.map(
|
|
2728
|
+
(variant) => firstMedia(variant.thumbnail) ?? firstMedia(variant.images)
|
|
2729
|
+
).find((value) => value != null) ?? null;
|
|
2730
|
+
const detailImages = mediaArray(detail.images);
|
|
2731
|
+
const primaryImage = selectedVariantPrimary ?? selectedValuePrimary ?? firstMedia(detail.listing.primaryImage) ?? matchingVariantPrimary ?? firstMedia(detailImages);
|
|
2732
|
+
const images = mediaArray(selectedVariant?.images).length > 0 ? mediaArray(selectedVariant?.images) : selectedValueImages.length > 0 ? selectedValueImages : detailImages;
|
|
2733
|
+
return {
|
|
2734
|
+
primaryImage,
|
|
2735
|
+
images
|
|
2736
|
+
};
|
|
2737
|
+
}
|
|
2738
|
+
function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
2739
|
+
if (selectedVariant) {
|
|
2740
|
+
const availableStock = selectedVariant.isUnlimited ? null : Math.max(0, selectedVariant.stock - selectedVariant.reservedStock);
|
|
2741
|
+
const isActive = selectedVariant.isActive !== false;
|
|
2742
|
+
return {
|
|
2743
|
+
availableForSale: isActive && (selectedVariant.isUnlimited || (availableStock ?? 0) > 0),
|
|
2744
|
+
isUnlimited: selectedVariant.isUnlimited,
|
|
2745
|
+
stock: selectedVariant.stock,
|
|
2746
|
+
reservedStock: selectedVariant.reservedStock,
|
|
2747
|
+
availableStock
|
|
2748
|
+
};
|
|
2749
|
+
}
|
|
2750
|
+
return {
|
|
2751
|
+
availableForSale: matchingVariants.some(isVariantAvailableForSale),
|
|
2752
|
+
isUnlimited: matchingVariants.some((variant) => variant.isUnlimited),
|
|
2753
|
+
stock: null,
|
|
2754
|
+
reservedStock: null,
|
|
2755
|
+
availableStock: null
|
|
2756
|
+
};
|
|
2757
|
+
}
|
|
2758
|
+
function buildAvailableValueStock(variants) {
|
|
2759
|
+
const activeVariants = variants.filter((variant) => variant.isActive !== false);
|
|
2760
|
+
const isUnlimited = activeVariants.some((variant) => variant.isUnlimited);
|
|
2761
|
+
const availableStock = isUnlimited ? null : activeVariants.reduce(
|
|
2762
|
+
(sum, variant) => sum + Math.max(0, variant.stock - variant.reservedStock),
|
|
2763
|
+
0
|
|
2764
|
+
);
|
|
2765
|
+
return {
|
|
2766
|
+
availableForSale: activeVariants.some(isVariantAvailableForSale),
|
|
2767
|
+
isUnlimited,
|
|
2768
|
+
availableStock
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
function getCandidateVariantsForValue(matrix, optionId, valueId, selectedValueIds) {
|
|
2772
|
+
const selectedByOption = getSelectedValueByOptionId(matrix, selectedValueIds);
|
|
2773
|
+
selectedByOption.set(optionId, valueId);
|
|
2774
|
+
return matrix.variants.filter(
|
|
2775
|
+
(variant) => Array.from(selectedByOption.entries()).every(
|
|
2776
|
+
([selectedOptionId, selectedValueId]) => variant.optionValueByOptionId.get(selectedOptionId) === selectedValueId
|
|
2777
|
+
)
|
|
2778
|
+
).map((variant) => variant.source);
|
|
2779
|
+
}
|
|
2780
|
+
function getResolutionContext(context) {
|
|
2781
|
+
return {
|
|
2782
|
+
images: context?.images ?? context?.detail?.images ?? [],
|
|
2783
|
+
listing: context?.listing ?? context?.detail?.listing ?? {}
|
|
2784
|
+
};
|
|
2785
|
+
}
|
|
2786
|
+
function resolveProductSelectionFromMatrix(matrix, selection = {}, options, context) {
|
|
2787
|
+
const { images, listing } = getResolutionContext(context);
|
|
2788
|
+
const effectiveSelection = hasExplicitSelection(selection) || listing.selectionHintVariant == null ? selection : { ...selection, variantId: listing.selectionHintVariant };
|
|
2789
|
+
const normalizedSelection = normalizeProductSelectionFromMatrix(
|
|
2790
|
+
matrix,
|
|
2791
|
+
effectiveSelection,
|
|
2792
|
+
options
|
|
2793
|
+
);
|
|
2794
|
+
const matchingVariantEntries = getMatchingVariantEntries(
|
|
2795
|
+
matrix,
|
|
2796
|
+
normalizedSelection
|
|
2797
|
+
);
|
|
2798
|
+
const activeMatchingVariantEntries = activeVariantEntries(
|
|
2799
|
+
matchingVariantEntries
|
|
2800
|
+
);
|
|
2801
|
+
const selectedVariantEntry = getExactSelectedVariantEntry(
|
|
2802
|
+
matrix,
|
|
2803
|
+
normalizedSelection,
|
|
2804
|
+
matchingVariantEntries
|
|
2805
|
+
);
|
|
2806
|
+
const selectedVariant = selectedVariantEntry?.source ?? null;
|
|
2807
|
+
const matchingVariants = activeMatchingVariantEntries.map(
|
|
2808
|
+
(variant) => variant.source
|
|
2809
|
+
);
|
|
2810
|
+
const selectedValues = matrix.optionIds.map((optionId) => normalizedSelection.byOptionId[optionId]).map((valueId) => valueId ? matrix.valueById.get(valueId) : void 0).filter((value) => value !== void 0);
|
|
2811
|
+
const availableValuesByOptionId = Object.fromEntries(
|
|
2812
|
+
matrix.options.map((option) => {
|
|
2813
|
+
const availableValueIds = new Set(
|
|
2814
|
+
getAvailableOptionValues(
|
|
2815
|
+
matrix,
|
|
2816
|
+
option.id,
|
|
2817
|
+
normalizedSelection.valueIds
|
|
2818
|
+
).map((value) => value.id)
|
|
2819
|
+
);
|
|
2820
|
+
return [
|
|
2821
|
+
option.id,
|
|
2822
|
+
option.values.map((value) => ({
|
|
2823
|
+
valueId: value.id,
|
|
2824
|
+
value: value.label,
|
|
2825
|
+
slug: value.slug ?? "",
|
|
2826
|
+
selected: normalizedSelection.byOptionId[option.id] === value.id,
|
|
2827
|
+
available: availableValueIds.has(value.id),
|
|
2828
|
+
...buildAvailableValueStock(
|
|
2829
|
+
getCandidateVariantsForValue(
|
|
2830
|
+
matrix,
|
|
2831
|
+
option.id,
|
|
2832
|
+
value.id,
|
|
2833
|
+
normalizedSelection.valueIds
|
|
2834
|
+
)
|
|
2835
|
+
),
|
|
2836
|
+
swatchColor: value.swatchColor ?? null,
|
|
2837
|
+
thumbnail: value.thumbnail ?? null,
|
|
2838
|
+
images: value.images ?? null
|
|
2839
|
+
}))
|
|
2840
|
+
];
|
|
2841
|
+
})
|
|
2842
|
+
);
|
|
2843
|
+
const availableValuesByOptionSlug = Object.fromEntries(
|
|
2844
|
+
matrix.options.map((option) => [
|
|
2845
|
+
option.slug,
|
|
2846
|
+
availableValuesByOptionId[option.id] ?? []
|
|
2847
|
+
])
|
|
2848
|
+
);
|
|
2849
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
2850
|
+
(optionId) => Boolean(normalizedSelection.byOptionId[optionId])
|
|
2851
|
+
);
|
|
2852
|
+
const priceVariants = selectedVariant ? [selectedVariant] : matchingVariants;
|
|
2853
|
+
return {
|
|
2854
|
+
normalizedSelection,
|
|
2855
|
+
selectedVariant,
|
|
2856
|
+
matchingVariants,
|
|
2857
|
+
partialVariants: selectedVariant ? [] : matchingVariants,
|
|
2858
|
+
availableValuesByOptionSlug,
|
|
2859
|
+
availableValuesByOptionId,
|
|
2860
|
+
allOptionsSelected,
|
|
2861
|
+
price: buildSelectionPrice(priceVariants),
|
|
2862
|
+
media: buildSelectionMedia(
|
|
2863
|
+
{
|
|
2864
|
+
images,
|
|
2865
|
+
listing: {
|
|
2866
|
+
primaryImage: listing.primaryImage ?? null
|
|
2867
|
+
}
|
|
2868
|
+
},
|
|
2869
|
+
selectedVariant,
|
|
2870
|
+
matchingVariants,
|
|
2871
|
+
selectedValues
|
|
2872
|
+
),
|
|
2873
|
+
stock: buildSelectionStock(selectedVariant, matchingVariants)
|
|
2874
|
+
};
|
|
2875
|
+
}
|
|
2876
|
+
function resolveProductSelection(detail, selection = {}, options) {
|
|
2877
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
2878
|
+
return resolveProductSelectionFromMatrix(matrix, selection, options, {
|
|
2879
|
+
detail
|
|
2880
|
+
});
|
|
2881
|
+
}
|
|
2882
|
+
function isProductDetailImageMedia(value) {
|
|
2883
|
+
return typeof value === "object" && value !== null;
|
|
2884
|
+
}
|
|
2885
|
+
function mediaDedupeKey(value) {
|
|
2886
|
+
if (value.id != null) return `id:${String(value.id)}`;
|
|
2887
|
+
if (value.url) return `url:${value.url}`;
|
|
2888
|
+
return null;
|
|
2889
|
+
}
|
|
2890
|
+
function getProductSelectionImages(resolution) {
|
|
2891
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2892
|
+
const images = [];
|
|
2893
|
+
const candidates = [
|
|
2894
|
+
resolution.media.primaryImage,
|
|
2895
|
+
...resolution.media.images
|
|
2896
|
+
].filter(isProductDetailImageMedia);
|
|
2897
|
+
for (const candidate of candidates) {
|
|
2898
|
+
const key = mediaDedupeKey(candidate);
|
|
2899
|
+
if (key && seen.has(key)) continue;
|
|
2900
|
+
if (key) seen.add(key);
|
|
2901
|
+
images.push(candidate);
|
|
2902
|
+
}
|
|
2903
|
+
return images;
|
|
2904
|
+
}
|
|
2905
|
+
function getProductHrefSlug(product) {
|
|
2906
|
+
if ("product" in product && product.product?.slug) {
|
|
2907
|
+
return product.product.slug;
|
|
2908
|
+
}
|
|
2909
|
+
if ("slug" in product && product.slug) return product.slug;
|
|
2910
|
+
throw new ProductSelectionCodecError(
|
|
2911
|
+
"Product slug is required to build a product href."
|
|
2912
|
+
);
|
|
2913
|
+
}
|
|
2914
|
+
function joinProductPath(basePath, slug, trailingSlash) {
|
|
2915
|
+
const base = basePath.replace(/\/+$/, "");
|
|
2916
|
+
const encodedSlug = encodeURIComponent(slug);
|
|
2917
|
+
return `${base}/${encodedSlug}${trailingSlash ? "/" : ""}`;
|
|
2918
|
+
}
|
|
2919
|
+
function getProductHrefGroupSelection(group, matrix) {
|
|
2920
|
+
if (!group) return null;
|
|
2921
|
+
if (group.variantId != null) return { variantId: group.variantId };
|
|
2922
|
+
const listingVariantId = group.listing?.selectionHintVariant;
|
|
2923
|
+
const optionId = group.optionId != null ? String(group.optionId) : group.optionSlug ? matrix?.optionBySlug.get(group.optionSlug)?.id : void 0;
|
|
2924
|
+
if (!optionId) {
|
|
2925
|
+
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
2926
|
+
}
|
|
2927
|
+
const option = matrix?.optionById.get(optionId);
|
|
2928
|
+
const optionValueId = group.optionValueId != null ? String(group.optionValueId) : group.optionValueSlug && option ? option.values.find((value) => value.slug === group.optionValueSlug)?.id : void 0;
|
|
2929
|
+
if (!optionValueId) {
|
|
2930
|
+
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
2931
|
+
}
|
|
2932
|
+
return { byOptionId: { [optionId]: optionValueId } };
|
|
2933
|
+
}
|
|
2934
|
+
function buildProductHref(product, group, options = {}) {
|
|
2935
|
+
const path = joinProductPath(
|
|
2936
|
+
options.basePath ?? "/products",
|
|
2937
|
+
getProductHrefSlug(product),
|
|
2938
|
+
options.trailingSlash ?? false
|
|
2939
|
+
);
|
|
2940
|
+
const params = new URLSearchParams();
|
|
2941
|
+
if (options.detail && options.selection) {
|
|
2942
|
+
const selection = stringifyProductSelection(options.detail, options.selection);
|
|
2943
|
+
return selection ? `${path}?${selection}` : path;
|
|
2944
|
+
}
|
|
2945
|
+
const groupSelection = getProductHrefGroupSelection(group, options.matrix);
|
|
2946
|
+
if (groupSelection) {
|
|
2947
|
+
if (options.detail) {
|
|
2948
|
+
const selection = stringifyProductSelection(options.detail, groupSelection);
|
|
2949
|
+
return selection ? `${path}?${selection}` : path;
|
|
2950
|
+
}
|
|
2951
|
+
if (groupSelection.variantId != null) {
|
|
2952
|
+
params.set("variant", String(groupSelection.variantId));
|
|
2953
|
+
return `${path}?${params.toString()}`;
|
|
2954
|
+
}
|
|
2955
|
+
const [selectionEntry] = Object.entries(groupSelection.byOptionId ?? {});
|
|
2956
|
+
if (selectionEntry) {
|
|
2957
|
+
const [optionId, valueId] = selectionEntry;
|
|
2958
|
+
params.set(`opt.${optionId}`, String(valueId));
|
|
2959
|
+
return `${path}?${params.toString()}`;
|
|
2960
|
+
}
|
|
2961
|
+
}
|
|
2962
|
+
if (group?.optionValueSlug) {
|
|
2963
|
+
const optionSlug = group.optionSlug ?? (group.optionId != null ? options.matrix?.optionById.get(String(group.optionId))?.slug : void 0);
|
|
2964
|
+
if (optionSlug) {
|
|
2965
|
+
params.set(`opt.${optionSlug}`, group.optionValueSlug);
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2968
|
+
return params.size > 0 ? `${path}?${params.toString()}` : path;
|
|
2969
|
+
}
|
|
3197
2970
|
function compareVariantOrder(a, b) {
|
|
3198
2971
|
const aOrder = Number(a._order ?? Number.MAX_SAFE_INTEGER);
|
|
3199
2972
|
const bOrder = Number(b._order ?? Number.MAX_SAFE_INTEGER);
|
|
@@ -3573,6 +3346,11 @@ function createAnalytics(config) {
|
|
|
3573
3346
|
}
|
|
3574
3347
|
};
|
|
3575
3348
|
}
|
|
3349
|
+
|
|
3350
|
+
// src/index.ts
|
|
3351
|
+
function createClient2(options) {
|
|
3352
|
+
return createClient(options);
|
|
3353
|
+
}
|
|
3576
3354
|
export {
|
|
3577
3355
|
ApiError,
|
|
3578
3356
|
AuthError,
|
|
@@ -3580,58 +3358,47 @@ export {
|
|
|
3580
3358
|
COLLECTIONS,
|
|
3581
3359
|
CUSTOMER_PASSWORD_RESET_OPERATION,
|
|
3582
3360
|
CartApi,
|
|
3583
|
-
Client,
|
|
3584
|
-
CollectionClient,
|
|
3585
|
-
CollectionHooks,
|
|
3586
|
-
CollectionQueryBuilder,
|
|
3587
3361
|
CommerceClient,
|
|
3588
3362
|
CommunityClient,
|
|
3589
3363
|
ConfigError,
|
|
3590
3364
|
ConflictError,
|
|
3591
3365
|
CustomerAuth,
|
|
3592
|
-
CustomerHooks,
|
|
3593
3366
|
CustomerNamespace,
|
|
3594
3367
|
DiscountApi,
|
|
3595
3368
|
GoneError,
|
|
3596
3369
|
IMAGE_SIZES,
|
|
3597
3370
|
INTERNAL_COLLECTIONS,
|
|
3598
|
-
ModerationApi,
|
|
3599
3371
|
NetworkError,
|
|
3600
3372
|
NotFoundError,
|
|
3601
3373
|
OrderApi,
|
|
3602
3374
|
PermissionError,
|
|
3603
3375
|
ProductApi,
|
|
3604
|
-
|
|
3376
|
+
ProductSelectionCodecError,
|
|
3605
3377
|
RateLimitError,
|
|
3606
|
-
ReadOnlyCollectionClient,
|
|
3607
3378
|
RealtimeConnection,
|
|
3608
3379
|
SDKError,
|
|
3609
3380
|
SERVER_COLLECTIONS,
|
|
3610
3381
|
SERVER_ONLY_COLLECTIONS,
|
|
3611
|
-
ServerClient,
|
|
3612
|
-
ServerCollectionClient,
|
|
3613
|
-
ServerCollectionQueryBuilder,
|
|
3614
|
-
ServerCommerceClient,
|
|
3615
3382
|
ServiceUnavailableError,
|
|
3616
3383
|
ShippingApi,
|
|
3617
3384
|
TimeoutError,
|
|
3618
3385
|
UsageLimitError,
|
|
3619
3386
|
ValidationError,
|
|
3387
|
+
buildProductHref,
|
|
3620
3388
|
buildProductListingGroupsByOption,
|
|
3621
3389
|
buildProductListingProjection,
|
|
3622
3390
|
buildProductOptionMatrix,
|
|
3623
|
-
|
|
3391
|
+
buildProductOptionMatrixFromDetail,
|
|
3624
3392
|
createAnalytics,
|
|
3625
3393
|
createAuthError,
|
|
3626
|
-
createClient,
|
|
3394
|
+
createClient2 as createClient,
|
|
3627
3395
|
createConflictError,
|
|
3628
3396
|
createCustomerAuthWebhookHandler,
|
|
3629
3397
|
createNotFoundError,
|
|
3630
3398
|
createPermissionError,
|
|
3399
|
+
createProductSelectionCodec,
|
|
3631
3400
|
createRateLimitError,
|
|
3632
|
-
createServerClient,
|
|
3633
3401
|
createTypedWebhookHandler,
|
|
3634
|
-
customerKeys,
|
|
3635
3402
|
formatOrderName,
|
|
3636
3403
|
generateOrderNumber,
|
|
3637
3404
|
getAvailableOptionValues,
|
|
@@ -3640,7 +3407,7 @@ export {
|
|
|
3640
3407
|
getImagePlaceholderStyle,
|
|
3641
3408
|
getImageSrcSet,
|
|
3642
3409
|
getImageUrl,
|
|
3643
|
-
|
|
3410
|
+
getProductSelectionImages,
|
|
3644
3411
|
getSelectedValueByOptionId,
|
|
3645
3412
|
getVideoGif,
|
|
3646
3413
|
getVideoMp4Url,
|
|
@@ -3664,9 +3431,14 @@ export {
|
|
|
3664
3431
|
isUsageLimitError,
|
|
3665
3432
|
isValidWebhookEvent,
|
|
3666
3433
|
isValidationError,
|
|
3434
|
+
normalizeProductSelection,
|
|
3435
|
+
normalizeProductSelectionFromMatrix,
|
|
3667
3436
|
normalizeSelectedValueIds,
|
|
3668
|
-
|
|
3437
|
+
parseProductSelection,
|
|
3438
|
+
resolveProductSelection,
|
|
3439
|
+
resolveProductSelectionFromMatrix,
|
|
3669
3440
|
resolveRelation,
|
|
3670
|
-
resolveVariantForSelection
|
|
3441
|
+
resolveVariantForSelection,
|
|
3442
|
+
stringifyProductSelection
|
|
3671
3443
|
};
|
|
3672
3444
|
//# sourceMappingURL=index.js.map
|