@01.software/sdk 0.32.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +253 -38
- package/dist/analytics/react.cjs.map +1 -1
- package/dist/analytics/react.js.map +1 -1
- package/dist/analytics.cjs.map +1 -1
- package/dist/analytics.js.map +1 -1
- package/dist/client.cjs +368 -24
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +7 -6
- package/dist/client.d.ts +7 -6
- package/dist/client.js +368 -24
- package/dist/client.js.map +1 -1
- package/dist/{collection-client-CORhppPb.d.cts → collection-client-CR2B8c1v.d.cts} +7 -3
- package/dist/{collection-client-DPGXnhoF.d.ts → collection-client-DkREjhQ9.d.ts} +7 -3
- package/dist/{const-DcY2_z9O.d.ts → const-BTvdrXtY.d.cts} +5 -5
- package/dist/{const-Brk2Ff0q.d.cts → const-CdqCauHQ.d.ts} +5 -5
- package/dist/index-CjA3U6X3.d.cts +186 -0
- package/dist/index-DK8_NXkh.d.ts +186 -0
- package/dist/index.cjs +1651 -260
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +74 -9
- package/dist/index.d.ts +74 -9
- package/dist/index.js +1651 -260
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-DVK1QCeU.d.cts → payload-types-C7tb7Xbs.d.cts} +2115 -1833
- package/dist/{payload-types-DVK1QCeU.d.ts → payload-types-C7tb7Xbs.d.ts} +2115 -1833
- package/dist/query.cjs +194 -35
- package/dist/query.cjs.map +1 -1
- package/dist/query.d.cts +45 -18
- package/dist/query.d.ts +45 -18
- package/dist/query.js +194 -35
- package/dist/query.js.map +1 -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.map +1 -1
- package/dist/{server-CrsPyqEc.d.cts → server-nXOezi4b.d.cts} +22 -6
- package/dist/{server-CrsPyqEc.d.ts → server-nXOezi4b.d.ts} +22 -6
- package/dist/server.cjs +474 -36
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +11 -179
- package/dist/server.d.ts +11 -179
- package/dist/server.js +474 -36
- package/dist/server.js.map +1 -1
- package/dist/{types-DUPC7Xn6.d.ts → types-1ylMrCuW.d.ts} +1 -1
- package/dist/{types-ByMrR_Z_.d.cts → types-Bx558PU6.d.cts} +1 -1
- package/dist/{types-CYMSBkJC.d.ts → types-Byo_Rty4.d.ts} +728 -75
- package/dist/{types-CAkWqIr6.d.cts → types-DDhtZI6E.d.cts} +728 -75
- package/dist/ui/canvas/server.cjs +231 -38
- package/dist/ui/canvas/server.cjs.map +1 -1
- package/dist/ui/canvas/server.d.cts +1 -1
- package/dist/ui/canvas/server.d.ts +1 -1
- package/dist/ui/canvas/server.js +221 -38
- package/dist/ui/canvas/server.js.map +1 -1
- package/dist/ui/canvas.cjs +320 -257
- package/dist/ui/canvas.cjs.map +1 -1
- package/dist/ui/canvas.d.cts +5 -19
- package/dist/ui/canvas.d.ts +5 -19
- package/dist/ui/canvas.js +323 -260
- 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.cjs +95 -0
- package/dist/webhook.cjs.map +1 -1
- package/dist/webhook.d.cts +20 -104
- package/dist/webhook.d.ts +20 -104
- package/dist/webhook.js +95 -0
- package/dist/webhook.js.map +1 -1
- package/package.json +4 -5
package/dist/index.js
CHANGED
|
@@ -253,9 +253,12 @@ function resolveApiUrl(apiUrl) {
|
|
|
253
253
|
|
|
254
254
|
// src/core/internal/utils/http.ts
|
|
255
255
|
var DEFAULT_TIMEOUT = 3e4;
|
|
256
|
+
var STOREFRONT_BROWSER_TIMEOUT = 15e3;
|
|
256
257
|
var DEFAULT_RETRYABLE_STATUSES = [408, 429, 500, 502, 503, 504];
|
|
257
258
|
var NON_RETRYABLE_STATUSES = [400, 401, 403, 404, 409, 422];
|
|
258
259
|
var SAFE_METHODS = ["GET", "HEAD", "OPTIONS"];
|
|
260
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
261
|
+
var STOREFRONT_BROWSER_MAX_RETRIES = 1;
|
|
259
262
|
function debugLog(debug, type, message, data) {
|
|
260
263
|
if (!debug) return;
|
|
261
264
|
const shouldLog = debug === true || type === "request" && debug.logRequests || type === "response" && debug.logResponses || type === "error" && debug.logErrors;
|
|
@@ -448,15 +451,18 @@ async function httpFetch(url, options) {
|
|
|
448
451
|
publishableKey,
|
|
449
452
|
secretKey,
|
|
450
453
|
customerToken,
|
|
451
|
-
timeout = DEFAULT_TIMEOUT,
|
|
454
|
+
timeout: timeoutOption = DEFAULT_TIMEOUT,
|
|
452
455
|
debug,
|
|
453
456
|
retry,
|
|
454
457
|
onUnauthorized,
|
|
455
458
|
...requestInit
|
|
456
459
|
} = options || {};
|
|
457
460
|
const baseUrl = resolveApiUrl(apiUrl);
|
|
461
|
+
const method = (requestInit.method || "GET").toUpperCase();
|
|
462
|
+
const isPublishableKeyBrowserGet = typeof window !== "undefined" && !secretKey && !customerToken && Boolean(publishableKey) && SAFE_METHODS.includes(method);
|
|
463
|
+
const timeout = timeoutOption === DEFAULT_TIMEOUT && isPublishableKeyBrowserGet ? STOREFRONT_BROWSER_TIMEOUT : timeoutOption;
|
|
458
464
|
const retryConfig = {
|
|
459
|
-
maxRetries: retry?.maxRetries ??
|
|
465
|
+
maxRetries: retry?.maxRetries ?? (isPublishableKeyBrowserGet ? STOREFRONT_BROWSER_MAX_RETRIES : DEFAULT_MAX_RETRIES),
|
|
460
466
|
retryableStatuses: retry?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES,
|
|
461
467
|
retryDelay: retry?.retryDelay ?? ((attempt) => Math.min(1e3 * 2 ** attempt, 1e4))
|
|
462
468
|
};
|
|
@@ -564,8 +570,8 @@ async function httpFetch(url, options) {
|
|
|
564
570
|
),
|
|
565
571
|
requestId
|
|
566
572
|
);
|
|
567
|
-
const
|
|
568
|
-
if (attempt < retryConfig.maxRetries && SAFE_METHODS.includes(
|
|
573
|
+
const method2 = (requestInit.method || "GET").toUpperCase();
|
|
574
|
+
if (attempt < retryConfig.maxRetries && SAFE_METHODS.includes(method2) && retryConfig.retryableStatuses.includes(response.status)) {
|
|
569
575
|
lastError = error;
|
|
570
576
|
const retryDelay = retryConfig.retryDelay(attempt);
|
|
571
577
|
debugLog(debug, "error", `Retrying in ${retryDelay}ms...`, error);
|
|
@@ -577,8 +583,8 @@ async function httpFetch(url, options) {
|
|
|
577
583
|
return response;
|
|
578
584
|
} catch (error) {
|
|
579
585
|
debugLog(debug, "error", url, error);
|
|
580
|
-
const
|
|
581
|
-
const isSafe = SAFE_METHODS.includes(
|
|
586
|
+
const method2 = (requestInit.method || "GET").toUpperCase();
|
|
587
|
+
const isSafe = SAFE_METHODS.includes(method2);
|
|
582
588
|
if (error instanceof Error && error.name === "AbortError") {
|
|
583
589
|
const timeoutError = createTimeoutError(
|
|
584
590
|
`Request timed out after ${timeout}ms.`,
|
|
@@ -637,6 +643,35 @@ async function httpFetch(url, options) {
|
|
|
637
643
|
throw lastError ?? new NetworkError("Request failed after retries");
|
|
638
644
|
}
|
|
639
645
|
|
|
646
|
+
// src/core/internal/utils/query-string.ts
|
|
647
|
+
function productDetailQuery(params) {
|
|
648
|
+
const search = new URLSearchParams();
|
|
649
|
+
if ("slug" in params) {
|
|
650
|
+
search.set("slug", params.slug);
|
|
651
|
+
} else {
|
|
652
|
+
search.set("id", params.id);
|
|
653
|
+
}
|
|
654
|
+
return `/api/products/detail?${search}`;
|
|
655
|
+
}
|
|
656
|
+
function productDetailCatalogQuery(params) {
|
|
657
|
+
const search = new URLSearchParams();
|
|
658
|
+
if ("slug" in params) {
|
|
659
|
+
search.set("slug", params.slug);
|
|
660
|
+
} else {
|
|
661
|
+
search.set("id", params.id);
|
|
662
|
+
}
|
|
663
|
+
return `/api/products/detail/catalog?${search}`;
|
|
664
|
+
}
|
|
665
|
+
function listingGroupsQuery(params) {
|
|
666
|
+
return `/api/products/listing-groups?ids=${params.productIds.map(encodeURIComponent).join(",")}`;
|
|
667
|
+
}
|
|
668
|
+
function listingGroupsCatalogQuery(params) {
|
|
669
|
+
return `/api/products/listing-groups/catalog?ids=${params.productIds.map(encodeURIComponent).join(",")}`;
|
|
670
|
+
}
|
|
671
|
+
function stockSnapshotQuery(params) {
|
|
672
|
+
return `/api/products/stock?variantIds=${params.variantIds.map(encodeURIComponent).join(",")}`;
|
|
673
|
+
}
|
|
674
|
+
|
|
640
675
|
// src/core/collection/http-client.ts
|
|
641
676
|
var HttpClient = class {
|
|
642
677
|
constructor(publishableKey, secretKey, getCustomerToken, onUnauthorized, onRequestId, apiUrl) {
|
|
@@ -955,6 +990,8 @@ async function parseApiResponse(response, endpoint) {
|
|
|
955
990
|
}
|
|
956
991
|
|
|
957
992
|
// src/core/community/community-client.ts
|
|
993
|
+
var DEFAULT_POST_LIST_SORT = "-lastActivityAt";
|
|
994
|
+
var DEFAULT_COMMENT_LIST_SORT = "-createdAt";
|
|
958
995
|
var CommunityClient = class {
|
|
959
996
|
constructor(options) {
|
|
960
997
|
this.publishableKey = requirePublishableKeyForSecret(
|
|
@@ -973,6 +1010,40 @@ var CommunityClient = class {
|
|
|
973
1010
|
const entries = Object.entries(params).filter((e) => e[1] !== void 0).map(([k, v]) => [k, String(v)]);
|
|
974
1011
|
return entries.length ? `?${new URLSearchParams(entries).toString()}` : "";
|
|
975
1012
|
}
|
|
1013
|
+
buildPostsListQuery(params) {
|
|
1014
|
+
const urlParams = new URLSearchParams();
|
|
1015
|
+
const sort = params?.sort ?? DEFAULT_POST_LIST_SORT;
|
|
1016
|
+
urlParams.set("sort", sort);
|
|
1017
|
+
if (params?.limit !== void 0) urlParams.set("limit", String(params.limit));
|
|
1018
|
+
if (params?.page !== void 0) urlParams.set("page", String(params.page));
|
|
1019
|
+
if (params?.categoryId !== void 0) {
|
|
1020
|
+
urlParams.set("where[categories][in]", params.categoryId);
|
|
1021
|
+
}
|
|
1022
|
+
if (params?.tagId !== void 0) {
|
|
1023
|
+
urlParams.set("where[tags][in]", params.tagId);
|
|
1024
|
+
}
|
|
1025
|
+
return `/api/posts?${urlParams.toString()}`;
|
|
1026
|
+
}
|
|
1027
|
+
buildCommentsListQuery(params) {
|
|
1028
|
+
const urlParams = new URLSearchParams();
|
|
1029
|
+
const sort = params.sort ?? DEFAULT_COMMENT_LIST_SORT;
|
|
1030
|
+
urlParams.set("sort", sort);
|
|
1031
|
+
if (params.postId !== void 0) {
|
|
1032
|
+
urlParams.set("where[post][equals]", params.postId);
|
|
1033
|
+
}
|
|
1034
|
+
if (params.parentId !== void 0) {
|
|
1035
|
+
urlParams.set("where[parent][equals]", params.parentId);
|
|
1036
|
+
}
|
|
1037
|
+
if (params.rootComment !== void 0) {
|
|
1038
|
+
urlParams.set("where[rootComment][equals]", params.rootComment);
|
|
1039
|
+
}
|
|
1040
|
+
if (params.topLevelOnly) {
|
|
1041
|
+
urlParams.set("where[parent][exists]", "false");
|
|
1042
|
+
}
|
|
1043
|
+
if (params.limit !== void 0) urlParams.set("limit", String(params.limit));
|
|
1044
|
+
if (params.page !== void 0) urlParams.set("page", String(params.page));
|
|
1045
|
+
return `/api/comments?${urlParams.toString()}`;
|
|
1046
|
+
}
|
|
976
1047
|
async execute(endpoint, method, body) {
|
|
977
1048
|
const token = typeof this.customerToken === "function" ? this.customerToken() : this.customerToken;
|
|
978
1049
|
try {
|
|
@@ -996,6 +1067,28 @@ var CommunityClient = class {
|
|
|
996
1067
|
createPost(params) {
|
|
997
1068
|
return this.execute("/api/posts", "POST", params);
|
|
998
1069
|
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Public post feed. Server applies the same visibility contract as
|
|
1072
|
+
* `communityPostRead` (published + visible + moderation-safe).
|
|
1073
|
+
*/
|
|
1074
|
+
listPosts(params) {
|
|
1075
|
+
return this.execute(
|
|
1076
|
+
this.buildPostsListQuery(params),
|
|
1077
|
+
"GET"
|
|
1078
|
+
);
|
|
1079
|
+
}
|
|
1080
|
+
listPostCategories(params) {
|
|
1081
|
+
return this.execute(
|
|
1082
|
+
`/api/post-categories${this.buildQuery(params)}`,
|
|
1083
|
+
"GET"
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
listPostTags(params) {
|
|
1087
|
+
return this.execute(
|
|
1088
|
+
`/api/post-tags${this.buildQuery(params)}`,
|
|
1089
|
+
"GET"
|
|
1090
|
+
);
|
|
1091
|
+
}
|
|
999
1092
|
getMyPosts(params) {
|
|
1000
1093
|
return this.execute(
|
|
1001
1094
|
`/api/posts/my${this.buildQuery(params)}`,
|
|
@@ -1031,16 +1124,37 @@ var CommunityClient = class {
|
|
|
1031
1124
|
}
|
|
1032
1125
|
return this.execute("/api/comments", "POST", body);
|
|
1033
1126
|
}
|
|
1127
|
+
/**
|
|
1128
|
+
* List comments for a post.
|
|
1129
|
+
*
|
|
1130
|
+
* - Default: all visible comments on the post (any depth).
|
|
1131
|
+
* - `topLevelOnly: true`: only root comments (`parent` unset).
|
|
1132
|
+
* - `rootComment`: comments belonging to a thread rooted at that comment.
|
|
1133
|
+
*/
|
|
1034
1134
|
listComments(params) {
|
|
1035
|
-
const { postId, page, limit, rootComment } = params;
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1135
|
+
const { postId, page, limit, rootComment, topLevelOnly, sort } = params;
|
|
1136
|
+
return this.execute(
|
|
1137
|
+
this.buildCommentsListQuery({
|
|
1138
|
+
postId,
|
|
1139
|
+
page,
|
|
1140
|
+
limit,
|
|
1141
|
+
rootComment,
|
|
1142
|
+
topLevelOnly,
|
|
1143
|
+
sort
|
|
1144
|
+
}),
|
|
1145
|
+
"GET"
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
/** Direct replies to a comment (`where[parent][equals]`). */
|
|
1149
|
+
listReplies(params) {
|
|
1150
|
+
const { commentId, page, limit, sort } = params;
|
|
1042
1151
|
return this.execute(
|
|
1043
|
-
|
|
1152
|
+
this.buildCommentsListQuery({
|
|
1153
|
+
parentId: commentId,
|
|
1154
|
+
page,
|
|
1155
|
+
limit,
|
|
1156
|
+
sort
|
|
1157
|
+
}),
|
|
1044
1158
|
"GET"
|
|
1045
1159
|
);
|
|
1046
1160
|
}
|
|
@@ -1068,10 +1182,18 @@ var CommunityClient = class {
|
|
|
1068
1182
|
}
|
|
1069
1183
|
// Reactions
|
|
1070
1184
|
addReaction(params) {
|
|
1071
|
-
const { postId, type } = params;
|
|
1185
|
+
const { postId, typeSlug, type } = params;
|
|
1186
|
+
const reactionType = typeSlug ?? type;
|
|
1187
|
+
if (!reactionType) {
|
|
1188
|
+
throw new SDKError(
|
|
1189
|
+
"validation_failed",
|
|
1190
|
+
"addReaction requires typeSlug (or deprecated type)",
|
|
1191
|
+
400
|
|
1192
|
+
);
|
|
1193
|
+
}
|
|
1072
1194
|
return this.execute("/api/reactions", "POST", {
|
|
1073
1195
|
post: postId,
|
|
1074
|
-
type
|
|
1196
|
+
type: reactionType
|
|
1075
1197
|
});
|
|
1076
1198
|
}
|
|
1077
1199
|
removeReaction(params) {
|
|
@@ -1082,10 +1204,18 @@ var CommunityClient = class {
|
|
|
1082
1204
|
);
|
|
1083
1205
|
}
|
|
1084
1206
|
addCommentReaction(params) {
|
|
1085
|
-
const { commentId, type } = params;
|
|
1207
|
+
const { commentId, typeSlug, type } = params;
|
|
1208
|
+
const reactionType = typeSlug ?? type;
|
|
1209
|
+
if (!reactionType) {
|
|
1210
|
+
throw new SDKError(
|
|
1211
|
+
"validation_failed",
|
|
1212
|
+
"addCommentReaction requires typeSlug (or deprecated type)",
|
|
1213
|
+
400
|
|
1214
|
+
);
|
|
1215
|
+
}
|
|
1086
1216
|
return this.execute("/api/reactions", "POST", {
|
|
1087
1217
|
comment: commentId,
|
|
1088
|
-
type
|
|
1218
|
+
type: reactionType
|
|
1089
1219
|
});
|
|
1090
1220
|
}
|
|
1091
1221
|
removeCommentReaction(params) {
|
|
@@ -1101,6 +1231,12 @@ var CommunityClient = class {
|
|
|
1101
1231
|
"GET"
|
|
1102
1232
|
);
|
|
1103
1233
|
}
|
|
1234
|
+
getCommentReactionSummary(params) {
|
|
1235
|
+
return this.execute(
|
|
1236
|
+
`/api/comments/${params.commentId}/reactions`,
|
|
1237
|
+
"GET"
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1104
1240
|
getReactionTypes() {
|
|
1105
1241
|
return this.execute(
|
|
1106
1242
|
"/api/reaction-types?limit=100",
|
|
@@ -1125,6 +1261,25 @@ var CommunityClient = class {
|
|
|
1125
1261
|
"GET"
|
|
1126
1262
|
);
|
|
1127
1263
|
}
|
|
1264
|
+
// Profiles
|
|
1265
|
+
listProfileLists(params) {
|
|
1266
|
+
return this.execute(
|
|
1267
|
+
`/api/customer-profile-lists${this.buildQuery(params)}`,
|
|
1268
|
+
"GET"
|
|
1269
|
+
);
|
|
1270
|
+
}
|
|
1271
|
+
async getProfileList(params) {
|
|
1272
|
+
const query = "slug" in params ? `?where[slug][equals]=${encodeURIComponent(params.slug)}&limit=1` : `?where[id][equals]=${encodeURIComponent(params.id)}&limit=1`;
|
|
1273
|
+
const res = await this.execute(`/api/customer-profile-lists${query}`, "GET");
|
|
1274
|
+
return res.docs[0] ?? null;
|
|
1275
|
+
}
|
|
1276
|
+
updatePublicProfile(body) {
|
|
1277
|
+
return this.execute(
|
|
1278
|
+
"/api/customers/me/profile",
|
|
1279
|
+
"PATCH",
|
|
1280
|
+
body
|
|
1281
|
+
);
|
|
1282
|
+
}
|
|
1128
1283
|
};
|
|
1129
1284
|
|
|
1130
1285
|
// src/core/customer/customer-auth.ts
|
|
@@ -1410,6 +1565,283 @@ var CartApi = class {
|
|
|
1410
1565
|
}
|
|
1411
1566
|
};
|
|
1412
1567
|
|
|
1568
|
+
// src/core/api/base-api.ts
|
|
1569
|
+
var BaseApi = class {
|
|
1570
|
+
constructor(apiName, options) {
|
|
1571
|
+
if (!options.secretKey) {
|
|
1572
|
+
throw createConfigError(`secretKey is required for ${apiName}.`);
|
|
1573
|
+
}
|
|
1574
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
1575
|
+
apiName,
|
|
1576
|
+
options.publishableKey,
|
|
1577
|
+
options.secretKey
|
|
1578
|
+
);
|
|
1579
|
+
this.secretKey = options.secretKey;
|
|
1580
|
+
this.apiUrl = options.apiUrl;
|
|
1581
|
+
this.onRequestId = options.onRequestId;
|
|
1582
|
+
}
|
|
1583
|
+
async request(endpoint, body, options) {
|
|
1584
|
+
const method = options?.method ?? "POST";
|
|
1585
|
+
try {
|
|
1586
|
+
const response = await httpFetch(endpoint, {
|
|
1587
|
+
method,
|
|
1588
|
+
apiUrl: this.apiUrl,
|
|
1589
|
+
publishableKey: this.publishableKey,
|
|
1590
|
+
secretKey: this.secretKey,
|
|
1591
|
+
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1592
|
+
...options?.headers && { headers: options.headers }
|
|
1593
|
+
});
|
|
1594
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1595
|
+
return parseApiResponse(response, endpoint);
|
|
1596
|
+
} catch (err) {
|
|
1597
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1598
|
+
this.onRequestId?.(id);
|
|
1599
|
+
throw err;
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
};
|
|
1603
|
+
|
|
1604
|
+
// src/core/api/product-api.ts
|
|
1605
|
+
var PRODUCT_DETAIL_UNAVAILABLE_REASONS = /* @__PURE__ */ new Set([
|
|
1606
|
+
"not_found",
|
|
1607
|
+
"not_published",
|
|
1608
|
+
"feature_disabled"
|
|
1609
|
+
]);
|
|
1610
|
+
function isRecord(value) {
|
|
1611
|
+
return typeof value === "object" && value !== null;
|
|
1612
|
+
}
|
|
1613
|
+
function readProductDetailUnavailableReason(value) {
|
|
1614
|
+
if (!isRecord(value)) return void 0;
|
|
1615
|
+
const directReason = value.reason ?? value.code;
|
|
1616
|
+
if (typeof directReason === "string" && PRODUCT_DETAIL_UNAVAILABLE_REASONS.has(directReason)) {
|
|
1617
|
+
return directReason;
|
|
1618
|
+
}
|
|
1619
|
+
return readProductDetailUnavailableReason(value.body);
|
|
1620
|
+
}
|
|
1621
|
+
function productDetailResultFromError(error) {
|
|
1622
|
+
if (!(error instanceof SDKError) || error.status !== 404) return void 0;
|
|
1623
|
+
const reason = readProductDetailUnavailableReason(error.details);
|
|
1624
|
+
if (!reason) return void 0;
|
|
1625
|
+
return { found: false, reason };
|
|
1626
|
+
}
|
|
1627
|
+
function rejectLegacyOptionValueSwatchColor(value) {
|
|
1628
|
+
if (Object.prototype.hasOwnProperty.call(
|
|
1629
|
+
value,
|
|
1630
|
+
"swatchColor"
|
|
1631
|
+
)) {
|
|
1632
|
+
throw new TypeError(
|
|
1633
|
+
'Product upsert option values no longer accept legacy flat "swatchColor"; use nested "swatch" instead.'
|
|
1634
|
+
);
|
|
1635
|
+
}
|
|
1636
|
+
}
|
|
1637
|
+
function normalizeProductUpsertOptionValue(value) {
|
|
1638
|
+
rejectLegacyOptionValueSwatchColor(value);
|
|
1639
|
+
return value;
|
|
1640
|
+
}
|
|
1641
|
+
function normalizeProductUpsertParams(params) {
|
|
1642
|
+
const options = params.options ?? [];
|
|
1643
|
+
const variants = params.variants ?? [];
|
|
1644
|
+
if (!options.length) {
|
|
1645
|
+
return { ...params, options, variants };
|
|
1646
|
+
}
|
|
1647
|
+
return {
|
|
1648
|
+
...params,
|
|
1649
|
+
options: options.map((option) => ({
|
|
1650
|
+
...option,
|
|
1651
|
+
values: option.values.map(
|
|
1652
|
+
(value) => normalizeProductUpsertOptionValue(value)
|
|
1653
|
+
)
|
|
1654
|
+
})),
|
|
1655
|
+
variants
|
|
1656
|
+
};
|
|
1657
|
+
}
|
|
1658
|
+
var PRODUCT_UPSERT_UNKNOWN_FIELD_REASON = "unknown_field";
|
|
1659
|
+
var PRODUCT_UPSERT_READONLY_FIELD_REASON = "product_field_readonly";
|
|
1660
|
+
function isProductUpsertFieldValidationErrorBody(value) {
|
|
1661
|
+
if (typeof value !== "object" || value === null) return false;
|
|
1662
|
+
const body = value;
|
|
1663
|
+
const reason = body.reason;
|
|
1664
|
+
return typeof body.error === "string" && typeof body.field === "string" && (reason === PRODUCT_UPSERT_UNKNOWN_FIELD_REASON || reason === PRODUCT_UPSERT_READONLY_FIELD_REASON);
|
|
1665
|
+
}
|
|
1666
|
+
var ProductApi = class extends BaseApi {
|
|
1667
|
+
constructor(options) {
|
|
1668
|
+
super("ProductApi", options);
|
|
1669
|
+
}
|
|
1670
|
+
/**
|
|
1671
|
+
* Check point-in-time stock availability for one or more product variants.
|
|
1672
|
+
* Results reflect available stock at the moment of the call and are not guaranteed
|
|
1673
|
+
* to remain available by the time an order is placed.
|
|
1674
|
+
*/
|
|
1675
|
+
stockCheck(params) {
|
|
1676
|
+
return this.request("/api/products/stock-check", params);
|
|
1677
|
+
}
|
|
1678
|
+
stockSnapshot(params) {
|
|
1679
|
+
return this.request(
|
|
1680
|
+
stockSnapshotQuery(params),
|
|
1681
|
+
void 0,
|
|
1682
|
+
{ method: "GET" }
|
|
1683
|
+
);
|
|
1684
|
+
}
|
|
1685
|
+
listingGroups(params) {
|
|
1686
|
+
return this.request(
|
|
1687
|
+
listingGroupsQuery(params),
|
|
1688
|
+
void 0,
|
|
1689
|
+
{ method: "GET" }
|
|
1690
|
+
);
|
|
1691
|
+
}
|
|
1692
|
+
listingGroupsCatalog(params) {
|
|
1693
|
+
return this.request(
|
|
1694
|
+
listingGroupsCatalogQuery(params),
|
|
1695
|
+
void 0,
|
|
1696
|
+
{ method: "GET" }
|
|
1697
|
+
);
|
|
1698
|
+
}
|
|
1699
|
+
/**
|
|
1700
|
+
* Fetch full product detail by slug or id.
|
|
1701
|
+
* Returns a discriminated result so storefronts can distinguish missing,
|
|
1702
|
+
* unpublished, and feature-disabled products.
|
|
1703
|
+
*
|
|
1704
|
+
* Only product-detail 404 responses carrying one of those allowlisted reasons
|
|
1705
|
+
* are mapped to `{ found: false, reason }`. Unknown or uncoded 404s, plus
|
|
1706
|
+
* permission/auth errors such as tenant mismatch, continue to throw typed SDK
|
|
1707
|
+
* errors instead of being collapsed into a storefront absence result.
|
|
1708
|
+
*/
|
|
1709
|
+
async detail(params) {
|
|
1710
|
+
try {
|
|
1711
|
+
const product = await this.request(
|
|
1712
|
+
productDetailQuery(params),
|
|
1713
|
+
void 0,
|
|
1714
|
+
{ method: "GET" }
|
|
1715
|
+
);
|
|
1716
|
+
return { found: true, product };
|
|
1717
|
+
} catch (err) {
|
|
1718
|
+
const notFoundResult = productDetailResultFromError(err);
|
|
1719
|
+
if (notFoundResult) return notFoundResult;
|
|
1720
|
+
throw err;
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
async detailCatalog(params) {
|
|
1724
|
+
try {
|
|
1725
|
+
const product = await this.request(
|
|
1726
|
+
productDetailCatalogQuery(params),
|
|
1727
|
+
void 0,
|
|
1728
|
+
{ method: "GET" }
|
|
1729
|
+
);
|
|
1730
|
+
return { found: true, product };
|
|
1731
|
+
} catch (err) {
|
|
1732
|
+
const notFoundResult = productDetailResultFromError(err);
|
|
1733
|
+
if (notFoundResult?.found === false) return notFoundResult;
|
|
1734
|
+
throw err;
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
/**
|
|
1738
|
+
* Atomically create or update a product together with its options,
|
|
1739
|
+
* option-values, and variants in a single transaction. Mirrors Shopify's
|
|
1740
|
+
* `productSet` shape and is the canonical write path for the MCP
|
|
1741
|
+
* `product-upsert` tool.
|
|
1742
|
+
*/
|
|
1743
|
+
upsert(params) {
|
|
1744
|
+
return this.request(
|
|
1745
|
+
"/api/products/upsert",
|
|
1746
|
+
normalizeProductUpsertParams(params)
|
|
1747
|
+
);
|
|
1748
|
+
}
|
|
1749
|
+
};
|
|
1750
|
+
|
|
1751
|
+
// src/core/api/order-api.ts
|
|
1752
|
+
function idempotencyRequestOptions(idempotencyKey) {
|
|
1753
|
+
return idempotencyKey ? { headers: { "X-Idempotency-Key": idempotencyKey } } : void 0;
|
|
1754
|
+
}
|
|
1755
|
+
function splitIdempotencyKey(params) {
|
|
1756
|
+
const { idempotencyKey, ...body } = params;
|
|
1757
|
+
return { body, idempotencyKey };
|
|
1758
|
+
}
|
|
1759
|
+
var OrderApi = class extends BaseApi {
|
|
1760
|
+
constructor(options) {
|
|
1761
|
+
super("OrderApi", options);
|
|
1762
|
+
}
|
|
1763
|
+
createOrder(params) {
|
|
1764
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1765
|
+
return this.request(
|
|
1766
|
+
"/api/orders/create",
|
|
1767
|
+
body,
|
|
1768
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1769
|
+
);
|
|
1770
|
+
}
|
|
1771
|
+
updateOrder(params) {
|
|
1772
|
+
return this.request("/api/orders/update", params);
|
|
1773
|
+
}
|
|
1774
|
+
updateTransaction(params) {
|
|
1775
|
+
return this.request("/api/transactions/update", params);
|
|
1776
|
+
}
|
|
1777
|
+
confirmPayment(params) {
|
|
1778
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1779
|
+
const headerKey = idempotencyKey ?? params.providerEventId;
|
|
1780
|
+
return this.request(
|
|
1781
|
+
"/api/orders/confirm-payment",
|
|
1782
|
+
body,
|
|
1783
|
+
idempotencyRequestOptions(headerKey)
|
|
1784
|
+
);
|
|
1785
|
+
}
|
|
1786
|
+
cancelOrder(params) {
|
|
1787
|
+
const { idempotencyKey } = params;
|
|
1788
|
+
const body = {
|
|
1789
|
+
orderNumber: params.orderNumber,
|
|
1790
|
+
reasonCode: params.reasonCode,
|
|
1791
|
+
reasonDetail: params.reasonDetail
|
|
1792
|
+
};
|
|
1793
|
+
return this.request(
|
|
1794
|
+
"/api/orders/cancel",
|
|
1795
|
+
body,
|
|
1796
|
+
idempotencyKey ? { headers: { "X-Idempotency-Key": idempotencyKey } } : void 0
|
|
1797
|
+
);
|
|
1798
|
+
}
|
|
1799
|
+
checkout(params) {
|
|
1800
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1801
|
+
return this.request(
|
|
1802
|
+
"/api/orders/checkout",
|
|
1803
|
+
body,
|
|
1804
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1805
|
+
);
|
|
1806
|
+
}
|
|
1807
|
+
createFulfillment(params) {
|
|
1808
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1809
|
+
return this.request(
|
|
1810
|
+
"/api/orders/create-fulfillment",
|
|
1811
|
+
body,
|
|
1812
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1813
|
+
);
|
|
1814
|
+
}
|
|
1815
|
+
updateFulfillment(params) {
|
|
1816
|
+
return this.request("/api/orders/update-fulfillment", params);
|
|
1817
|
+
}
|
|
1818
|
+
bulkImportFulfillments(params) {
|
|
1819
|
+
return this.request(
|
|
1820
|
+
"/api/fulfillments/bulk-import",
|
|
1821
|
+
params
|
|
1822
|
+
);
|
|
1823
|
+
}
|
|
1824
|
+
returnWithRefund(params) {
|
|
1825
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1826
|
+
return this.request(
|
|
1827
|
+
"/api/returns/return-refund",
|
|
1828
|
+
body,
|
|
1829
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
createReturn(params) {
|
|
1833
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1834
|
+
return this.request(
|
|
1835
|
+
"/api/returns/create",
|
|
1836
|
+
body,
|
|
1837
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1838
|
+
);
|
|
1839
|
+
}
|
|
1840
|
+
updateReturn(params) {
|
|
1841
|
+
return this.request("/api/returns/update", params);
|
|
1842
|
+
}
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1413
1845
|
// src/core/commerce/commerce-client.ts
|
|
1414
1846
|
var CommerceClient = class {
|
|
1415
1847
|
constructor(options) {
|
|
@@ -1420,7 +1852,7 @@ var CommerceClient = class {
|
|
|
1420
1852
|
onUnauthorized: options.onUnauthorized,
|
|
1421
1853
|
onRequestId: options.onRequestId
|
|
1422
1854
|
});
|
|
1423
|
-
const execute = async (endpoint, body) => {
|
|
1855
|
+
const execute = async (endpoint, body, requestOptions) => {
|
|
1424
1856
|
const token = options.customerToken();
|
|
1425
1857
|
try {
|
|
1426
1858
|
const response = await httpFetch(endpoint, {
|
|
@@ -1429,7 +1861,26 @@ var CommerceClient = class {
|
|
|
1429
1861
|
publishableKey: options.publishableKey,
|
|
1430
1862
|
customerToken: token ?? void 0,
|
|
1431
1863
|
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized },
|
|
1432
|
-
body: JSON.stringify(body)
|
|
1864
|
+
body: JSON.stringify(body),
|
|
1865
|
+
...requestOptions?.headers && { headers: requestOptions.headers }
|
|
1866
|
+
});
|
|
1867
|
+
options.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1868
|
+
return parseApiResponse(response, endpoint);
|
|
1869
|
+
} catch (err) {
|
|
1870
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1871
|
+
options.onRequestId?.(id);
|
|
1872
|
+
throw err;
|
|
1873
|
+
}
|
|
1874
|
+
};
|
|
1875
|
+
const executeGet = async (endpoint) => {
|
|
1876
|
+
const token = options.customerToken();
|
|
1877
|
+
try {
|
|
1878
|
+
const response = await httpFetch(endpoint, {
|
|
1879
|
+
method: "GET",
|
|
1880
|
+
apiUrl: options.apiUrl,
|
|
1881
|
+
publishableKey: options.publishableKey,
|
|
1882
|
+
customerToken: token ?? void 0,
|
|
1883
|
+
...token && options.onUnauthorized && { onUnauthorized: options.onUnauthorized }
|
|
1433
1884
|
});
|
|
1434
1885
|
options.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1435
1886
|
return parseApiResponse(response, endpoint);
|
|
@@ -1441,12 +1892,28 @@ var CommerceClient = class {
|
|
|
1441
1892
|
};
|
|
1442
1893
|
this.product = {
|
|
1443
1894
|
stockCheck: (params) => execute("/api/products/stock-check", params),
|
|
1444
|
-
|
|
1895
|
+
stockSnapshot: (params) => executeGet(stockSnapshotQuery(params)),
|
|
1896
|
+
listingGroups: (params) => executeGet(listingGroupsQuery(params)),
|
|
1897
|
+
listingGroupsCatalog: (params) => executeGet(listingGroupsCatalogQuery(params)),
|
|
1445
1898
|
detail: async (params) => {
|
|
1446
1899
|
try {
|
|
1447
|
-
|
|
1900
|
+
const product = await executeGet(productDetailQuery(params));
|
|
1901
|
+
return { found: true, product };
|
|
1902
|
+
} catch (err) {
|
|
1903
|
+
const notFoundResult = productDetailResultFromError(err);
|
|
1904
|
+
if (notFoundResult) return notFoundResult;
|
|
1905
|
+
throw err;
|
|
1906
|
+
}
|
|
1907
|
+
},
|
|
1908
|
+
detailCatalog: async (params) => {
|
|
1909
|
+
try {
|
|
1910
|
+
const product = await executeGet(
|
|
1911
|
+
productDetailCatalogQuery(params)
|
|
1912
|
+
);
|
|
1913
|
+
return { found: true, product };
|
|
1448
1914
|
} catch (err) {
|
|
1449
|
-
|
|
1915
|
+
const notFoundResult = productDetailResultFromError(err);
|
|
1916
|
+
if (notFoundResult?.found === false) return notFoundResult;
|
|
1450
1917
|
throw err;
|
|
1451
1918
|
}
|
|
1452
1919
|
}
|
|
@@ -1461,7 +1928,14 @@ var CommerceClient = class {
|
|
|
1461
1928
|
clear: cartApi.clearCart.bind(cartApi)
|
|
1462
1929
|
};
|
|
1463
1930
|
this.orders = {
|
|
1464
|
-
checkout: (params) =>
|
|
1931
|
+
checkout: (params) => {
|
|
1932
|
+
const { body, idempotencyKey } = splitIdempotencyKey(params);
|
|
1933
|
+
return execute(
|
|
1934
|
+
"/api/orders/checkout",
|
|
1935
|
+
body,
|
|
1936
|
+
idempotencyRequestOptions(idempotencyKey)
|
|
1937
|
+
);
|
|
1938
|
+
},
|
|
1465
1939
|
listMine: (params) => options.customerAuth.getMyOrders(params)
|
|
1466
1940
|
};
|
|
1467
1941
|
this.discounts = {
|
|
@@ -1473,15 +1947,122 @@ var CommerceClient = class {
|
|
|
1473
1947
|
}
|
|
1474
1948
|
};
|
|
1475
1949
|
|
|
1476
|
-
// src/core/
|
|
1477
|
-
var
|
|
1950
|
+
// src/core/events/events-client.ts
|
|
1951
|
+
var EventsClient = class {
|
|
1478
1952
|
constructor(options) {
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1953
|
+
const secretKey = options.secretKey;
|
|
1954
|
+
this.publishableKey = requirePublishableKeyForSecret(
|
|
1955
|
+
"EventsClient",
|
|
1956
|
+
options.publishableKey,
|
|
1957
|
+
secretKey
|
|
1958
|
+
);
|
|
1959
|
+
this.apiUrl = options.apiUrl;
|
|
1960
|
+
this.customerToken = options.customerToken;
|
|
1961
|
+
this.onUnauthorized = options.onUnauthorized;
|
|
1962
|
+
this.onRequestId = options.onRequestId;
|
|
1963
|
+
}
|
|
1964
|
+
getRange(params) {
|
|
1965
|
+
return this.execute(
|
|
1966
|
+
buildRangeEndpoint(params),
|
|
1967
|
+
"GET",
|
|
1968
|
+
void 0,
|
|
1969
|
+
{ useCustomerAuth: false }
|
|
1970
|
+
);
|
|
1971
|
+
}
|
|
1972
|
+
register(params) {
|
|
1973
|
+
return this.execute(
|
|
1974
|
+
"/api/event-registrations/register",
|
|
1975
|
+
"POST",
|
|
1976
|
+
buildRegistrationRequestBody(params),
|
|
1977
|
+
{ useCustomerAuth: true }
|
|
1978
|
+
);
|
|
1979
|
+
}
|
|
1980
|
+
getGuestRegistration(token) {
|
|
1981
|
+
return this.execute(
|
|
1982
|
+
"/api/event-registrations/guest/lookup",
|
|
1983
|
+
"POST",
|
|
1984
|
+
{ token },
|
|
1985
|
+
{ useCustomerAuth: false }
|
|
1986
|
+
);
|
|
1987
|
+
}
|
|
1988
|
+
cancelGuestRegistration(token, params = {}) {
|
|
1989
|
+
return this.execute(
|
|
1990
|
+
"/api/event-registrations/guest/cancel",
|
|
1991
|
+
"POST",
|
|
1992
|
+
buildGuestCancelRequestBody(token, params),
|
|
1993
|
+
{ useCustomerAuth: false }
|
|
1994
|
+
);
|
|
1995
|
+
}
|
|
1996
|
+
async execute(endpoint, method, body, options = {}) {
|
|
1997
|
+
const useCustomerAuth = options.useCustomerAuth === true;
|
|
1998
|
+
const token = useCustomerAuth ? typeof this.customerToken === "function" ? this.customerToken() : this.customerToken : void 0;
|
|
1999
|
+
try {
|
|
2000
|
+
const response = await httpFetch(endpoint, {
|
|
2001
|
+
method,
|
|
2002
|
+
apiUrl: this.apiUrl,
|
|
2003
|
+
publishableKey: this.publishableKey,
|
|
2004
|
+
...useCustomerAuth && token ? { customerToken: token } : {},
|
|
2005
|
+
...useCustomerAuth && token && this.onUnauthorized && { onUnauthorized: this.onUnauthorized },
|
|
2006
|
+
...body !== void 0 && { body: JSON.stringify(body) }
|
|
2007
|
+
});
|
|
2008
|
+
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
2009
|
+
return parseApiResponse(response, endpoint);
|
|
2010
|
+
} catch (err) {
|
|
2011
|
+
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
2012
|
+
this.onRequestId?.(id);
|
|
2013
|
+
throw err;
|
|
2014
|
+
}
|
|
2015
|
+
}
|
|
2016
|
+
};
|
|
2017
|
+
function buildRegistrationRequestBody(params) {
|
|
2018
|
+
return {
|
|
2019
|
+
event: params.event,
|
|
2020
|
+
occurrence: params.occurrence,
|
|
2021
|
+
...params.quantity !== void 0 && { quantity: params.quantity },
|
|
2022
|
+
...params.attendee !== void 0 && { attendee: params.attendee },
|
|
2023
|
+
...params.answers !== void 0 && { answers: params.answers }
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
2026
|
+
function buildGuestCancelRequestBody(token, params) {
|
|
2027
|
+
return {
|
|
2028
|
+
token,
|
|
2029
|
+
...params.reason !== void 0 && { reason: params.reason }
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
function buildRangeEndpoint(params) {
|
|
2033
|
+
const urlParams = new URLSearchParams();
|
|
2034
|
+
urlParams.set("start", formatDateParam(params.start));
|
|
2035
|
+
urlParams.set("end", formatDateParam(params.end));
|
|
2036
|
+
if (params.limit !== void 0) urlParams.set("limit", String(params.limit));
|
|
2037
|
+
if (params.page !== void 0) urlParams.set("page", String(params.page));
|
|
2038
|
+
appendValues(urlParams, "calendar", params.calendar);
|
|
2039
|
+
appendValues(urlParams, "calendarSlug", params.calendarSlug);
|
|
2040
|
+
appendValues(urlParams, "category", params.category);
|
|
2041
|
+
appendValues(urlParams, "categorySlug", params.categorySlug);
|
|
2042
|
+
appendValues(urlParams, "tag", params.tag);
|
|
2043
|
+
appendValues(urlParams, "tagSlug", params.tagSlug);
|
|
2044
|
+
return `/api/event-occurrences/range?${urlParams.toString()}`;
|
|
2045
|
+
}
|
|
2046
|
+
function formatDateParam(value) {
|
|
2047
|
+
return value instanceof Date ? value.toISOString() : value;
|
|
2048
|
+
}
|
|
2049
|
+
function appendValues(params, key, value) {
|
|
2050
|
+
if (value === void 0) return;
|
|
2051
|
+
const values = Array.isArray(value) ? value : [value];
|
|
2052
|
+
for (const entry of values) {
|
|
2053
|
+
if (entry) params.append(key, entry);
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
// src/core/client/client.ts
|
|
2058
|
+
var Client = class {
|
|
2059
|
+
constructor(options) {
|
|
2060
|
+
this.lastRequestId = null;
|
|
2061
|
+
const publishableKey = options.publishableKey;
|
|
2062
|
+
if (!publishableKey) {
|
|
2063
|
+
throw createConfigError("publishableKey is required.");
|
|
2064
|
+
}
|
|
2065
|
+
this.config = { ...options, publishableKey };
|
|
1485
2066
|
const metadata = {
|
|
1486
2067
|
timestamp: Date.now(),
|
|
1487
2068
|
userAgent: typeof window !== "undefined" ? window.navigator?.userAgent : "Node.js"
|
|
@@ -1518,6 +2099,13 @@ var Client = class {
|
|
|
1518
2099
|
onUnauthorized,
|
|
1519
2100
|
onRequestId
|
|
1520
2101
|
});
|
|
2102
|
+
this.events = new EventsClient({
|
|
2103
|
+
publishableKey: this.config.publishableKey,
|
|
2104
|
+
apiUrl: this.config.apiUrl,
|
|
2105
|
+
customerToken: () => this.customer.auth.getToken(),
|
|
2106
|
+
onUnauthorized,
|
|
2107
|
+
onRequestId
|
|
2108
|
+
});
|
|
1521
2109
|
this.collections = new ReadOnlyCollectionClient(
|
|
1522
2110
|
this.config.publishableKey,
|
|
1523
2111
|
void 0,
|
|
@@ -1538,92 +2126,6 @@ function createClient(options) {
|
|
|
1538
2126
|
return new Client(options);
|
|
1539
2127
|
}
|
|
1540
2128
|
|
|
1541
|
-
// src/core/api/base-api.ts
|
|
1542
|
-
var BaseApi = class {
|
|
1543
|
-
constructor(apiName, options) {
|
|
1544
|
-
if (!options.secretKey) {
|
|
1545
|
-
throw createConfigError(`secretKey is required for ${apiName}.`);
|
|
1546
|
-
}
|
|
1547
|
-
this.publishableKey = requirePublishableKeyForSecret(
|
|
1548
|
-
apiName,
|
|
1549
|
-
options.publishableKey,
|
|
1550
|
-
options.secretKey
|
|
1551
|
-
);
|
|
1552
|
-
this.secretKey = options.secretKey;
|
|
1553
|
-
this.apiUrl = options.apiUrl;
|
|
1554
|
-
this.onRequestId = options.onRequestId;
|
|
1555
|
-
}
|
|
1556
|
-
async request(endpoint, body, options) {
|
|
1557
|
-
const method = options?.method ?? "POST";
|
|
1558
|
-
try {
|
|
1559
|
-
const response = await httpFetch(endpoint, {
|
|
1560
|
-
method,
|
|
1561
|
-
apiUrl: this.apiUrl,
|
|
1562
|
-
publishableKey: this.publishableKey,
|
|
1563
|
-
secretKey: this.secretKey,
|
|
1564
|
-
...body !== void 0 && { body: JSON.stringify(body) },
|
|
1565
|
-
...options?.headers && { headers: options.headers }
|
|
1566
|
-
});
|
|
1567
|
-
this.onRequestId?.(response.headers.get("x-request-id") ?? null);
|
|
1568
|
-
return parseApiResponse(response, endpoint);
|
|
1569
|
-
} catch (err) {
|
|
1570
|
-
const id = err instanceof SDKError ? err.requestId ?? null : null;
|
|
1571
|
-
this.onRequestId?.(id);
|
|
1572
|
-
throw err;
|
|
1573
|
-
}
|
|
1574
|
-
}
|
|
1575
|
-
};
|
|
1576
|
-
|
|
1577
|
-
// src/core/api/order-api.ts
|
|
1578
|
-
var OrderApi = class extends BaseApi {
|
|
1579
|
-
constructor(options) {
|
|
1580
|
-
super("OrderApi", options);
|
|
1581
|
-
}
|
|
1582
|
-
createOrder(params) {
|
|
1583
|
-
return this.request("/api/orders/create", params);
|
|
1584
|
-
}
|
|
1585
|
-
updateOrder(params) {
|
|
1586
|
-
return this.request("/api/orders/update", params);
|
|
1587
|
-
}
|
|
1588
|
-
updateTransaction(params) {
|
|
1589
|
-
return this.request("/api/transactions/update", params);
|
|
1590
|
-
}
|
|
1591
|
-
confirmPayment(params) {
|
|
1592
|
-
return this.request(
|
|
1593
|
-
"/api/orders/confirm-payment",
|
|
1594
|
-
params,
|
|
1595
|
-
params.providerEventId ? { headers: { "X-Idempotency-Key": params.providerEventId } } : void 0
|
|
1596
|
-
);
|
|
1597
|
-
}
|
|
1598
|
-
checkout(params) {
|
|
1599
|
-
return this.request("/api/orders/checkout", params);
|
|
1600
|
-
}
|
|
1601
|
-
createFulfillment(params) {
|
|
1602
|
-
return this.request("/api/orders/create-fulfillment", params);
|
|
1603
|
-
}
|
|
1604
|
-
updateFulfillment(params) {
|
|
1605
|
-
return this.request("/api/orders/update-fulfillment", params);
|
|
1606
|
-
}
|
|
1607
|
-
bulkImportFulfillments(params) {
|
|
1608
|
-
return this.request(
|
|
1609
|
-
"/api/orders/bulk-import-fulfillments",
|
|
1610
|
-
params
|
|
1611
|
-
);
|
|
1612
|
-
}
|
|
1613
|
-
returnWithRefund(params) {
|
|
1614
|
-
return this.request(
|
|
1615
|
-
"/api/returns/return-refund",
|
|
1616
|
-
params
|
|
1617
|
-
);
|
|
1618
|
-
}
|
|
1619
|
-
createReturn(params) {
|
|
1620
|
-
return this.request("/api/returns/create", params);
|
|
1621
|
-
}
|
|
1622
|
-
updateReturn(params) {
|
|
1623
|
-
return this.request("/api/returns/update", params);
|
|
1624
|
-
}
|
|
1625
|
-
};
|
|
1626
|
-
|
|
1627
2129
|
// src/core/api/discount-api.ts
|
|
1628
2130
|
var DiscountApi = class extends BaseApi {
|
|
1629
2131
|
constructor(options) {
|
|
@@ -1644,62 +2146,66 @@ var ShippingApi = class extends BaseApi {
|
|
|
1644
2146
|
}
|
|
1645
2147
|
};
|
|
1646
2148
|
|
|
1647
|
-
// src/core/
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
return this.request("/api/products/stock-check", params);
|
|
1659
|
-
}
|
|
1660
|
-
listingGroups(params) {
|
|
1661
|
-
return this.request(
|
|
1662
|
-
"/api/products/listing-groups",
|
|
1663
|
-
params
|
|
1664
|
-
);
|
|
1665
|
-
}
|
|
1666
|
-
/**
|
|
1667
|
-
* Fetch full product detail by slug or id.
|
|
1668
|
-
* Returns `null` on 404 regardless of reason (`not_found` / `not_published` /
|
|
1669
|
-
* `feature_disabled`). For the reason behind a null,
|
|
1670
|
-
* inspect `client.lastRequestId` against backend logs.
|
|
1671
|
-
*/
|
|
1672
|
-
async detail(params) {
|
|
1673
|
-
try {
|
|
1674
|
-
return await this.request("/api/products/detail", params);
|
|
1675
|
-
} catch (err) {
|
|
1676
|
-
if (err instanceof NotFoundError) return null;
|
|
1677
|
-
throw err;
|
|
2149
|
+
// src/core/commerce/merge-catalog-stock.ts
|
|
2150
|
+
function mergeProductDetailWithStock(catalog, snapshot) {
|
|
2151
|
+
const snapshotByVariantId = new Map(
|
|
2152
|
+
snapshot.snapshots.map((item) => [item.variantId, item])
|
|
2153
|
+
);
|
|
2154
|
+
let missingSnapshotCount = 0;
|
|
2155
|
+
const variants = catalog.variants.map((variant) => {
|
|
2156
|
+
const snap = snapshotByVariantId.get(String(variant.id));
|
|
2157
|
+
if (!snap) {
|
|
2158
|
+
missingSnapshotCount += 1;
|
|
2159
|
+
return { ...variant, stock: 0, reservedStock: 0 };
|
|
1678
2160
|
}
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
2161
|
+
return {
|
|
2162
|
+
...variant,
|
|
2163
|
+
stock: snap.isUnlimited ? 0 : snap.availableStock,
|
|
2164
|
+
reservedStock: 0
|
|
2165
|
+
};
|
|
2166
|
+
});
|
|
2167
|
+
const trackedVariants = variants.filter((variant) => !variant.isUnlimited);
|
|
2168
|
+
const totalInventory = trackedVariants.length === 0 ? null : trackedVariants.reduce(
|
|
2169
|
+
(sum, variant) => sum + Math.max(0, (variant.stock ?? 0) - (variant.reservedStock ?? 0)),
|
|
2170
|
+
0
|
|
2171
|
+
);
|
|
2172
|
+
const { liveStockRequired: _liveStockRequired, ...listingBase } = catalog.listing;
|
|
2173
|
+
const availableForSale = snapshot.snapshots.some(
|
|
2174
|
+
(item) => item.status === "available" && item.availableForSale
|
|
2175
|
+
);
|
|
2176
|
+
return {
|
|
2177
|
+
stockMergeStatus: missingSnapshotCount === 0 ? "complete" : "partial",
|
|
2178
|
+
product: {
|
|
2179
|
+
...catalog,
|
|
2180
|
+
product: { ...catalog.product, totalInventory },
|
|
2181
|
+
variants,
|
|
2182
|
+
listing: { ...listingBase, availableForSale }
|
|
2183
|
+
}
|
|
2184
|
+
};
|
|
2185
|
+
}
|
|
1690
2186
|
|
|
1691
2187
|
// src/core/webhook/index.ts
|
|
1692
2188
|
var ORDER_CHANGED_EVENT_TYPE = "collection.orderChanged";
|
|
2189
|
+
var COMMERCE_NOTIFICATION_EVENT_TYPE = "commerce.notification";
|
|
1693
2190
|
function isValidWebhookEvent(data) {
|
|
1694
2191
|
if (typeof data !== "object" || data === null) return false;
|
|
1695
2192
|
const obj = data;
|
|
1696
2193
|
return typeof obj.collection === "string" && typeof obj.operation === "string" && obj.operation.length > 0 && typeof obj.data === "object" && obj.data !== null;
|
|
1697
2194
|
}
|
|
2195
|
+
var COMMERCE_NOTIFICATION_OPERATION = "notification";
|
|
2196
|
+
var COMMERCE_NOTIFICATION_EVENTS = [
|
|
2197
|
+
"orderPaid",
|
|
2198
|
+
"orderCanceled",
|
|
2199
|
+
"fulfillmentShipped",
|
|
2200
|
+
"orderDelivered",
|
|
2201
|
+
"returnRequested",
|
|
2202
|
+
"returnCompleted"
|
|
2203
|
+
];
|
|
1698
2204
|
function isStringOrNumber(value) {
|
|
1699
2205
|
return typeof value === "string" || typeof value === "number";
|
|
1700
2206
|
}
|
|
1701
2207
|
function isWebhookOrderScope(value) {
|
|
1702
|
-
if (!
|
|
2208
|
+
if (!isRecord2(value)) return false;
|
|
1703
2209
|
if (value.kind === "collection") {
|
|
1704
2210
|
return typeof value.collection === "string";
|
|
1705
2211
|
}
|
|
@@ -1709,16 +2215,40 @@ function isWebhookOrderScope(value) {
|
|
|
1709
2215
|
return false;
|
|
1710
2216
|
}
|
|
1711
2217
|
function isWebhookOrderMoved(value) {
|
|
1712
|
-
if (!
|
|
2218
|
+
if (!isRecord2(value)) return false;
|
|
1713
2219
|
return typeof value.collection === "string" && isStringOrNumber(value.id) && (value.relatedCollection === void 0 || typeof value.relatedCollection === "string") && (value.relatedId === void 0 || isStringOrNumber(value.relatedId));
|
|
1714
2220
|
}
|
|
1715
2221
|
function hasOptionalOrderValue(value, key) {
|
|
1716
2222
|
return value[key] === void 0 || value[key] === null || typeof value[key] === "string";
|
|
1717
2223
|
}
|
|
1718
2224
|
function isWebhookOrderChange(value) {
|
|
1719
|
-
if (!
|
|
2225
|
+
if (!isRecord2(value)) return false;
|
|
1720
2226
|
return value.type === "order" && value.source === "payload-orderable" && (value.orderableFieldName === void 0 || typeof value.orderableFieldName === "string") && hasOptionalOrderValue(value, "previousOrder") && hasOptionalOrderValue(value, "nextOrder") && isWebhookOrderScope(value.scope) && isWebhookOrderMoved(value.moved);
|
|
1721
2227
|
}
|
|
2228
|
+
function isCommerceNotificationEventName(value) {
|
|
2229
|
+
return typeof value === "string" && COMMERCE_NOTIFICATION_EVENTS.includes(value);
|
|
2230
|
+
}
|
|
2231
|
+
function isCommerceNotificationSourceCollection(value) {
|
|
2232
|
+
return value === "orders" || value === "fulfillments" || value === "returns";
|
|
2233
|
+
}
|
|
2234
|
+
function isCommerceNotificationData(value) {
|
|
2235
|
+
if (!isRecord2(value)) return false;
|
|
2236
|
+
if (value.source !== void 0 && (!isRecord2(value.source) || !isCommerceNotificationSourceCollection(value.source.collection) || !isStringOrNumber(value.source.id))) {
|
|
2237
|
+
return false;
|
|
2238
|
+
}
|
|
2239
|
+
return (value.orderId === void 0 || isStringOrNumber(value.orderId)) && (value.orderNumber === void 0 || typeof value.orderNumber === "string") && (value.status === void 0 || typeof value.status === "string") && (value.totalAmount === void 0 || typeof value.totalAmount === "number") && (value.currency === void 0 || typeof value.currency === "string") && (value.fulfillmentId === void 0 || isStringOrNumber(value.fulfillmentId)) && (value.fulfillmentStatus === void 0 || typeof value.fulfillmentStatus === "string") && (value.shippedAt === void 0 || typeof value.shippedAt === "string") && (value.returnId === void 0 || isStringOrNumber(value.returnId)) && (value.returnStatus === void 0 || typeof value.returnStatus === "string") && (value.refundAmount === void 0 || typeof value.refundAmount === "number") && (value.completedAt === void 0 || typeof value.completedAt === "string");
|
|
2240
|
+
}
|
|
2241
|
+
function isCommerceNotification(value) {
|
|
2242
|
+
if (!isRecord2(value)) return false;
|
|
2243
|
+
return isCommerceNotificationEventName(value.event) && typeof value.intentId === "string" && value.intentId.length > 0 && typeof value.dedupeKey === "string" && value.dedupeKey.length > 0 && (value.orderId === void 0 || typeof value.orderId === "string") && (value.fulfillmentId === void 0 || typeof value.fulfillmentId === "string") && (value.returnId === void 0 || typeof value.returnId === "string");
|
|
2244
|
+
}
|
|
2245
|
+
function isWebhookCommerceNotificationChange(value) {
|
|
2246
|
+
if (!isRecord2(value)) return false;
|
|
2247
|
+
return value.type === "notification" && value.source === "commerce-notifications" && isCommerceNotificationEventName(value.event) && isCommerceNotificationSourceCollection(value.sourceCollection) && isStringOrNumber(value.sourceId);
|
|
2248
|
+
}
|
|
2249
|
+
function matchesOptionalId(actual, expected) {
|
|
2250
|
+
return actual === void 0 || expected === void 0 || actual === expected;
|
|
2251
|
+
}
|
|
1722
2252
|
function isOrderChangedWebhookEvent(event) {
|
|
1723
2253
|
if (!isValidWebhookEvent(event) || event.operation !== "update" || event.eventType !== ORDER_CHANGED_EVENT_TYPE || !isWebhookOrderChange(event.change)) {
|
|
1724
2254
|
return false;
|
|
@@ -1731,6 +2261,60 @@ function isOrderChangedWebhookEvent(event) {
|
|
|
1731
2261
|
}
|
|
1732
2262
|
return true;
|
|
1733
2263
|
}
|
|
2264
|
+
function isCommerceNotificationWebhookEvent(event) {
|
|
2265
|
+
if (!isValidWebhookEvent(event) || event.operation !== COMMERCE_NOTIFICATION_OPERATION || event.eventType !== COMMERCE_NOTIFICATION_EVENT_TYPE || !isCommerceNotificationData(event.data) || !isCommerceNotification(event.notification)) {
|
|
2266
|
+
return false;
|
|
2267
|
+
}
|
|
2268
|
+
const notification = event.notification;
|
|
2269
|
+
const data = event.data;
|
|
2270
|
+
const change = event.change;
|
|
2271
|
+
const sourceCollection = data.source?.collection ?? event.collection;
|
|
2272
|
+
const sourceId = data.source?.id;
|
|
2273
|
+
if (!isCommerceNotificationSourceCollection(sourceCollection)) return false;
|
|
2274
|
+
if (data.source !== void 0 && event.collection !== data.source.collection) {
|
|
2275
|
+
return false;
|
|
2276
|
+
}
|
|
2277
|
+
if (change !== void 0) {
|
|
2278
|
+
if (!isWebhookCommerceNotificationChange(change)) return false;
|
|
2279
|
+
if (change.sourceCollection !== sourceCollection) return false;
|
|
2280
|
+
if (change.event !== notification.event) return false;
|
|
2281
|
+
if (!matchesOptionalId(change.sourceId, sourceId)) return false;
|
|
2282
|
+
}
|
|
2283
|
+
const changeSourceId = isWebhookCommerceNotificationChange(change) ? change.sourceId : void 0;
|
|
2284
|
+
if (notification.event === "orderPaid" || notification.event === "orderCanceled" || notification.event === "orderDelivered") {
|
|
2285
|
+
return sourceCollection === "orders" && typeof notification.orderId === "string" && notification.orderId.length > 0 && matchesOptionalId(data.orderId, sourceId) && matchesOptionalId(notification.orderId, sourceId) && matchesOptionalId(data.orderId, notification.orderId) && matchesOptionalId(changeSourceId, data.orderId ?? notification.orderId);
|
|
2286
|
+
}
|
|
2287
|
+
if (notification.event === "fulfillmentShipped") {
|
|
2288
|
+
return sourceCollection === "fulfillments" && matchesOptionalId(data.fulfillmentId, sourceId) && typeof notification.fulfillmentId === "string" && notification.fulfillmentId.length > 0 && matchesOptionalId(notification.fulfillmentId, sourceId) && matchesOptionalId(data.fulfillmentId, notification.fulfillmentId) && matchesOptionalId(changeSourceId, notification.fulfillmentId);
|
|
2289
|
+
}
|
|
2290
|
+
if (notification.event === "returnRequested" || notification.event === "returnCompleted") {
|
|
2291
|
+
return sourceCollection === "returns" && matchesOptionalId(data.returnId, sourceId) && typeof notification.returnId === "string" && notification.returnId.length > 0 && matchesOptionalId(notification.returnId, sourceId) && matchesOptionalId(data.returnId, notification.returnId) && matchesOptionalId(changeSourceId, notification.returnId);
|
|
2292
|
+
}
|
|
2293
|
+
return false;
|
|
2294
|
+
}
|
|
2295
|
+
function getCommerceNotificationIdempotencyKey(event) {
|
|
2296
|
+
return `${event.notification.intentId}:${event.notification.dedupeKey}`;
|
|
2297
|
+
}
|
|
2298
|
+
function defineCommerceEmailConfig(config) {
|
|
2299
|
+
return config;
|
|
2300
|
+
}
|
|
2301
|
+
function createCommerceEmailWebhookHandler(handlers) {
|
|
2302
|
+
return async (event) => {
|
|
2303
|
+
if (!isCommerceNotificationWebhookEvent(event)) {
|
|
2304
|
+
await handlers.unhandled?.(event);
|
|
2305
|
+
return;
|
|
2306
|
+
}
|
|
2307
|
+
const handler = handlers[event.notification.event];
|
|
2308
|
+
if (!handler) {
|
|
2309
|
+
await handlers.unhandled?.(event);
|
|
2310
|
+
return;
|
|
2311
|
+
}
|
|
2312
|
+
await handler({
|
|
2313
|
+
event,
|
|
2314
|
+
idempotencyKey: getCommerceNotificationIdempotencyKey(event)
|
|
2315
|
+
});
|
|
2316
|
+
};
|
|
2317
|
+
}
|
|
1734
2318
|
function isWebhookCollection(event, collection) {
|
|
1735
2319
|
return isValidWebhookEvent(event) && event.collection === collection;
|
|
1736
2320
|
}
|
|
@@ -1738,7 +2322,7 @@ function isWebhookOperation(event, operation) {
|
|
|
1738
2322
|
return isValidWebhookEvent(event) && event.operation === operation;
|
|
1739
2323
|
}
|
|
1740
2324
|
var CUSTOMER_PASSWORD_RESET_OPERATION = "password-reset";
|
|
1741
|
-
function
|
|
2325
|
+
function isRecord2(value) {
|
|
1742
2326
|
return typeof value === "object" && value !== null;
|
|
1743
2327
|
}
|
|
1744
2328
|
function hasString(value, key) {
|
|
@@ -1748,7 +2332,7 @@ function hasStringOrNumber(value, key) {
|
|
|
1748
2332
|
return typeof value[key] === "string" || typeof value[key] === "number";
|
|
1749
2333
|
}
|
|
1750
2334
|
function isCustomerPasswordResetWebhookEvent(event) {
|
|
1751
|
-
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !
|
|
2335
|
+
if (event.collection !== "customers" || event.operation !== CUSTOMER_PASSWORD_RESET_OPERATION || !isRecord2(event.data)) {
|
|
1752
2336
|
return false;
|
|
1753
2337
|
}
|
|
1754
2338
|
return hasStringOrNumber(event.data, "customerId") && hasString(event.data, "email") && hasString(event.data, "name") && hasString(event.data, "resetPasswordToken") && hasString(event.data, "resetPasswordExpiresAt");
|
|
@@ -1868,6 +2452,7 @@ var INTERNAL_COLLECTIONS = [
|
|
|
1868
2452
|
"subscriptions",
|
|
1869
2453
|
"billing-history",
|
|
1870
2454
|
"inventory-reservations",
|
|
2455
|
+
"commerce-notification-intents",
|
|
1871
2456
|
"product-collection-items",
|
|
1872
2457
|
"order-status-logs",
|
|
1873
2458
|
"api-keys",
|
|
@@ -1951,6 +2536,7 @@ var COLLECTIONS = [
|
|
|
1951
2536
|
"reaction-types",
|
|
1952
2537
|
"bookmarks",
|
|
1953
2538
|
"post-categories",
|
|
2539
|
+
"post-tags",
|
|
1954
2540
|
"customer-profile-lists",
|
|
1955
2541
|
// Events
|
|
1956
2542
|
"event-calendars",
|
|
@@ -2099,22 +2685,252 @@ var RealtimeConnection = class {
|
|
|
2099
2685
|
}
|
|
2100
2686
|
}
|
|
2101
2687
|
}
|
|
2102
|
-
scheduleReconnect() {
|
|
2103
|
-
if (this.reconnectTimer) return;
|
|
2104
|
-
const delay2 = Math.min(
|
|
2105
|
-
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
2106
|
-
MAX_RECONNECT_DELAY
|
|
2107
|
-
);
|
|
2108
|
-
this.reconnectAttempt++;
|
|
2109
|
-
this.reconnectTimer = setTimeout(() => {
|
|
2110
|
-
this.reconnectTimer = null;
|
|
2111
|
-
this.abortController = new AbortController();
|
|
2112
|
-
this.startStream(this.abortController.signal);
|
|
2113
|
-
}, delay2);
|
|
2688
|
+
scheduleReconnect() {
|
|
2689
|
+
if (this.reconnectTimer) return;
|
|
2690
|
+
const delay2 = Math.min(
|
|
2691
|
+
INITIAL_RECONNECT_DELAY * Math.pow(RECONNECT_BACKOFF_FACTOR, this.reconnectAttempt),
|
|
2692
|
+
MAX_RECONNECT_DELAY
|
|
2693
|
+
);
|
|
2694
|
+
this.reconnectAttempt++;
|
|
2695
|
+
this.reconnectTimer = setTimeout(() => {
|
|
2696
|
+
this.reconnectTimer = null;
|
|
2697
|
+
this.abortController = new AbortController();
|
|
2698
|
+
this.startStream(this.abortController.signal);
|
|
2699
|
+
}, delay2);
|
|
2700
|
+
}
|
|
2701
|
+
};
|
|
2702
|
+
|
|
2703
|
+
// src/utils/product-selection-media.ts
|
|
2704
|
+
function selectedSwatchMediaItemId(swatch) {
|
|
2705
|
+
if (!swatch || swatch.type !== "media") return null;
|
|
2706
|
+
const id = swatch.mediaItemId;
|
|
2707
|
+
if (id == null || id === "") return null;
|
|
2708
|
+
return String(id);
|
|
2709
|
+
}
|
|
2710
|
+
function getMediaId(value) {
|
|
2711
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
2712
|
+
return String(value);
|
|
2713
|
+
}
|
|
2714
|
+
if (typeof value === "object" && value !== null && "id" in value) {
|
|
2715
|
+
const id = value.id;
|
|
2716
|
+
if (typeof id === "string" || typeof id === "number") return String(id);
|
|
2717
|
+
}
|
|
2718
|
+
return null;
|
|
2719
|
+
}
|
|
2720
|
+
function toPointerId(value) {
|
|
2721
|
+
return getMediaId(value);
|
|
2722
|
+
}
|
|
2723
|
+
function mediaArray(value) {
|
|
2724
|
+
if (!Array.isArray(value)) return [];
|
|
2725
|
+
return value.filter((entry) => entry != null);
|
|
2726
|
+
}
|
|
2727
|
+
function uniqueWithPrimaryFirst(primary, items) {
|
|
2728
|
+
const unique = /* @__PURE__ */ new Map();
|
|
2729
|
+
for (const item of items) {
|
|
2730
|
+
const id = getMediaId(item);
|
|
2731
|
+
const key = id ?? `inline:${unique.size}`;
|
|
2732
|
+
if (!unique.has(key)) unique.set(key, item);
|
|
2733
|
+
}
|
|
2734
|
+
if (primary) {
|
|
2735
|
+
const primaryId = getMediaId(primary);
|
|
2736
|
+
const prefixed = /* @__PURE__ */ new Map();
|
|
2737
|
+
const primaryKey = primaryId ?? "inline:primary";
|
|
2738
|
+
prefixed.set(primaryKey, primary);
|
|
2739
|
+
for (const [key, value] of unique.entries()) {
|
|
2740
|
+
if (!prefixed.has(key)) prefixed.set(key, value);
|
|
2741
|
+
}
|
|
2742
|
+
return Array.from(prefixed.values());
|
|
2743
|
+
}
|
|
2744
|
+
return Array.from(unique.values());
|
|
2745
|
+
}
|
|
2746
|
+
function buildPoolById(pool) {
|
|
2747
|
+
const poolById = /* @__PURE__ */ new Map();
|
|
2748
|
+
for (const item of pool) {
|
|
2749
|
+
const id = getMediaId(item);
|
|
2750
|
+
if (id) poolById.set(id, item);
|
|
2751
|
+
}
|
|
2752
|
+
return poolById;
|
|
2753
|
+
}
|
|
2754
|
+
function resolveVariantImageItems(variant, poolById) {
|
|
2755
|
+
if (!variant || !Array.isArray(variant.images)) return [];
|
|
2756
|
+
const resolved = [];
|
|
2757
|
+
for (const entry of variant.images) {
|
|
2758
|
+
if (entry == null) continue;
|
|
2759
|
+
if (typeof entry === "string" || typeof entry === "number") {
|
|
2760
|
+
const pooled = poolById.get(String(entry));
|
|
2761
|
+
if (pooled) resolved.push(pooled);
|
|
2762
|
+
continue;
|
|
2763
|
+
}
|
|
2764
|
+
const mediaId2 = getMediaId(entry);
|
|
2765
|
+
if (mediaId2) {
|
|
2766
|
+
const pooled = poolById.get(mediaId2);
|
|
2767
|
+
if (pooled) {
|
|
2768
|
+
resolved.push(pooled);
|
|
2769
|
+
}
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
return resolved;
|
|
2774
|
+
}
|
|
2775
|
+
function resolveOptionSwatchPrimary(selectedOptionValues, poolById) {
|
|
2776
|
+
if (!selectedOptionValues?.length) return null;
|
|
2777
|
+
for (const optionValue of selectedOptionValues) {
|
|
2778
|
+
const swatch = optionValue?.swatch;
|
|
2779
|
+
const swatchPointer = selectedSwatchMediaItemId(swatch);
|
|
2780
|
+
if (swatchPointer) {
|
|
2781
|
+
const pooled = poolById.get(swatchPointer);
|
|
2782
|
+
if (pooled) return pooled;
|
|
2783
|
+
}
|
|
2784
|
+
const inline = swatch?.inlineMedia;
|
|
2785
|
+
if (inline != null && typeof inline === "object") {
|
|
2786
|
+
return inline;
|
|
2787
|
+
}
|
|
2788
|
+
}
|
|
2789
|
+
return null;
|
|
2790
|
+
}
|
|
2791
|
+
function resolveProductSelectionMedia(input) {
|
|
2792
|
+
const pool = mediaArray(input.productMediaPool);
|
|
2793
|
+
const poolById = buildPoolById(pool);
|
|
2794
|
+
const selectedVariantImages = resolveVariantImageItems(
|
|
2795
|
+
input.selectedVariant,
|
|
2796
|
+
poolById
|
|
2797
|
+
);
|
|
2798
|
+
if (selectedVariantImages.length > 0) {
|
|
2799
|
+
const primaryImage = selectedVariantImages[0] ?? null;
|
|
2800
|
+
return {
|
|
2801
|
+
primaryImage,
|
|
2802
|
+
images: uniqueWithPrimaryFirst(primaryImage, selectedVariantImages),
|
|
2803
|
+
source: "variant_media_selected"
|
|
2804
|
+
};
|
|
2805
|
+
}
|
|
2806
|
+
if (input.selectedVariant == null && (input.matchingVariants?.length ?? 0) > 0) {
|
|
2807
|
+
const mergedMatchingImages = [];
|
|
2808
|
+
for (const matchingVariant of input.matchingVariants ?? []) {
|
|
2809
|
+
mergedMatchingImages.push(
|
|
2810
|
+
...resolveVariantImageItems(matchingVariant, poolById)
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
if (mergedMatchingImages.length > 0) {
|
|
2814
|
+
const primaryImage = mergedMatchingImages[0] ?? null;
|
|
2815
|
+
return {
|
|
2816
|
+
primaryImage,
|
|
2817
|
+
images: uniqueWithPrimaryFirst(primaryImage, mergedMatchingImages),
|
|
2818
|
+
source: "variant_media_matching"
|
|
2819
|
+
};
|
|
2820
|
+
}
|
|
2821
|
+
}
|
|
2822
|
+
const optionSwatchPrimary = resolveOptionSwatchPrimary(
|
|
2823
|
+
input.selectedOptionValues,
|
|
2824
|
+
poolById
|
|
2825
|
+
);
|
|
2826
|
+
if (optionSwatchPrimary) {
|
|
2827
|
+
return {
|
|
2828
|
+
primaryImage: optionSwatchPrimary,
|
|
2829
|
+
images: [optionSwatchPrimary],
|
|
2830
|
+
source: "option_swatch"
|
|
2831
|
+
};
|
|
2832
|
+
}
|
|
2833
|
+
return {
|
|
2834
|
+
primaryImage: null,
|
|
2835
|
+
images: [],
|
|
2836
|
+
source: "none"
|
|
2837
|
+
};
|
|
2838
|
+
}
|
|
2839
|
+
function resolveProductDisplayMedia(input) {
|
|
2840
|
+
const pool = mediaArray(input.productMediaPool);
|
|
2841
|
+
const poolById = buildPoolById(pool);
|
|
2842
|
+
const listingPointer = toPointerId(input.listingPrimaryImage);
|
|
2843
|
+
if (listingPointer) {
|
|
2844
|
+
const listingPrimary = poolById.get(listingPointer);
|
|
2845
|
+
if (listingPrimary) {
|
|
2846
|
+
return {
|
|
2847
|
+
primaryImage: listingPrimary,
|
|
2848
|
+
images: uniqueWithPrimaryFirst(listingPrimary, pool),
|
|
2849
|
+
source: "listing_primary"
|
|
2850
|
+
};
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
const productPrimaryPointer = getMediaId(input.productPrimaryMediaItemId);
|
|
2854
|
+
if (productPrimaryPointer) {
|
|
2855
|
+
const productPrimary = poolById.get(productPrimaryPointer);
|
|
2856
|
+
if (productPrimary) {
|
|
2857
|
+
return {
|
|
2858
|
+
primaryImage: productPrimary,
|
|
2859
|
+
images: uniqueWithPrimaryFirst(productPrimary, pool),
|
|
2860
|
+
source: "product_primary"
|
|
2861
|
+
};
|
|
2862
|
+
}
|
|
2114
2863
|
}
|
|
2115
|
-
|
|
2864
|
+
if (pool.length > 0) {
|
|
2865
|
+
const productPoolPrimary = pool[0] ?? null;
|
|
2866
|
+
if (productPoolPrimary) {
|
|
2867
|
+
return {
|
|
2868
|
+
primaryImage: productPoolPrimary,
|
|
2869
|
+
images: uniqueWithPrimaryFirst(productPoolPrimary, pool),
|
|
2870
|
+
source: "product_pool"
|
|
2871
|
+
};
|
|
2872
|
+
}
|
|
2873
|
+
}
|
|
2874
|
+
return {
|
|
2875
|
+
primaryImage: null,
|
|
2876
|
+
images: [],
|
|
2877
|
+
source: "none"
|
|
2878
|
+
};
|
|
2879
|
+
}
|
|
2880
|
+
function resolveListingPrimaryImagePointer(input) {
|
|
2881
|
+
const pool = mediaArray(input.productMediaPool);
|
|
2882
|
+
const resolvedPointer = getMediaId(input.resolvedPrimary);
|
|
2883
|
+
if (resolvedPointer && input.resolvedSource !== "product_pool" && input.resolvedSource !== "none") {
|
|
2884
|
+
return resolvedPointer;
|
|
2885
|
+
}
|
|
2886
|
+
const poolById = buildPoolById(pool);
|
|
2887
|
+
const listingPointer = getMediaId(input.listingPrimaryImage);
|
|
2888
|
+
if (listingPointer && poolById.has(listingPointer)) {
|
|
2889
|
+
return listingPointer;
|
|
2890
|
+
}
|
|
2891
|
+
const primaryPointer = toPointerId(input.productPrimaryMediaItemId);
|
|
2892
|
+
if (primaryPointer && poolById.has(primaryPointer)) return primaryPointer;
|
|
2893
|
+
const thumbnailPointer = getMediaId(input.productThumbnail);
|
|
2894
|
+
if (thumbnailPointer && poolById.has(thumbnailPointer)) {
|
|
2895
|
+
return thumbnailPointer;
|
|
2896
|
+
}
|
|
2897
|
+
if (pool.length > 0) {
|
|
2898
|
+
const firstPoolId = getMediaId(pool[0]);
|
|
2899
|
+
if (firstPoolId) return firstPoolId;
|
|
2900
|
+
}
|
|
2901
|
+
return null;
|
|
2902
|
+
}
|
|
2116
2903
|
|
|
2117
2904
|
// src/utils/ecommerce.ts
|
|
2905
|
+
function normalizeMatrixSwatch(swatch) {
|
|
2906
|
+
if (!swatch) return null;
|
|
2907
|
+
const rawMedia = swatch.mediaItemId;
|
|
2908
|
+
const inlineMedia = rawMedia != null && typeof rawMedia === "object" && "url" in rawMedia && typeof rawMedia.url === "string" ? rawMedia : null;
|
|
2909
|
+
return {
|
|
2910
|
+
type: swatch.type ?? null,
|
|
2911
|
+
color: swatch.color ?? null,
|
|
2912
|
+
mediaItemId: extractEntityId(rawMedia),
|
|
2913
|
+
inlineMedia
|
|
2914
|
+
};
|
|
2915
|
+
}
|
|
2916
|
+
var DEFAULT_PRODUCT_SELECTION_URL_EMIT = "slug-compat";
|
|
2917
|
+
function resolveProductSelectionUrlEmit(emit) {
|
|
2918
|
+
return emit ?? DEFAULT_PRODUCT_SELECTION_URL_EMIT;
|
|
2919
|
+
}
|
|
2920
|
+
function appendSlugCompatSelectionParam(params, matrix, optionId, valueId) {
|
|
2921
|
+
const option = matrix.optionById.get(optionId);
|
|
2922
|
+
const value = matrix.valueById.get(valueId);
|
|
2923
|
+
if (!option?.slug || !value?.slug) return false;
|
|
2924
|
+
const slugMatches = option.values.filter(
|
|
2925
|
+
(candidate) => candidate.slug === value.slug
|
|
2926
|
+
);
|
|
2927
|
+
if (slugMatches.length !== 1) return false;
|
|
2928
|
+
params.append(`opt.${option.slug}`, value.slug);
|
|
2929
|
+
return true;
|
|
2930
|
+
}
|
|
2931
|
+
function appendCanonicalSelectionParam(params, optionId, valueId) {
|
|
2932
|
+
params.append(`opt.${optionId}`, valueId);
|
|
2933
|
+
}
|
|
2118
2934
|
var ProductSelectionCodecError = class extends Error {
|
|
2119
2935
|
constructor(message) {
|
|
2120
2936
|
super(message);
|
|
@@ -2159,6 +2975,18 @@ function getVariantPrimaryImage(variant) {
|
|
|
2159
2975
|
if (!variant) return null;
|
|
2160
2976
|
return extractEntityId(variant.thumbnail) ?? getFirstMediaId(variant.images);
|
|
2161
2977
|
}
|
|
2978
|
+
function resolveGenericListingPrimaryImage(product, resolvedPrimary, resolvedSource) {
|
|
2979
|
+
return resolveListingPrimaryImagePointer({
|
|
2980
|
+
productMediaPool: product?.images ?? [],
|
|
2981
|
+
productPrimaryMediaItemId: getRelationID(
|
|
2982
|
+
product?.primaryMediaItemId ?? null
|
|
2983
|
+
),
|
|
2984
|
+
productThumbnail: product?.thumbnail ?? null,
|
|
2985
|
+
listingPrimaryImage: product?.listing?.primaryImage ?? null,
|
|
2986
|
+
resolvedPrimary,
|
|
2987
|
+
resolvedSource
|
|
2988
|
+
});
|
|
2989
|
+
}
|
|
2162
2990
|
function getFirstAvailableVariantPrimaryImage(variants) {
|
|
2163
2991
|
const orderedVariants = [...variants].sort(compareVariantOrder);
|
|
2164
2992
|
for (const variant of orderedVariants) {
|
|
@@ -2168,6 +2996,27 @@ function getFirstAvailableVariantPrimaryImage(variants) {
|
|
|
2168
2996
|
}
|
|
2169
2997
|
return null;
|
|
2170
2998
|
}
|
|
2999
|
+
function normalizeProductOptionValueSwatch(swatch) {
|
|
3000
|
+
if (swatch == null) return null;
|
|
3001
|
+
if (typeof swatch !== "object") return null;
|
|
3002
|
+
const raw = swatch;
|
|
3003
|
+
const mediaItemId = extractEntityId(raw.mediaItemId);
|
|
3004
|
+
const color = typeof raw.color === "string" ? raw.color.trim() : "";
|
|
3005
|
+
const hasColor = color.length > 0;
|
|
3006
|
+
if (raw.type === "media" || mediaItemId && raw.type !== "color") {
|
|
3007
|
+
if (!mediaItemId || hasColor) return null;
|
|
3008
|
+
return {
|
|
3009
|
+
type: "media",
|
|
3010
|
+
mediaItemId,
|
|
3011
|
+
color: null
|
|
3012
|
+
};
|
|
3013
|
+
}
|
|
3014
|
+
if (raw.type === "color" || hasColor) {
|
|
3015
|
+
if (mediaItemId || !hasColor) return null;
|
|
3016
|
+
return { type: "color", color, mediaItemId: null };
|
|
3017
|
+
}
|
|
3018
|
+
return null;
|
|
3019
|
+
}
|
|
2171
3020
|
function normalizeOptionValue(value, fallbackOptionId, fallbackOptionSlug) {
|
|
2172
3021
|
return {
|
|
2173
3022
|
id: String(value.id),
|
|
@@ -2175,9 +3024,7 @@ function normalizeOptionValue(value, fallbackOptionId, fallbackOptionSlug) {
|
|
|
2175
3024
|
optionSlug: fallbackOptionSlug,
|
|
2176
3025
|
label: value.value || value.slug || String(value.id),
|
|
2177
3026
|
slug: value.slug ?? null,
|
|
2178
|
-
|
|
2179
|
-
thumbnail: value.thumbnail ?? null,
|
|
2180
|
-
images: value.images ?? null,
|
|
3027
|
+
swatch: normalizeProductOptionValueSwatch(value.swatch),
|
|
2181
3028
|
order: value._order ?? value["_product-option-values_values_order"] ?? ""
|
|
2182
3029
|
};
|
|
2183
3030
|
}
|
|
@@ -2283,9 +3130,7 @@ function buildProductOptionMatrixFromDetail(detail) {
|
|
|
2283
3130
|
optionSlug: option.slug,
|
|
2284
3131
|
label: value.value || value.slug || String(value.id),
|
|
2285
3132
|
slug: value.slug,
|
|
2286
|
-
|
|
2287
|
-
thumbnail: value.thumbnail ?? null,
|
|
2288
|
-
images: value.images ?? null,
|
|
3133
|
+
swatch: normalizeProductOptionValueSwatch(value.swatch),
|
|
2289
3134
|
order: matrixOrder(valueIndex)
|
|
2290
3135
|
}))
|
|
2291
3136
|
}));
|
|
@@ -2721,13 +3566,19 @@ function stringifyProductSelection(detail, selection = {}, options) {
|
|
|
2721
3566
|
return params.toString();
|
|
2722
3567
|
}
|
|
2723
3568
|
}
|
|
3569
|
+
const emit = resolveProductSelectionUrlEmit(options?.emit);
|
|
2724
3570
|
for (const optionId of matrix.optionIds) {
|
|
2725
3571
|
const valueId = normalized.byOptionId[optionId];
|
|
2726
3572
|
if (!valueId) continue;
|
|
2727
3573
|
if (!matrix.optionById.has(optionId) || !matrix.valueById.has(valueId)) {
|
|
2728
3574
|
continue;
|
|
2729
3575
|
}
|
|
2730
|
-
|
|
3576
|
+
if (emit === "slug-compat") {
|
|
3577
|
+
if (appendSlugCompatSelectionParam(params, matrix, optionId, valueId)) {
|
|
3578
|
+
continue;
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
appendCanonicalSelectionParam(params, optionId, valueId);
|
|
2731
3582
|
}
|
|
2732
3583
|
return params.toString();
|
|
2733
3584
|
}
|
|
@@ -2766,6 +3617,100 @@ function getExactSelectedVariantEntry(matrix, selection, matchingVariants) {
|
|
|
2766
3617
|
)
|
|
2767
3618
|
) ?? null;
|
|
2768
3619
|
}
|
|
3620
|
+
function normalizedSelectionFromByOptionId(matrix, selectedByOptionId, variantId) {
|
|
3621
|
+
const byOptionId = Object.fromEntries(
|
|
3622
|
+
matrix.optionIds.map((optionId) => [optionId, selectedByOptionId.get(optionId)]).filter((entry) => Boolean(entry[1]))
|
|
3623
|
+
);
|
|
3624
|
+
const byOptionSlug = Object.fromEntries(
|
|
3625
|
+
matrix.options.map((option) => {
|
|
3626
|
+
const valueId = selectedByOptionId.get(option.id);
|
|
3627
|
+
const value = valueId ? matrix.valueById.get(valueId) : void 0;
|
|
3628
|
+
return [option.slug, value?.slug ?? void 0];
|
|
3629
|
+
}).filter((entry) => Boolean(entry[1]))
|
|
3630
|
+
);
|
|
3631
|
+
return {
|
|
3632
|
+
byOptionSlug,
|
|
3633
|
+
byOptionId,
|
|
3634
|
+
valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
|
|
3635
|
+
variantId
|
|
3636
|
+
};
|
|
3637
|
+
}
|
|
3638
|
+
function buildFilledNormalizedSelection(matrix, normalizedSelection, listing) {
|
|
3639
|
+
const selectedByOptionId = new Map(
|
|
3640
|
+
Object.entries(normalizedSelection.byOptionId)
|
|
3641
|
+
);
|
|
3642
|
+
if (matrix.optionIds.every((optionId) => selectedByOptionId.has(optionId))) {
|
|
3643
|
+
const variantId = normalizedSelection.variantId ?? resolveVariantIdForCompleteSelection(matrix, selectedByOptionId);
|
|
3644
|
+
if (variantId === normalizedSelection.variantId) {
|
|
3645
|
+
return normalizedSelection;
|
|
3646
|
+
}
|
|
3647
|
+
return normalizedSelectionFromByOptionId(
|
|
3648
|
+
matrix,
|
|
3649
|
+
selectedByOptionId,
|
|
3650
|
+
variantId
|
|
3651
|
+
);
|
|
3652
|
+
}
|
|
3653
|
+
for (const option of matrix.options) {
|
|
3654
|
+
if (selectedByOptionId.has(option.id)) continue;
|
|
3655
|
+
if (option.values.length === 1) {
|
|
3656
|
+
selectedByOptionId.set(option.id, option.values[0].id);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
if (matrix.optionIds.every((optionId) => selectedByOptionId.has(optionId))) {
|
|
3660
|
+
const variantId = normalizedSelection.variantId ?? resolveVariantIdForCompleteSelection(matrix, selectedByOptionId);
|
|
3661
|
+
return normalizedSelectionFromByOptionId(
|
|
3662
|
+
matrix,
|
|
3663
|
+
selectedByOptionId,
|
|
3664
|
+
variantId
|
|
3665
|
+
);
|
|
3666
|
+
}
|
|
3667
|
+
const partialNormalized = normalizedSelectionFromByOptionId(
|
|
3668
|
+
matrix,
|
|
3669
|
+
selectedByOptionId,
|
|
3670
|
+
normalizedSelection.variantId
|
|
3671
|
+
);
|
|
3672
|
+
const matchingVariantEntries = getMatchingVariantEntries(
|
|
3673
|
+
matrix,
|
|
3674
|
+
partialNormalized
|
|
3675
|
+
);
|
|
3676
|
+
const orderedMatches = [...matchingVariantEntries].sort(
|
|
3677
|
+
(left, right) => compareVariantOrder(left.source, right.source)
|
|
3678
|
+
);
|
|
3679
|
+
const hintVariantId = listing.selectionHintVariant != null ? String(listing.selectionHintVariant) : null;
|
|
3680
|
+
const hintedEntry = hintVariantId != null ? orderedMatches.find((entry) => entry.id === hintVariantId) : void 0;
|
|
3681
|
+
const availableEntry = orderedMatches.find(
|
|
3682
|
+
(entry) => isVariantAvailableForSale(entry.source)
|
|
3683
|
+
);
|
|
3684
|
+
const chosenEntry = hintedEntry ?? availableEntry ?? orderedMatches[0];
|
|
3685
|
+
if (!chosenEntry) {
|
|
3686
|
+
return normalizedSelection;
|
|
3687
|
+
}
|
|
3688
|
+
for (const optionId of matrix.optionIds) {
|
|
3689
|
+
if (selectedByOptionId.has(optionId)) continue;
|
|
3690
|
+
const valueId = chosenEntry.optionValueByOptionId.get(optionId);
|
|
3691
|
+
if (valueId) selectedByOptionId.set(optionId, valueId);
|
|
3692
|
+
}
|
|
3693
|
+
const filledVariantId = matrix.optionIds.every(
|
|
3694
|
+
(optionId) => selectedByOptionId.has(optionId)
|
|
3695
|
+
) ? resolveVariantIdForCompleteSelection(matrix, selectedByOptionId) ?? chosenEntry.id : normalizedSelection.variantId;
|
|
3696
|
+
return normalizedSelectionFromByOptionId(
|
|
3697
|
+
matrix,
|
|
3698
|
+
selectedByOptionId,
|
|
3699
|
+
filledVariantId
|
|
3700
|
+
);
|
|
3701
|
+
}
|
|
3702
|
+
function resolveVariantIdForCompleteSelection(matrix, selectedByOptionId) {
|
|
3703
|
+
if (!matrix.optionIds.every((optionId) => selectedByOptionId.has(optionId))) {
|
|
3704
|
+
return null;
|
|
3705
|
+
}
|
|
3706
|
+
const normalized = normalizedSelectionFromByOptionId(
|
|
3707
|
+
matrix,
|
|
3708
|
+
selectedByOptionId,
|
|
3709
|
+
null
|
|
3710
|
+
);
|
|
3711
|
+
const matchingVariantEntries = getMatchingVariantEntries(matrix, normalized);
|
|
3712
|
+
return getExactSelectedVariantEntry(matrix, normalized, matchingVariantEntries)?.id ?? null;
|
|
3713
|
+
}
|
|
2769
3714
|
function buildSelectionPrice(variants) {
|
|
2770
3715
|
const { min, max } = getMinMax(variants.map((variant) => variant.price));
|
|
2771
3716
|
const { min: compareAtMin, max: compareAtMax } = getMinMax(
|
|
@@ -2779,38 +3724,51 @@ function buildSelectionPrice(variants) {
|
|
|
2779
3724
|
isRange: min !== null && max !== null ? min !== max : false
|
|
2780
3725
|
};
|
|
2781
3726
|
}
|
|
2782
|
-
function firstMedia(value) {
|
|
2783
|
-
if (value == null) return null;
|
|
2784
|
-
if (Array.isArray(value)) return firstMedia(value[0]);
|
|
2785
|
-
return value;
|
|
2786
|
-
}
|
|
2787
3727
|
function isPresentMedia(value) {
|
|
2788
3728
|
return value != null;
|
|
2789
3729
|
}
|
|
2790
|
-
function
|
|
3730
|
+
function mediaArray2(values) {
|
|
2791
3731
|
if (!Array.isArray(values)) return [];
|
|
2792
3732
|
return values.filter(isPresentMedia);
|
|
2793
3733
|
}
|
|
2794
3734
|
function buildSelectionMedia(detail, selectedVariant, matchingVariants, selectedValues) {
|
|
2795
|
-
const
|
|
2796
|
-
const
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
3735
|
+
const productPool = mediaArray2(detail.images);
|
|
3736
|
+
const resolvedMedia = resolveProductSelectionMedia({
|
|
3737
|
+
productMediaPool: productPool,
|
|
3738
|
+
productPrimaryMediaItemId: getRelationID(detail.primaryMediaItemId),
|
|
3739
|
+
selectedVariant: selectedVariant ? {
|
|
3740
|
+
id: selectedVariant.id,
|
|
3741
|
+
images: selectedVariant.images
|
|
3742
|
+
} : null,
|
|
3743
|
+
matchingVariants: matchingVariants.map((variant) => ({
|
|
3744
|
+
id: variant.id,
|
|
3745
|
+
images: variant.images
|
|
3746
|
+
})),
|
|
3747
|
+
selectedOptionValues: selectedValues.map((value) => ({
|
|
3748
|
+
id: value.id,
|
|
3749
|
+
swatch: normalizeMatrixSwatch(value.swatch)
|
|
3750
|
+
}))
|
|
3751
|
+
});
|
|
3752
|
+
if (resolvedMedia.source !== "none") {
|
|
3753
|
+
return {
|
|
3754
|
+
primaryImage: resolvedMedia.primaryImage,
|
|
3755
|
+
images: resolvedMedia.images,
|
|
3756
|
+
source: resolvedMedia.source
|
|
3757
|
+
};
|
|
3758
|
+
}
|
|
3759
|
+
const displayMedia = resolveProductDisplayMedia({
|
|
3760
|
+
productMediaPool: productPool,
|
|
3761
|
+
productPrimaryMediaItemId: getRelationID(detail.primaryMediaItemId),
|
|
3762
|
+
listingPrimaryImage: detail.listing.primaryImage ?? null
|
|
3763
|
+
});
|
|
3764
|
+
if (displayMedia.source !== "none") {
|
|
3765
|
+
return {
|
|
3766
|
+
primaryImage: displayMedia.primaryImage,
|
|
3767
|
+
images: displayMedia.images,
|
|
3768
|
+
source: displayMedia.source
|
|
3769
|
+
};
|
|
3770
|
+
}
|
|
3771
|
+
return { primaryImage: null, images: [], source: "none" };
|
|
2814
3772
|
}
|
|
2815
3773
|
function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
2816
3774
|
if (selectedVariant) {
|
|
@@ -2833,7 +3791,9 @@ function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
|
2833
3791
|
};
|
|
2834
3792
|
}
|
|
2835
3793
|
function buildAvailableValueStock(variants) {
|
|
2836
|
-
const activeVariants = variants.filter(
|
|
3794
|
+
const activeVariants = variants.filter(
|
|
3795
|
+
(variant) => variant.isActive !== false
|
|
3796
|
+
);
|
|
2837
3797
|
const isUnlimited = activeVariants.some((variant) => variant.isUnlimited);
|
|
2838
3798
|
const availableStock = isUnlimited ? null : activeVariants.reduce(
|
|
2839
3799
|
(sum, variant) => sum + Math.max(0, variant.stock - variant.reservedStock),
|
|
@@ -2845,6 +3805,19 @@ function buildAvailableValueStock(variants) {
|
|
|
2845
3805
|
availableStock
|
|
2846
3806
|
};
|
|
2847
3807
|
}
|
|
3808
|
+
function buildProductSelectionAvailableValue(matrixValue, _productImages, selected, available, stock) {
|
|
3809
|
+
return {
|
|
3810
|
+
valueId: matrixValue.id,
|
|
3811
|
+
value: matrixValue.label,
|
|
3812
|
+
label: matrixValue.label,
|
|
3813
|
+
slug: matrixValue.slug ?? "",
|
|
3814
|
+
selected,
|
|
3815
|
+
available,
|
|
3816
|
+
exists: available,
|
|
3817
|
+
...stock,
|
|
3818
|
+
swatch: matrixValue.swatch ?? null
|
|
3819
|
+
};
|
|
3820
|
+
}
|
|
2848
3821
|
function getCandidateVariantsForValue(matrix, optionId, valueId, selectedValueIds) {
|
|
2849
3822
|
const selectedByOption = getSelectedValueByOptionId(matrix, selectedValueIds);
|
|
2850
3823
|
selectedByOption.set(optionId, valueId);
|
|
@@ -2857,17 +3830,25 @@ function getCandidateVariantsForValue(matrix, optionId, valueId, selectedValueId
|
|
|
2857
3830
|
function getResolutionContext(context) {
|
|
2858
3831
|
return {
|
|
2859
3832
|
images: context?.images ?? context?.detail?.images ?? [],
|
|
2860
|
-
listing: context?.listing ?? context?.detail?.listing ?? {}
|
|
3833
|
+
listing: context?.listing ?? context?.detail?.listing ?? {},
|
|
3834
|
+
primaryMediaItemId: context?.primaryMediaItemId ?? context?.detail?.primaryMediaItemId ?? null
|
|
2861
3835
|
};
|
|
2862
3836
|
}
|
|
2863
3837
|
function resolveProductSelectionFromMatrix(matrix, selection = {}, options, context) {
|
|
2864
|
-
const { images, listing } = getResolutionContext(context);
|
|
3838
|
+
const { images, listing, primaryMediaItemId } = getResolutionContext(context);
|
|
2865
3839
|
const effectiveSelection = hasExplicitSelection(selection) || listing.selectionHintVariant == null ? selection : { ...selection, variantId: listing.selectionHintVariant };
|
|
2866
|
-
|
|
3840
|
+
let normalizedSelection = normalizeProductSelectionFromMatrix(
|
|
2867
3841
|
matrix,
|
|
2868
3842
|
effectiveSelection,
|
|
2869
3843
|
options
|
|
2870
3844
|
);
|
|
3845
|
+
if (options?.fillDefaults) {
|
|
3846
|
+
normalizedSelection = buildFilledNormalizedSelection(
|
|
3847
|
+
matrix,
|
|
3848
|
+
normalizedSelection,
|
|
3849
|
+
listing
|
|
3850
|
+
);
|
|
3851
|
+
}
|
|
2871
3852
|
const matchingVariantEntries = getMatchingVariantEntries(
|
|
2872
3853
|
matrix,
|
|
2873
3854
|
normalizedSelection
|
|
@@ -2896,24 +3877,22 @@ function resolveProductSelectionFromMatrix(matrix, selection = {}, options, cont
|
|
|
2896
3877
|
);
|
|
2897
3878
|
return [
|
|
2898
3879
|
option.id,
|
|
2899
|
-
option.values.map(
|
|
2900
|
-
|
|
2901
|
-
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
|
|
2910
|
-
|
|
3880
|
+
option.values.map(
|
|
3881
|
+
(value) => buildProductSelectionAvailableValue(
|
|
3882
|
+
value,
|
|
3883
|
+
images,
|
|
3884
|
+
normalizedSelection.byOptionId[option.id] === value.id,
|
|
3885
|
+
availableValueIds.has(value.id),
|
|
3886
|
+
buildAvailableValueStock(
|
|
3887
|
+
getCandidateVariantsForValue(
|
|
3888
|
+
matrix,
|
|
3889
|
+
option.id,
|
|
3890
|
+
value.id,
|
|
3891
|
+
normalizedSelection.valueIds
|
|
3892
|
+
)
|
|
2911
3893
|
)
|
|
2912
|
-
)
|
|
2913
|
-
|
|
2914
|
-
thumbnail: value.thumbnail ?? null,
|
|
2915
|
-
images: value.images ?? null
|
|
2916
|
-
}))
|
|
3894
|
+
)
|
|
3895
|
+
)
|
|
2917
3896
|
];
|
|
2918
3897
|
})
|
|
2919
3898
|
);
|
|
@@ -2941,7 +3920,8 @@ function resolveProductSelectionFromMatrix(matrix, selection = {}, options, cont
|
|
|
2941
3920
|
images,
|
|
2942
3921
|
listing: {
|
|
2943
3922
|
primaryImage: listing.primaryImage ?? null
|
|
2944
|
-
}
|
|
3923
|
+
},
|
|
3924
|
+
primaryMediaItemId
|
|
2945
3925
|
},
|
|
2946
3926
|
selectedVariant,
|
|
2947
3927
|
matchingVariants,
|
|
@@ -2956,6 +3936,78 @@ function resolveProductSelection(detail, selection = {}, options) {
|
|
|
2956
3936
|
detail
|
|
2957
3937
|
});
|
|
2958
3938
|
}
|
|
3939
|
+
function selectNext(detail, current = {}, optionSlug, valueSlug, options) {
|
|
3940
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
3941
|
+
const listing = detail.listing ?? {};
|
|
3942
|
+
const normalizedCurrent = normalizeProductSelectionFromMatrix(
|
|
3943
|
+
matrix,
|
|
3944
|
+
current,
|
|
3945
|
+
options
|
|
3946
|
+
);
|
|
3947
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
3948
|
+
if (!option) return normalizedCurrent;
|
|
3949
|
+
const valueMatches = option.values.filter(
|
|
3950
|
+
(candidate) => candidate.slug === valueSlug
|
|
3951
|
+
);
|
|
3952
|
+
if (valueMatches.length !== 1) return normalizedCurrent;
|
|
3953
|
+
const clickedValueId = valueMatches[0].id;
|
|
3954
|
+
const keepClickedSelection = () => {
|
|
3955
|
+
const selectedByOptionId = /* @__PURE__ */ new Map();
|
|
3956
|
+
selectedByOptionId.set(option.id, clickedValueId);
|
|
3957
|
+
for (const otherOptionId of matrix.optionIds) {
|
|
3958
|
+
if (otherOptionId === option.id) continue;
|
|
3959
|
+
const otherValueId = normalizedCurrent.byOptionId[otherOptionId];
|
|
3960
|
+
if (!otherValueId) continue;
|
|
3961
|
+
const constraintIds = normalizeSelectedValueIds(
|
|
3962
|
+
matrix,
|
|
3963
|
+
Array.from(selectedByOptionId.values())
|
|
3964
|
+
);
|
|
3965
|
+
const available = getAvailableOptionValues(
|
|
3966
|
+
matrix,
|
|
3967
|
+
otherOptionId,
|
|
3968
|
+
constraintIds
|
|
3969
|
+
);
|
|
3970
|
+
if (available.some((candidate) => candidate.id === otherValueId)) {
|
|
3971
|
+
selectedByOptionId.set(otherOptionId, otherValueId);
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
return buildFilledNormalizedSelection(
|
|
3975
|
+
matrix,
|
|
3976
|
+
normalizedSelectionFromByOptionId(matrix, selectedByOptionId, null),
|
|
3977
|
+
listing
|
|
3978
|
+
);
|
|
3979
|
+
};
|
|
3980
|
+
const keepPriorSelection = () => {
|
|
3981
|
+
const selectedByOptionId = new Map(
|
|
3982
|
+
Object.entries(normalizedCurrent.byOptionId)
|
|
3983
|
+
);
|
|
3984
|
+
selectedByOptionId.delete(option.id);
|
|
3985
|
+
return buildFilledNormalizedSelection(
|
|
3986
|
+
matrix,
|
|
3987
|
+
normalizedSelectionFromByOptionId(matrix, selectedByOptionId, null),
|
|
3988
|
+
listing
|
|
3989
|
+
);
|
|
3990
|
+
};
|
|
3991
|
+
const clickedResult = keepClickedSelection();
|
|
3992
|
+
const clickedVariantId = resolveVariantIdForCompleteSelection(
|
|
3993
|
+
matrix,
|
|
3994
|
+
new Map(Object.entries(clickedResult.byOptionId))
|
|
3995
|
+
);
|
|
3996
|
+
const priorValueIds = Object.entries(normalizedCurrent.byOptionId).filter(([otherOptionId]) => otherOptionId !== option.id).map(([, valueId]) => valueId);
|
|
3997
|
+
const clickedAvailableWithPrior = priorValueIds.length === 0 || getAvailableOptionValues(matrix, option.id, priorValueIds).some(
|
|
3998
|
+
(candidate) => candidate.id === clickedValueId
|
|
3999
|
+
);
|
|
4000
|
+
if (clickedVariantId != null) {
|
|
4001
|
+
const priorClickedValue = normalizedCurrent.byOptionId[option.id];
|
|
4002
|
+
if (priorClickedValue != null && priorClickedValue !== clickedValueId) {
|
|
4003
|
+
return clickedResult;
|
|
4004
|
+
}
|
|
4005
|
+
if (clickedAvailableWithPrior) {
|
|
4006
|
+
return clickedResult;
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
return keepPriorSelection();
|
|
4010
|
+
}
|
|
2959
4011
|
function isProductDetailImageMedia(value) {
|
|
2960
4012
|
return typeof value === "object" && value !== null;
|
|
2961
4013
|
}
|
|
@@ -2996,18 +4048,47 @@ function joinProductPath(basePath, slug, trailingSlash) {
|
|
|
2996
4048
|
function getProductHrefGroupSelection(group, matrix) {
|
|
2997
4049
|
if (!group) return null;
|
|
2998
4050
|
if (group.variantId != null) return { variantId: group.variantId };
|
|
2999
|
-
const listingVariantId = group.listing?.selectionHintVariant;
|
|
3000
4051
|
const optionId = group.optionId != null ? String(group.optionId) : group.optionSlug ? matrix?.optionBySlug.get(group.optionSlug)?.id : void 0;
|
|
3001
|
-
if (!optionId)
|
|
3002
|
-
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
3003
|
-
}
|
|
4052
|
+
if (!optionId) return null;
|
|
3004
4053
|
const option = matrix?.optionById.get(optionId);
|
|
3005
4054
|
const optionValueId = group.optionValueId != null ? String(group.optionValueId) : group.optionValueSlug && option ? option.values.find((value) => value.slug === group.optionValueSlug)?.id : void 0;
|
|
3006
|
-
if (!optionValueId)
|
|
3007
|
-
return listingVariantId != null ? { variantId: listingVariantId } : null;
|
|
3008
|
-
}
|
|
4055
|
+
if (!optionValueId) return null;
|
|
3009
4056
|
return { byOptionId: { [optionId]: optionValueId } };
|
|
3010
4057
|
}
|
|
4058
|
+
function getProductHrefCodecOptions(options) {
|
|
4059
|
+
return options.emit != null ? { emit: options.emit } : void 0;
|
|
4060
|
+
}
|
|
4061
|
+
function appendGroupByOptionIdHrefParams(params, group, groupSelection, options) {
|
|
4062
|
+
const emit = resolveProductSelectionUrlEmit(options.emit);
|
|
4063
|
+
const byOptionId = groupSelection.byOptionId ?? {};
|
|
4064
|
+
const entries = Object.entries(byOptionId);
|
|
4065
|
+
if (entries.length === 0) return false;
|
|
4066
|
+
for (const [optionId, valueId] of entries) {
|
|
4067
|
+
const optionSlug = group?.optionSlug ?? options.matrix?.optionById.get(optionId)?.slug ?? null;
|
|
4068
|
+
const valueSlug = group?.optionValueSlug ?? options.matrix?.valueById.get(String(valueId))?.slug ?? null;
|
|
4069
|
+
if (emit === "slug-compat" && optionSlug && valueSlug && entries.length === 1) {
|
|
4070
|
+
params.set(`opt.${optionSlug}`, valueSlug);
|
|
4071
|
+
return true;
|
|
4072
|
+
}
|
|
4073
|
+
if (emit === "slug-compat" && options.matrix && appendSlugCompatSelectionParam(
|
|
4074
|
+
params,
|
|
4075
|
+
options.matrix,
|
|
4076
|
+
optionId,
|
|
4077
|
+
String(valueId)
|
|
4078
|
+
)) {
|
|
4079
|
+
continue;
|
|
4080
|
+
}
|
|
4081
|
+
appendCanonicalSelectionParam(params, optionId, String(valueId));
|
|
4082
|
+
}
|
|
4083
|
+
return params.size > 0;
|
|
4084
|
+
}
|
|
4085
|
+
function getPreferCompleteVariantFromHintSelection(group, options) {
|
|
4086
|
+
if (!options.preferCompleteVariantFromHint) return null;
|
|
4087
|
+
if (group?.optionValueId != null || group?.optionValueSlug) return null;
|
|
4088
|
+
const hintVariantId = group?.listing?.selectionHintVariant;
|
|
4089
|
+
if (hintVariantId == null) return null;
|
|
4090
|
+
return { variantId: hintVariantId };
|
|
4091
|
+
}
|
|
3011
4092
|
function buildProductHref(product, group, options = {}) {
|
|
3012
4093
|
const path = joinProductPath(
|
|
3013
4094
|
options.basePath ?? "/products",
|
|
@@ -3016,23 +4097,46 @@ function buildProductHref(product, group, options = {}) {
|
|
|
3016
4097
|
);
|
|
3017
4098
|
const params = new URLSearchParams();
|
|
3018
4099
|
if (options.detail && options.selection) {
|
|
3019
|
-
const selection = stringifyProductSelection(
|
|
4100
|
+
const selection = stringifyProductSelection(
|
|
4101
|
+
options.detail,
|
|
4102
|
+
options.selection,
|
|
4103
|
+
getProductHrefCodecOptions(options)
|
|
4104
|
+
);
|
|
3020
4105
|
return selection ? `${path}?${selection}` : path;
|
|
3021
4106
|
}
|
|
4107
|
+
const preferVariantSelection = getPreferCompleteVariantFromHintSelection(
|
|
4108
|
+
group,
|
|
4109
|
+
options
|
|
4110
|
+
);
|
|
4111
|
+
if (preferVariantSelection) {
|
|
4112
|
+
if (options.detail) {
|
|
4113
|
+
const selection = stringifyProductSelection(
|
|
4114
|
+
options.detail,
|
|
4115
|
+
preferVariantSelection,
|
|
4116
|
+
getProductHrefCodecOptions(options)
|
|
4117
|
+
);
|
|
4118
|
+
return selection ? `${path}?${selection}` : path;
|
|
4119
|
+
}
|
|
4120
|
+
if (preferVariantSelection.variantId != null) {
|
|
4121
|
+
params.set("variant", String(preferVariantSelection.variantId));
|
|
4122
|
+
return `${path}?${params.toString()}`;
|
|
4123
|
+
}
|
|
4124
|
+
}
|
|
3022
4125
|
const groupSelection = getProductHrefGroupSelection(group, options.matrix);
|
|
3023
4126
|
if (groupSelection) {
|
|
3024
4127
|
if (options.detail) {
|
|
3025
|
-
const selection = stringifyProductSelection(
|
|
4128
|
+
const selection = stringifyProductSelection(
|
|
4129
|
+
options.detail,
|
|
4130
|
+
groupSelection,
|
|
4131
|
+
getProductHrefCodecOptions(options)
|
|
4132
|
+
);
|
|
3026
4133
|
return selection ? `${path}?${selection}` : path;
|
|
3027
4134
|
}
|
|
3028
4135
|
if (groupSelection.variantId != null) {
|
|
3029
4136
|
params.set("variant", String(groupSelection.variantId));
|
|
3030
4137
|
return `${path}?${params.toString()}`;
|
|
3031
4138
|
}
|
|
3032
|
-
|
|
3033
|
-
if (selectionEntry) {
|
|
3034
|
-
const [optionId, valueId] = selectionEntry;
|
|
3035
|
-
params.set(`opt.${optionId}`, String(valueId));
|
|
4139
|
+
if (appendGroupByOptionIdHrefParams(params, group, groupSelection, options)) {
|
|
3036
4140
|
return `${path}?${params.toString()}`;
|
|
3037
4141
|
}
|
|
3038
4142
|
}
|
|
@@ -3059,6 +4163,26 @@ function isVariantAvailableForSale(variant) {
|
|
|
3059
4163
|
if (variant.isUnlimited) return true;
|
|
3060
4164
|
return (variant.stock ?? 0) - (variant.reservedStock ?? 0) > 0;
|
|
3061
4165
|
}
|
|
4166
|
+
function sortVariantsForMediaSelection(variants) {
|
|
4167
|
+
const orderedVariants = [...variants].sort(compareVariantOrder);
|
|
4168
|
+
const activeVariants = orderedVariants.filter(
|
|
4169
|
+
(variant) => variant.isActive !== false
|
|
4170
|
+
);
|
|
4171
|
+
const availableVariants = activeVariants.filter(isVariantAvailableForSale);
|
|
4172
|
+
const unavailableActiveVariants = activeVariants.filter(
|
|
4173
|
+
(variant) => !isVariantAvailableForSale(variant)
|
|
4174
|
+
);
|
|
4175
|
+
return [...availableVariants, ...unavailableActiveVariants];
|
|
4176
|
+
}
|
|
4177
|
+
function variantHasPoolMedia(variant, productMediaPool) {
|
|
4178
|
+
if (!variant?.images?.length) return false;
|
|
4179
|
+
return variant.images.some((image) => {
|
|
4180
|
+
const imageId = getRelationID(image);
|
|
4181
|
+
return Boolean(
|
|
4182
|
+
imageId && productMediaPool.some((poolItem) => getRelationID(poolItem) === imageId)
|
|
4183
|
+
);
|
|
4184
|
+
});
|
|
4185
|
+
}
|
|
3062
4186
|
function getMinMax(values) {
|
|
3063
4187
|
const numbers = values.filter(
|
|
3064
4188
|
(value) => typeof value === "number"
|
|
@@ -3084,11 +4208,28 @@ function buildProductListingProjection(product, variants) {
|
|
|
3084
4208
|
const { min: minCompareAtPrice, max: maxCompareAtPrice } = getMinMax(
|
|
3085
4209
|
activeVariants.map((variant) => variant.compareAtPrice)
|
|
3086
4210
|
);
|
|
3087
|
-
const
|
|
3088
|
-
const
|
|
4211
|
+
const productMediaPool = product?.images ?? [];
|
|
4212
|
+
const selectionHintHasPoolMedia = variantHasPoolMedia(
|
|
4213
|
+
selectionHintVariant,
|
|
4214
|
+
productMediaPool
|
|
4215
|
+
);
|
|
4216
|
+
const mediaSelectionVariants = sortVariantsForMediaSelection(variants);
|
|
4217
|
+
const resolvedProductMedia = resolveProductSelectionMedia({
|
|
4218
|
+
productMediaPool,
|
|
4219
|
+
productPrimaryMediaItemId: getRelationID(product?.primaryMediaItemId),
|
|
4220
|
+
selectedVariant: selectionHintVariant && selectionHintHasPoolMedia ? {
|
|
4221
|
+
id: selectionHintVariant.id,
|
|
4222
|
+
images: selectionHintVariant.images
|
|
4223
|
+
} : null,
|
|
4224
|
+
matchingVariants: selectionHintHasPoolMedia ? [] : mediaSelectionVariants
|
|
4225
|
+
});
|
|
3089
4226
|
return {
|
|
3090
4227
|
selectionHintVariant: getRelationID(selectionHintVariant?.id) ?? null,
|
|
3091
|
-
primaryImage:
|
|
4228
|
+
primaryImage: resolveGenericListingPrimaryImage(
|
|
4229
|
+
product,
|
|
4230
|
+
resolvedProductMedia.primaryImage,
|
|
4231
|
+
resolvedProductMedia.source
|
|
4232
|
+
),
|
|
3092
4233
|
minPrice,
|
|
3093
4234
|
maxPrice,
|
|
3094
4235
|
minCompareAtPrice,
|
|
@@ -3100,7 +4241,40 @@ function buildProductListingProjection(product, variants) {
|
|
|
3100
4241
|
function buildProductListingCard(item, options = {}) {
|
|
3101
4242
|
const product = item.product;
|
|
3102
4243
|
const groups = item.groups;
|
|
3103
|
-
const
|
|
4244
|
+
const variants = getProductListingCardVariants(item);
|
|
4245
|
+
const projectedListing = buildProductListingProjection(product, variants);
|
|
4246
|
+
const selectionHintVariant = getRelationID(product.listing?.selectionHintVariant) ?? projectedListing.selectionHintVariant;
|
|
4247
|
+
const representativeVariant = findListingCardRepresentativeVariant(
|
|
4248
|
+
variants,
|
|
4249
|
+
selectionHintVariant
|
|
4250
|
+
);
|
|
4251
|
+
const productMediaPool = mediaArray2(product.images);
|
|
4252
|
+
const representativeHasPoolMedia = Boolean(
|
|
4253
|
+
representativeVariant?.images?.some(
|
|
4254
|
+
(image) => productMediaPool.some(
|
|
4255
|
+
(poolItem) => getRelationID(poolItem) === getRelationID(image)
|
|
4256
|
+
)
|
|
4257
|
+
)
|
|
4258
|
+
);
|
|
4259
|
+
const resolvedPrimaryMedia = resolveProductSelectionMedia(
|
|
4260
|
+
{
|
|
4261
|
+
productMediaPool,
|
|
4262
|
+
productPrimaryMediaItemId: getRelationID(product.primaryMediaItemId),
|
|
4263
|
+
selectedVariant: representativeHasPoolMedia ? representativeVariant : null,
|
|
4264
|
+
matchingVariants: representativeHasPoolMedia ? [] : variants
|
|
4265
|
+
}
|
|
4266
|
+
);
|
|
4267
|
+
const listingPrimaryPointer = resolveListingPrimaryImagePointer({
|
|
4268
|
+
productMediaPool,
|
|
4269
|
+
productPrimaryMediaItemId: getRelationID(product.primaryMediaItemId),
|
|
4270
|
+
productThumbnail: product.thumbnail ?? null,
|
|
4271
|
+
listingPrimaryImage: product.listing?.primaryImage ?? null,
|
|
4272
|
+
resolvedPrimary: resolvedPrimaryMedia.primaryImage,
|
|
4273
|
+
resolvedSource: resolvedPrimaryMedia.source
|
|
4274
|
+
});
|
|
4275
|
+
const listingPrimaryMedia = listingPrimaryPointer ? productMediaPool.find(
|
|
4276
|
+
(item2) => getRelationID(item2) === listingPrimaryPointer
|
|
4277
|
+
) ?? resolvedPrimaryMedia.primaryImage ?? null : null;
|
|
3104
4278
|
const priceRange = aggregateListingPriceRange(groups);
|
|
3105
4279
|
const availableForSale = groups.some(
|
|
3106
4280
|
(group) => group.listing.availableForSale
|
|
@@ -3108,14 +4282,42 @@ function buildProductListingCard(item, options = {}) {
|
|
|
3108
4282
|
const swatches = groups.length > 1 ? groups.map((group) => buildListingSwatch(product, group, options)) : [];
|
|
3109
4283
|
return {
|
|
3110
4284
|
id: String(product.id),
|
|
3111
|
-
href: buildProductHref(
|
|
4285
|
+
href: buildProductHref(
|
|
4286
|
+
{ slug: product.slug },
|
|
4287
|
+
{ listing: { selectionHintVariant } },
|
|
4288
|
+
options
|
|
4289
|
+
),
|
|
3112
4290
|
title: product.title,
|
|
3113
|
-
|
|
4291
|
+
representativeVariant,
|
|
4292
|
+
primaryImage: listingPrimaryMedia,
|
|
3114
4293
|
priceRange,
|
|
3115
4294
|
availableForSale,
|
|
3116
4295
|
swatches
|
|
3117
4296
|
};
|
|
3118
4297
|
}
|
|
4298
|
+
function getProductListingCardVariants(item) {
|
|
4299
|
+
const productVariants = item.product.variants?.docs;
|
|
4300
|
+
if (Array.isArray(productVariants) && productVariants.length > 0) {
|
|
4301
|
+
return productVariants;
|
|
4302
|
+
}
|
|
4303
|
+
const variants = [];
|
|
4304
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4305
|
+
for (const group of item.groups) {
|
|
4306
|
+
for (const variant of group.variants) {
|
|
4307
|
+
const id = getRelationID(variant.id);
|
|
4308
|
+
if (id && seen.has(id)) continue;
|
|
4309
|
+
if (id) seen.add(id);
|
|
4310
|
+
variants.push(variant);
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
return variants;
|
|
4314
|
+
}
|
|
4315
|
+
function findListingCardRepresentativeVariant(variants, representativeVariantId) {
|
|
4316
|
+
if (representativeVariantId == null) return null;
|
|
4317
|
+
return variants.find(
|
|
4318
|
+
(variant) => getRelationID(variant.id) === representativeVariantId
|
|
4319
|
+
) ?? null;
|
|
4320
|
+
}
|
|
3119
4321
|
function aggregateListingPriceRange(groups) {
|
|
3120
4322
|
const minPrice = minOfNullable(groups.map((g) => g.listing.minPrice));
|
|
3121
4323
|
const maxPrice = maxOfNullable(groups.map((g) => g.listing.maxPrice));
|
|
@@ -3135,17 +4337,16 @@ function aggregateListingPriceRange(groups) {
|
|
|
3135
4337
|
};
|
|
3136
4338
|
}
|
|
3137
4339
|
function buildListingSwatch(product, group, options) {
|
|
3138
|
-
const thumbnail = firstMedia(group.optionValueThumbnail) ?? firstMedia(group.optionValueImages) ?? null;
|
|
3139
4340
|
return {
|
|
3140
4341
|
optionId: group.optionId,
|
|
3141
4342
|
optionValueId: group.optionValueId,
|
|
3142
4343
|
label: group.optionValueLabel,
|
|
3143
|
-
|
|
3144
|
-
thumbnail,
|
|
4344
|
+
swatch: group.optionValueSwatch ?? null,
|
|
3145
4345
|
href: buildProductHref(
|
|
3146
4346
|
{ slug: product.slug },
|
|
3147
4347
|
{
|
|
3148
4348
|
optionId: group.optionId,
|
|
4349
|
+
optionSlug: group.optionSlug,
|
|
3149
4350
|
optionValueId: group.optionValueId,
|
|
3150
4351
|
optionValueSlug: group.optionValueSlug,
|
|
3151
4352
|
listing: group.listing
|
|
@@ -3180,19 +4381,34 @@ function buildProductListingGroupsByOption(args) {
|
|
|
3180
4381
|
return [];
|
|
3181
4382
|
}
|
|
3182
4383
|
const listingBase = buildProductListingProjection(void 0, variants);
|
|
3183
|
-
const
|
|
3184
|
-
const
|
|
3185
|
-
|
|
4384
|
+
const orderedVariants = sortVariantsForMediaSelection(variants);
|
|
4385
|
+
const productMediaPool = (args.product?.images ?? []).filter(
|
|
4386
|
+
(image) => image != null && typeof image === "object"
|
|
4387
|
+
);
|
|
4388
|
+
const resolvedMedia = resolveProductSelectionMedia({
|
|
4389
|
+
productMediaPool,
|
|
4390
|
+
productPrimaryMediaItemId: extractEntityId(
|
|
4391
|
+
args.product?.primaryMediaItemId ?? null
|
|
4392
|
+
),
|
|
4393
|
+
selectedVariant: null,
|
|
4394
|
+
matchingVariants: orderedVariants,
|
|
4395
|
+
selectedOptionValues: [
|
|
4396
|
+
{
|
|
4397
|
+
id: value.id,
|
|
4398
|
+
swatch: normalizeMatrixSwatch(value.swatch)
|
|
4399
|
+
}
|
|
4400
|
+
]
|
|
4401
|
+
});
|
|
4402
|
+
const groupPrimaryImage = extractEntityId(resolvedMedia.primaryImage) ?? getFirstAvailableVariantPrimaryImage(variants) ?? extractEntityId(args.product?.thumbnail) ?? getFirstMediaId(args.product?.images);
|
|
3186
4403
|
return [
|
|
3187
4404
|
{
|
|
3188
4405
|
optionId: primaryOption.id,
|
|
3189
4406
|
optionTitle: primaryOption.title,
|
|
4407
|
+
optionSlug: primaryOption.slug,
|
|
3190
4408
|
optionValueId: value.id,
|
|
3191
4409
|
optionValueLabel: value.label,
|
|
3192
4410
|
optionValueSlug: value.slug,
|
|
3193
|
-
|
|
3194
|
-
optionValueThumbnail: value.thumbnail ?? null,
|
|
3195
|
-
optionValueImages: value.images ?? null,
|
|
4411
|
+
optionValueSwatch: value.swatch ?? null,
|
|
3196
4412
|
variantIds: variants.map((variant) => String(variant.id)),
|
|
3197
4413
|
variantCount: variants.length,
|
|
3198
4414
|
variants,
|
|
@@ -3205,6 +4421,162 @@ function buildProductListingGroupsByOption(args) {
|
|
|
3205
4421
|
});
|
|
3206
4422
|
}
|
|
3207
4423
|
|
|
4424
|
+
// src/utils/product-media.ts
|
|
4425
|
+
function mediaPointerOptionValues(values) {
|
|
4426
|
+
return (values ?? []).filter((value) => value != null && value !== "").map((value) => ({
|
|
4427
|
+
swatch: {
|
|
4428
|
+
type: "media",
|
|
4429
|
+
mediaItemId: value
|
|
4430
|
+
}
|
|
4431
|
+
}));
|
|
4432
|
+
}
|
|
4433
|
+
function resolveProductMedia(input) {
|
|
4434
|
+
const resolved = resolveProductSelectionMedia({
|
|
4435
|
+
...input,
|
|
4436
|
+
selectedVariant: input.selectedVariant ?? (input.variantFeaturedMediaItemId ? { images: [input.variantFeaturedMediaItemId] } : null),
|
|
4437
|
+
selectedOptionValues: input.selectedOptionValues ?? mediaPointerOptionValues(input.selectedOptionValueMediaItemIds)
|
|
4438
|
+
});
|
|
4439
|
+
if (resolved.source !== "none") {
|
|
4440
|
+
return {
|
|
4441
|
+
...resolved,
|
|
4442
|
+
source: resolved.source === "variant_media_selected" ? "variant_featured" : resolved.source
|
|
4443
|
+
};
|
|
4444
|
+
}
|
|
4445
|
+
const display = resolveProductDisplayMedia({
|
|
4446
|
+
productMediaPool: input.productMediaPool,
|
|
4447
|
+
productPrimaryMediaItemId: input.productPrimaryMediaItemId
|
|
4448
|
+
});
|
|
4449
|
+
return {
|
|
4450
|
+
primaryImage: display.primaryImage,
|
|
4451
|
+
images: display.images,
|
|
4452
|
+
source: display.source
|
|
4453
|
+
};
|
|
4454
|
+
}
|
|
4455
|
+
|
|
4456
|
+
// src/utils/product-gallery.ts
|
|
4457
|
+
function mediaId(value) {
|
|
4458
|
+
if (typeof value === "string" || typeof value === "number") {
|
|
4459
|
+
return String(value);
|
|
4460
|
+
}
|
|
4461
|
+
if (typeof value === "object" && value !== null && "id" in value) {
|
|
4462
|
+
const id = value.id;
|
|
4463
|
+
if (typeof id === "string" || typeof id === "number") return String(id);
|
|
4464
|
+
}
|
|
4465
|
+
return null;
|
|
4466
|
+
}
|
|
4467
|
+
function relationshipIds(values) {
|
|
4468
|
+
if (!Array.isArray(values)) return [];
|
|
4469
|
+
return values.map((value) => mediaId(value)).filter((value) => Boolean(value));
|
|
4470
|
+
}
|
|
4471
|
+
function buildPoolById2(pool) {
|
|
4472
|
+
const poolById = /* @__PURE__ */ new Map();
|
|
4473
|
+
for (const item of pool) {
|
|
4474
|
+
const id = mediaId(item);
|
|
4475
|
+
if (id) poolById.set(id, item);
|
|
4476
|
+
}
|
|
4477
|
+
return poolById;
|
|
4478
|
+
}
|
|
4479
|
+
function mediaSetItemsInPool(mediaSet, poolById) {
|
|
4480
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4481
|
+
const items = [];
|
|
4482
|
+
const primaryId = mediaId(mediaSet.primaryMediaItemId);
|
|
4483
|
+
if (primaryId) {
|
|
4484
|
+
const primary = poolById.get(primaryId);
|
|
4485
|
+
if (primary) {
|
|
4486
|
+
seen.add(primaryId);
|
|
4487
|
+
items.push(primary);
|
|
4488
|
+
}
|
|
4489
|
+
}
|
|
4490
|
+
for (const itemId of relationshipIds(mediaSet.items)) {
|
|
4491
|
+
if (seen.has(itemId)) continue;
|
|
4492
|
+
const item = poolById.get(itemId);
|
|
4493
|
+
if (!item) continue;
|
|
4494
|
+
seen.add(itemId);
|
|
4495
|
+
items.push(item);
|
|
4496
|
+
}
|
|
4497
|
+
return items;
|
|
4498
|
+
}
|
|
4499
|
+
function resolveLegacyMediaSet(input) {
|
|
4500
|
+
if (!Array.isArray(input.mediaSets)) return null;
|
|
4501
|
+
const pool = input.productMediaPool ?? input.pool ?? [];
|
|
4502
|
+
const poolById = buildPoolById2(pool);
|
|
4503
|
+
const selectedVariantId = mediaId(input.selectedVariantId);
|
|
4504
|
+
if (selectedVariantId) {
|
|
4505
|
+
const exactSet = input.mediaSets.find(
|
|
4506
|
+
(set) => set.matchType === "exact-variant" && relationshipIds(set.variantIds).includes(selectedVariantId)
|
|
4507
|
+
);
|
|
4508
|
+
if (exactSet) {
|
|
4509
|
+
const images = mediaSetItemsInPool(exactSet, poolById);
|
|
4510
|
+
if (images.length > 0) {
|
|
4511
|
+
return {
|
|
4512
|
+
primaryImage: images[0] ?? null,
|
|
4513
|
+
images,
|
|
4514
|
+
source: "exact_variant_set"
|
|
4515
|
+
};
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
}
|
|
4519
|
+
const selectedOptionValueIds = new Set(
|
|
4520
|
+
relationshipIds(input.selectedOptionValueIds)
|
|
4521
|
+
);
|
|
4522
|
+
const optionSetMatches = input.mediaSets.filter((set) => set.matchType === "option-values").map((set) => {
|
|
4523
|
+
const optionValueIds = relationshipIds(set.optionValueIds);
|
|
4524
|
+
if (optionValueIds.length === 0) return null;
|
|
4525
|
+
if (!optionValueIds.every((id) => selectedOptionValueIds.has(id))) {
|
|
4526
|
+
return null;
|
|
4527
|
+
}
|
|
4528
|
+
const images = mediaSetItemsInPool(set, poolById);
|
|
4529
|
+
if (images.length === 0) return null;
|
|
4530
|
+
return { optionValueCount: optionValueIds.length, images };
|
|
4531
|
+
}).filter(
|
|
4532
|
+
(match) => Boolean(match)
|
|
4533
|
+
).sort((a, b) => b.optionValueCount - a.optionValueCount);
|
|
4534
|
+
if (optionSetMatches.length > 0) {
|
|
4535
|
+
const images = optionSetMatches[0].images;
|
|
4536
|
+
return {
|
|
4537
|
+
primaryImage: images[0] ?? null,
|
|
4538
|
+
images,
|
|
4539
|
+
source: "option_value_set"
|
|
4540
|
+
};
|
|
4541
|
+
}
|
|
4542
|
+
const defaultSet = input.mediaSets.find((set) => set.matchType === "default");
|
|
4543
|
+
if (defaultSet) {
|
|
4544
|
+
const images = mediaSetItemsInPool(defaultSet, poolById);
|
|
4545
|
+
if (images.length > 0) {
|
|
4546
|
+
return {
|
|
4547
|
+
primaryImage: images[0] ?? null,
|
|
4548
|
+
images,
|
|
4549
|
+
source: "default_set"
|
|
4550
|
+
};
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
return {
|
|
4554
|
+
primaryImage: null,
|
|
4555
|
+
images: [],
|
|
4556
|
+
source: "none"
|
|
4557
|
+
};
|
|
4558
|
+
}
|
|
4559
|
+
function resolveProductGallery(input) {
|
|
4560
|
+
const legacyResolved = resolveLegacyMediaSet(input);
|
|
4561
|
+
if (legacyResolved && legacyResolved.source !== "none") return legacyResolved;
|
|
4562
|
+
const selectedVariantId = mediaId(input.selectedVariantId);
|
|
4563
|
+
const selectedOptionValueIds = relationshipIds(input.selectedOptionValueIds);
|
|
4564
|
+
const selection = resolveProductSelectionMedia({
|
|
4565
|
+
...input,
|
|
4566
|
+
productMediaPool: input.productMediaPool ?? input.pool ?? null,
|
|
4567
|
+
selectedVariant: input.selectedVariant ?? (selectedVariantId ? { id: selectedVariantId } : null),
|
|
4568
|
+
selectedOptionValues: input.selectedOptionValues ?? selectedOptionValueIds.map((id) => ({ id }))
|
|
4569
|
+
});
|
|
4570
|
+
if (selection.source !== "none") {
|
|
4571
|
+
return selection;
|
|
4572
|
+
}
|
|
4573
|
+
return resolveProductDisplayMedia({
|
|
4574
|
+
productMediaPool: input.productMediaPool ?? input.pool ?? null,
|
|
4575
|
+
productPrimaryMediaItemId: input.productPrimaryMediaItemId,
|
|
4576
|
+
listingPrimaryImage: input.listingPrimaryImage
|
|
4577
|
+
});
|
|
4578
|
+
}
|
|
4579
|
+
|
|
3208
4580
|
// src/utils/image.ts
|
|
3209
4581
|
var IMAGE_SIZES = [384, 768, 1536];
|
|
3210
4582
|
function getImageUrl(image, displayWidth, dpr = 1) {
|
|
@@ -3499,6 +4871,9 @@ export {
|
|
|
3499
4871
|
AuthError,
|
|
3500
4872
|
BaseApi,
|
|
3501
4873
|
COLLECTIONS,
|
|
4874
|
+
COMMERCE_NOTIFICATION_EVENTS,
|
|
4875
|
+
COMMERCE_NOTIFICATION_EVENT_TYPE,
|
|
4876
|
+
COMMERCE_NOTIFICATION_OPERATION,
|
|
3502
4877
|
CUSTOMER_PASSWORD_RESET_OPERATION,
|
|
3503
4878
|
CartApi,
|
|
3504
4879
|
CommerceClient,
|
|
@@ -3508,6 +4883,7 @@ export {
|
|
|
3508
4883
|
CustomerAuth,
|
|
3509
4884
|
CustomerNamespace,
|
|
3510
4885
|
DiscountApi,
|
|
4886
|
+
EventsClient,
|
|
3511
4887
|
GoneError,
|
|
3512
4888
|
IMAGE_SIZES,
|
|
3513
4889
|
INTERNAL_COLLECTIONS,
|
|
@@ -3515,6 +4891,8 @@ export {
|
|
|
3515
4891
|
NotFoundError,
|
|
3516
4892
|
ORDER_CHANGED_EVENT_TYPE,
|
|
3517
4893
|
OrderApi,
|
|
4894
|
+
PRODUCT_UPSERT_READONLY_FIELD_REASON,
|
|
4895
|
+
PRODUCT_UPSERT_UNKNOWN_FIELD_REASON,
|
|
3518
4896
|
PermissionError,
|
|
3519
4897
|
ProductApi,
|
|
3520
4898
|
ProductSelectionCodecError,
|
|
@@ -3537,6 +4915,7 @@ export {
|
|
|
3537
4915
|
createAnalytics,
|
|
3538
4916
|
createAuthError,
|
|
3539
4917
|
createClient2 as createClient,
|
|
4918
|
+
createCommerceEmailWebhookHandler,
|
|
3540
4919
|
createConflictError,
|
|
3541
4920
|
createCustomerAuthWebhookHandler,
|
|
3542
4921
|
createNotFoundError,
|
|
@@ -3544,9 +4923,11 @@ export {
|
|
|
3544
4923
|
createProductSelectionCodec,
|
|
3545
4924
|
createRateLimitError,
|
|
3546
4925
|
createTypedWebhookHandler,
|
|
4926
|
+
defineCommerceEmailConfig,
|
|
3547
4927
|
formatOrderName,
|
|
3548
4928
|
generateOrderNumber,
|
|
3549
4929
|
getAvailableOptionValues,
|
|
4930
|
+
getCommerceNotificationIdempotencyKey,
|
|
3550
4931
|
getImageLqip,
|
|
3551
4932
|
getImagePalette,
|
|
3552
4933
|
getImagePlaceholderStyle,
|
|
@@ -3562,6 +4943,7 @@ export {
|
|
|
3562
4943
|
handleWebhook,
|
|
3563
4944
|
isApiError,
|
|
3564
4945
|
isAuthError,
|
|
4946
|
+
isCommerceNotificationWebhookEvent,
|
|
3565
4947
|
isConfigError,
|
|
3566
4948
|
isConflictError,
|
|
3567
4949
|
isCustomerPasswordResetWebhookEvent,
|
|
@@ -3570,6 +4952,7 @@ export {
|
|
|
3570
4952
|
isNotFoundError,
|
|
3571
4953
|
isOrderChangedWebhookEvent,
|
|
3572
4954
|
isPermissionError,
|
|
4955
|
+
isProductUpsertFieldValidationErrorBody,
|
|
3573
4956
|
isRateLimitError,
|
|
3574
4957
|
isSDKError,
|
|
3575
4958
|
isServiceUnavailableError,
|
|
@@ -3579,14 +4962,22 @@ export {
|
|
|
3579
4962
|
isValidationError,
|
|
3580
4963
|
isWebhookCollection,
|
|
3581
4964
|
isWebhookOperation,
|
|
4965
|
+
mergeProductDetailWithStock,
|
|
3582
4966
|
normalizeProductSelection,
|
|
3583
4967
|
normalizeProductSelectionFromMatrix,
|
|
3584
4968
|
normalizeSelectedValueIds,
|
|
3585
4969
|
parseProductSelection,
|
|
4970
|
+
resolveListingPrimaryImagePointer,
|
|
4971
|
+
resolveProductDisplayMedia,
|
|
4972
|
+
resolveProductGallery,
|
|
4973
|
+
resolveProductMedia,
|
|
3586
4974
|
resolveProductSelection,
|
|
3587
4975
|
resolveProductSelectionFromMatrix,
|
|
4976
|
+
resolveProductSelectionMedia,
|
|
3588
4977
|
resolveRelation,
|
|
3589
4978
|
resolveVariantForSelection,
|
|
4979
|
+
selectNext,
|
|
4980
|
+
selectedSwatchMediaItemId,
|
|
3590
4981
|
stringifyProductSelection
|
|
3591
4982
|
};
|
|
3592
4983
|
//# sourceMappingURL=index.js.map
|