@01.software/sdk 0.28.0 → 0.29.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/dist/{const-mdQQtIOz.d.ts → const-DAjQYNuM.d.ts} +1 -1
- package/dist/{const-Cz9Ki_I7.d.cts → const-Dsixdi6z.d.cts} +1 -1
- package/dist/index.cjs +546 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -7
- package/dist/index.d.ts +7 -7
- package/dist/index.js +546 -10
- package/dist/index.js.map +1 -1
- package/dist/{payload-types-BrSYb-sh.d.cts → payload-types-Ci-ZA7aM.d.cts} +8 -3
- package/dist/{payload-types-BrSYb-sh.d.ts → payload-types-Ci-ZA7aM.d.ts} +8 -3
- package/dist/realtime.d.cts +2 -2
- package/dist/realtime.d.ts +2 -2
- package/dist/{server-D369bCVJ.d.cts → server-C0C8dtms.d.cts} +113 -13
- package/dist/{server-C2Q9R-Lu.d.ts → server-Cv0Q4dPQ.d.ts} +113 -13
- package/dist/server.cjs +1 -4
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +4 -4
- package/dist/server.d.ts +4 -4
- package/dist/server.js +1 -4
- package/dist/server.js.map +1 -1
- package/dist/{types-BLUb4cYq.d.ts → types-BWq_WlbB.d.ts} +1 -1
- package/dist/{types-CW4PaIL7.d.cts → types-zKjATmDK.d.cts} +1 -1
- package/dist/ui/form.d.cts +1 -1
- package/dist/ui/form.d.ts +1 -1
- package/dist/ui/video.d.cts +1 -1
- package/dist/ui/video.d.ts +1 -1
- package/dist/webhook.d.cts +3 -3
- package/dist/webhook.d.ts +3 -3
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,7 @@ __export(src_exports, {
|
|
|
47
47
|
OrderApi: () => OrderApi,
|
|
48
48
|
PermissionError: () => PermissionError,
|
|
49
49
|
ProductApi: () => ProductApi,
|
|
50
|
+
ProductSelectionCodecError: () => ProductSelectionCodecError,
|
|
50
51
|
QueryHooks: () => QueryHooks,
|
|
51
52
|
RateLimitError: () => RateLimitError,
|
|
52
53
|
ReadOnlyCollectionClient: () => ReadOnlyCollectionClient,
|
|
@@ -66,6 +67,7 @@ __export(src_exports, {
|
|
|
66
67
|
buildProductListingGroupsByOption: () => buildProductListingGroupsByOption,
|
|
67
68
|
buildProductListingProjection: () => buildProductListingProjection,
|
|
68
69
|
buildProductOptionMatrix: () => buildProductOptionMatrix,
|
|
70
|
+
buildProductOptionMatrixFromDetail: () => buildProductOptionMatrixFromDetail,
|
|
69
71
|
collectionKeys: () => collectionKeys,
|
|
70
72
|
createAnalytics: () => createAnalytics,
|
|
71
73
|
createAuthError: () => createAuthError,
|
|
@@ -74,6 +76,7 @@ __export(src_exports, {
|
|
|
74
76
|
createCustomerAuthWebhookHandler: () => createCustomerAuthWebhookHandler,
|
|
75
77
|
createNotFoundError: () => createNotFoundError,
|
|
76
78
|
createPermissionError: () => createPermissionError,
|
|
79
|
+
createProductSelectionCodec: () => createProductSelectionCodec,
|
|
77
80
|
createRateLimitError: () => createRateLimitError,
|
|
78
81
|
createServerClient: () => createServerClient,
|
|
79
82
|
createTypedWebhookHandler: () => createTypedWebhookHandler,
|
|
@@ -110,10 +113,14 @@ __export(src_exports, {
|
|
|
110
113
|
isUsageLimitError: () => isUsageLimitError,
|
|
111
114
|
isValidWebhookEvent: () => isValidWebhookEvent,
|
|
112
115
|
isValidationError: () => isValidationError,
|
|
116
|
+
normalizeProductSelection: () => normalizeProductSelection,
|
|
113
117
|
normalizeSelectedValueIds: () => normalizeSelectedValueIds,
|
|
118
|
+
parseProductSelection: () => parseProductSelection,
|
|
114
119
|
productKeys: () => productKeys,
|
|
120
|
+
resolveProductSelection: () => resolveProductSelection,
|
|
115
121
|
resolveRelation: () => resolveRelation,
|
|
116
|
-
resolveVariantForSelection: () => resolveVariantForSelection
|
|
122
|
+
resolveVariantForSelection: () => resolveVariantForSelection,
|
|
123
|
+
stringifyProductSelection: () => stringifyProductSelection
|
|
117
124
|
});
|
|
118
125
|
module.exports = __toCommonJS(src_exports);
|
|
119
126
|
|
|
@@ -2043,10 +2050,7 @@ var ProductApi = class extends BaseApi {
|
|
|
2043
2050
|
* `product-upsert` tool.
|
|
2044
2051
|
*/
|
|
2045
2052
|
upsert(params) {
|
|
2046
|
-
return this.request(
|
|
2047
|
-
"/api/products/upsert",
|
|
2048
|
-
params
|
|
2049
|
-
);
|
|
2053
|
+
return this.request("/api/products/upsert", params);
|
|
2050
2054
|
}
|
|
2051
2055
|
};
|
|
2052
2056
|
|
|
@@ -3129,6 +3133,13 @@ function createTypedWebhookHandler(collection, handler) {
|
|
|
3129
3133
|
}
|
|
3130
3134
|
|
|
3131
3135
|
// src/utils/ecommerce.ts
|
|
3136
|
+
var ProductSelectionCodecError = class extends Error {
|
|
3137
|
+
constructor(message) {
|
|
3138
|
+
super(message);
|
|
3139
|
+
this.code = "ambiguous_product_selection_query";
|
|
3140
|
+
this.name = "ProductSelectionCodecError";
|
|
3141
|
+
}
|
|
3142
|
+
};
|
|
3132
3143
|
function getRelationID(value) {
|
|
3133
3144
|
if (typeof value === "string") return value;
|
|
3134
3145
|
if (typeof value === "number") return String(value);
|
|
@@ -3175,10 +3186,11 @@ function getFirstAvailableVariantPrimaryImage(variants) {
|
|
|
3175
3186
|
}
|
|
3176
3187
|
return null;
|
|
3177
3188
|
}
|
|
3178
|
-
function normalizeOptionValue(value, fallbackOptionId) {
|
|
3189
|
+
function normalizeOptionValue(value, fallbackOptionId, fallbackOptionSlug) {
|
|
3179
3190
|
return {
|
|
3180
3191
|
id: String(value.id),
|
|
3181
3192
|
optionId: getRelationID(value.option) ?? fallbackOptionId,
|
|
3193
|
+
optionSlug: fallbackOptionSlug,
|
|
3182
3194
|
label: value.value || value.slug || String(value.id),
|
|
3183
3195
|
slug: value.slug ?? null,
|
|
3184
3196
|
swatchColor: value.swatchColor ?? null,
|
|
@@ -3187,20 +3199,26 @@ function normalizeOptionValue(value, fallbackOptionId) {
|
|
|
3187
3199
|
order: value._order ?? value["_product-option-values_values_order"] ?? ""
|
|
3188
3200
|
};
|
|
3189
3201
|
}
|
|
3190
|
-
function normalizeVariantOptionValues(variant, valueToOptionId, optionIds) {
|
|
3202
|
+
function normalizeVariantOptionValues(variant, optionById, valueToOptionId, optionIds) {
|
|
3191
3203
|
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
3204
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
3192
3205
|
for (const rawValue of Array.isArray(variant.optionValues) ? variant.optionValues : []) {
|
|
3193
3206
|
const valueId = getRelationID(rawValue);
|
|
3194
3207
|
if (!valueId) continue;
|
|
3195
3208
|
const optionId = valueToOptionId.get(valueId) ?? (isProductOptionValueDoc(rawValue) ? getRelationID(rawValue.option) : void 0);
|
|
3196
3209
|
if (!optionId || optionValueByOptionId.has(optionId)) continue;
|
|
3197
3210
|
optionValueByOptionId.set(optionId, valueId);
|
|
3211
|
+
const optionSlug = optionById.get(optionId)?.slug;
|
|
3212
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
3213
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
3214
|
+
}
|
|
3198
3215
|
}
|
|
3199
3216
|
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
3200
3217
|
return {
|
|
3201
3218
|
id: String(variant.id),
|
|
3202
3219
|
optionValueIds,
|
|
3203
3220
|
optionValueByOptionId,
|
|
3221
|
+
optionValueByOptionSlug,
|
|
3204
3222
|
source: variant
|
|
3205
3223
|
};
|
|
3206
3224
|
}
|
|
@@ -3210,17 +3228,20 @@ function buildProductOptionMatrix({
|
|
|
3210
3228
|
}) {
|
|
3211
3229
|
const normalizedOptions = options.map((option) => {
|
|
3212
3230
|
const valuesById = /* @__PURE__ */ new Map();
|
|
3231
|
+
const optionSlug = option.slug ?? String(option.id);
|
|
3213
3232
|
for (const rawValue of option.values?.docs ?? []) {
|
|
3214
3233
|
if (!isProductOptionValueDoc(rawValue)) continue;
|
|
3215
3234
|
const normalizedValue = normalizeOptionValue(
|
|
3216
3235
|
rawValue,
|
|
3217
|
-
String(option.id)
|
|
3236
|
+
String(option.id),
|
|
3237
|
+
optionSlug
|
|
3218
3238
|
);
|
|
3219
3239
|
valuesById.set(normalizedValue.id, normalizedValue);
|
|
3220
3240
|
}
|
|
3221
3241
|
return {
|
|
3222
3242
|
id: String(option.id),
|
|
3223
3243
|
title: option.title ?? String(option.id),
|
|
3244
|
+
slug: optionSlug,
|
|
3224
3245
|
order: option._order ?? option["_product-options_options_order"] ?? "",
|
|
3225
3246
|
values: Array.from(valuesById.values()).sort(
|
|
3226
3247
|
(left, right) => compareOrder(left.order, right.order)
|
|
@@ -3230,24 +3251,113 @@ function buildProductOptionMatrix({
|
|
|
3230
3251
|
const optionById = new Map(
|
|
3231
3252
|
normalizedOptions.map((option) => [option.id, option])
|
|
3232
3253
|
);
|
|
3254
|
+
const optionBySlug = new Map(
|
|
3255
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
3256
|
+
);
|
|
3233
3257
|
const valueById = /* @__PURE__ */ new Map();
|
|
3234
3258
|
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
3259
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
3235
3260
|
for (const option of normalizedOptions) {
|
|
3236
3261
|
for (const value of option.values) {
|
|
3237
3262
|
valueById.set(value.id, value);
|
|
3238
3263
|
valueToOptionId.set(value.id, option.id);
|
|
3264
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
3239
3265
|
}
|
|
3240
3266
|
}
|
|
3241
3267
|
const optionIds = normalizedOptions.map((option) => option.id);
|
|
3268
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
3242
3269
|
const normalizedVariants = variants.map(
|
|
3243
|
-
(variant) => normalizeVariantOptionValues(
|
|
3270
|
+
(variant) => normalizeVariantOptionValues(
|
|
3271
|
+
variant,
|
|
3272
|
+
optionById,
|
|
3273
|
+
valueToOptionId,
|
|
3274
|
+
optionIds
|
|
3275
|
+
)
|
|
3276
|
+
);
|
|
3277
|
+
return {
|
|
3278
|
+
options: normalizedOptions,
|
|
3279
|
+
optionIds,
|
|
3280
|
+
optionSlugs,
|
|
3281
|
+
optionById,
|
|
3282
|
+
optionBySlug,
|
|
3283
|
+
valueById,
|
|
3284
|
+
valueToOptionId,
|
|
3285
|
+
valueToOptionSlug,
|
|
3286
|
+
variants: normalizedVariants
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
3289
|
+
function matrixOrder(index) {
|
|
3290
|
+
return String(index).padStart(6, "0");
|
|
3291
|
+
}
|
|
3292
|
+
function buildProductOptionMatrixFromDetail(detail) {
|
|
3293
|
+
const normalizedOptions = detail.options.map((option, optionIndex) => ({
|
|
3294
|
+
id: String(option.id),
|
|
3295
|
+
title: option.title || String(option.id),
|
|
3296
|
+
slug: option.slug,
|
|
3297
|
+
order: matrixOrder(optionIndex),
|
|
3298
|
+
values: option.values.map((value, valueIndex) => ({
|
|
3299
|
+
id: String(value.id),
|
|
3300
|
+
optionId: String(option.id),
|
|
3301
|
+
optionSlug: option.slug,
|
|
3302
|
+
label: value.value || value.slug || String(value.id),
|
|
3303
|
+
slug: value.slug,
|
|
3304
|
+
swatchColor: value.swatchColor ?? null,
|
|
3305
|
+
thumbnail: value.thumbnail ?? null,
|
|
3306
|
+
images: value.images ?? null,
|
|
3307
|
+
order: matrixOrder(valueIndex)
|
|
3308
|
+
}))
|
|
3309
|
+
}));
|
|
3310
|
+
const optionById = new Map(
|
|
3311
|
+
normalizedOptions.map((option) => [option.id, option])
|
|
3244
3312
|
);
|
|
3313
|
+
const optionBySlug = new Map(
|
|
3314
|
+
normalizedOptions.map((option) => [option.slug, option])
|
|
3315
|
+
);
|
|
3316
|
+
const valueById = /* @__PURE__ */ new Map();
|
|
3317
|
+
const valueToOptionId = /* @__PURE__ */ new Map();
|
|
3318
|
+
const valueToOptionSlug = /* @__PURE__ */ new Map();
|
|
3319
|
+
for (const option of normalizedOptions) {
|
|
3320
|
+
for (const value of option.values) {
|
|
3321
|
+
valueById.set(value.id, value);
|
|
3322
|
+
valueToOptionId.set(value.id, option.id);
|
|
3323
|
+
valueToOptionSlug.set(value.id, option.slug);
|
|
3324
|
+
}
|
|
3325
|
+
}
|
|
3326
|
+
const optionIds = normalizedOptions.map((option) => option.id);
|
|
3327
|
+
const optionSlugs = normalizedOptions.map((option) => option.slug);
|
|
3328
|
+
const normalizedVariants = detail.variants.map((variant) => {
|
|
3329
|
+
const optionValueByOptionId = /* @__PURE__ */ new Map();
|
|
3330
|
+
const optionValueByOptionSlug = /* @__PURE__ */ new Map();
|
|
3331
|
+
for (const rawValue of variant.optionValues) {
|
|
3332
|
+
const optionId = String(rawValue.optionId);
|
|
3333
|
+
const valueId = String(rawValue.valueId);
|
|
3334
|
+
const optionSlug = rawValue.optionSlug;
|
|
3335
|
+
if (!optionById.has(optionId)) continue;
|
|
3336
|
+
if (valueToOptionId.get(valueId) !== optionId) continue;
|
|
3337
|
+
if (optionValueByOptionId.has(optionId)) continue;
|
|
3338
|
+
optionValueByOptionId.set(optionId, valueId);
|
|
3339
|
+
if (optionSlug && !optionValueByOptionSlug.has(optionSlug)) {
|
|
3340
|
+
optionValueByOptionSlug.set(optionSlug, valueId);
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
const optionValueIds = optionIds.map((optionId) => optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId));
|
|
3344
|
+
return {
|
|
3345
|
+
id: String(variant.id),
|
|
3346
|
+
optionValueIds,
|
|
3347
|
+
optionValueByOptionId,
|
|
3348
|
+
optionValueByOptionSlug,
|
|
3349
|
+
source: variant
|
|
3350
|
+
};
|
|
3351
|
+
});
|
|
3245
3352
|
return {
|
|
3246
3353
|
options: normalizedOptions,
|
|
3247
3354
|
optionIds,
|
|
3355
|
+
optionSlugs,
|
|
3248
3356
|
optionById,
|
|
3357
|
+
optionBySlug,
|
|
3249
3358
|
valueById,
|
|
3250
3359
|
valueToOptionId,
|
|
3360
|
+
valueToOptionSlug,
|
|
3251
3361
|
variants: normalizedVariants
|
|
3252
3362
|
};
|
|
3253
3363
|
}
|
|
@@ -3280,7 +3390,7 @@ function getAvailableOptionValues(matrix, optionId, selectedValueIds) {
|
|
|
3280
3390
|
)
|
|
3281
3391
|
);
|
|
3282
3392
|
const availableValueIds = new Set(
|
|
3283
|
-
matchingVariants.map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
3393
|
+
matchingVariants.filter((variant) => variant.source.isActive !== false).map((variant) => variant.optionValueByOptionId.get(optionId)).filter((valueId) => Boolean(valueId))
|
|
3284
3394
|
);
|
|
3285
3395
|
return option.values.filter((value) => availableValueIds.has(value.id));
|
|
3286
3396
|
}
|
|
@@ -3295,6 +3405,432 @@ function resolveVariantForSelection(matrix, selectedValueIds) {
|
|
|
3295
3405
|
)
|
|
3296
3406
|
);
|
|
3297
3407
|
}
|
|
3408
|
+
function getVariantSelection(matrix, variantId) {
|
|
3409
|
+
if (variantId == null) return void 0;
|
|
3410
|
+
const id = String(variantId);
|
|
3411
|
+
return matrix.variants.find((variant) => variant.id === id);
|
|
3412
|
+
}
|
|
3413
|
+
function hasExplicitSelection(selection) {
|
|
3414
|
+
return Boolean(
|
|
3415
|
+
selection.variantId != null || selection.search || selection.valueIds || Object.keys(selection.byOptionId ?? {}).length > 0 || Object.keys(selection.byOptionSlug ?? {}).length > 0
|
|
3416
|
+
);
|
|
3417
|
+
}
|
|
3418
|
+
function assignSelectedValue(matrix, selectedByOptionId, optionId, valueId) {
|
|
3419
|
+
if (valueId == null) return;
|
|
3420
|
+
const normalizedValueId = String(valueId);
|
|
3421
|
+
if (matrix.valueToOptionId.get(normalizedValueId) !== optionId) return;
|
|
3422
|
+
selectedByOptionId.set(optionId, normalizedValueId);
|
|
3423
|
+
}
|
|
3424
|
+
function assignSelectedValueSlugByOptionId(matrix, selectedByOptionId, optionId, valueSlug) {
|
|
3425
|
+
if (!valueSlug) return;
|
|
3426
|
+
const option = matrix.optionById.get(optionId);
|
|
3427
|
+
if (!option) return;
|
|
3428
|
+
const value = option.values.find((candidate) => candidate.slug === valueSlug);
|
|
3429
|
+
if (!value) return;
|
|
3430
|
+
selectedByOptionId.set(optionId, value.id);
|
|
3431
|
+
}
|
|
3432
|
+
function assignSelectedValueSlugByOptionSlug(matrix, selectedByOptionId, optionSlug, valueSlug) {
|
|
3433
|
+
if (!valueSlug) return;
|
|
3434
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
3435
|
+
if (!option) return;
|
|
3436
|
+
const value = option.values.find((candidate) => candidate.slug === valueSlug);
|
|
3437
|
+
if (!value) return;
|
|
3438
|
+
selectedByOptionId.set(option.id, value.id);
|
|
3439
|
+
}
|
|
3440
|
+
function requireValueSlug(value) {
|
|
3441
|
+
if (value.slug) return value.slug;
|
|
3442
|
+
throw new ProductSelectionCodecError(
|
|
3443
|
+
`Option value "${value.id}" does not have a slug and cannot be used in product selection URLs.`
|
|
3444
|
+
);
|
|
3445
|
+
}
|
|
3446
|
+
function requireOptionSlug(option) {
|
|
3447
|
+
if (option.slug) return option.slug;
|
|
3448
|
+
throw new ProductSelectionCodecError(
|
|
3449
|
+
`Option "${option.id}" does not have a slug and cannot be used in product selection URLs.`
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
function toSearchParams(search) {
|
|
3453
|
+
if (!search) return new URLSearchParams();
|
|
3454
|
+
if (search instanceof URLSearchParams) return new URLSearchParams(search);
|
|
3455
|
+
if (search instanceof URL) return new URLSearchParams(search.searchParams);
|
|
3456
|
+
const trimmed = search.trim();
|
|
3457
|
+
if (!trimmed) return new URLSearchParams();
|
|
3458
|
+
try {
|
|
3459
|
+
if (/^[a-z][a-z0-9+.-]*:\/\//i.test(trimmed)) {
|
|
3460
|
+
return new URL(trimmed).searchParams;
|
|
3461
|
+
}
|
|
3462
|
+
} catch {
|
|
3463
|
+
return new URLSearchParams();
|
|
3464
|
+
}
|
|
3465
|
+
return new URLSearchParams(
|
|
3466
|
+
trimmed.startsWith("?") ? trimmed.slice(1) : trimmed
|
|
3467
|
+
);
|
|
3468
|
+
}
|
|
3469
|
+
function slugLike(value) {
|
|
3470
|
+
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3471
|
+
}
|
|
3472
|
+
function assertNoAmbiguousSelectionParams(matrix, params) {
|
|
3473
|
+
const knownSelectionKeys = /* @__PURE__ */ new Set();
|
|
3474
|
+
for (const option of matrix.options) {
|
|
3475
|
+
knownSelectionKeys.add(slugLike(option.slug));
|
|
3476
|
+
knownSelectionKeys.add(slugLike(option.title));
|
|
3477
|
+
for (const value of option.values) {
|
|
3478
|
+
if (value.slug) knownSelectionKeys.add(slugLike(value.slug));
|
|
3479
|
+
}
|
|
3480
|
+
}
|
|
3481
|
+
for (const [key, value] of params.entries()) {
|
|
3482
|
+
if (key.startsWith("opt.")) {
|
|
3483
|
+
const optionToken = key.slice(4);
|
|
3484
|
+
if (!optionToken || !matrix.optionBySlug.has(optionToken) && !matrix.optionById.has(optionToken)) {
|
|
3485
|
+
throw new ProductSelectionCodecError(
|
|
3486
|
+
`Unknown product selection query parameter "${key}". Use opt.<optionSlug>=<valueSlug>.`
|
|
3487
|
+
);
|
|
3488
|
+
}
|
|
3489
|
+
if (!value) {
|
|
3490
|
+
throw new ProductSelectionCodecError(
|
|
3491
|
+
`Product selection query parameter "${key}" requires a value slug.`
|
|
3492
|
+
);
|
|
3493
|
+
}
|
|
3494
|
+
continue;
|
|
3495
|
+
}
|
|
3496
|
+
const keyToken = slugLike(key);
|
|
3497
|
+
if (knownSelectionKeys.has(keyToken) || value === "" && knownSelectionKeys.has(keyToken)) {
|
|
3498
|
+
throw new ProductSelectionCodecError(
|
|
3499
|
+
`Ambiguous product selection query parameter "${key}". Use opt.<optionSlug>=<valueSlug>.`
|
|
3500
|
+
);
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
}
|
|
3504
|
+
function emitLegacyOptionIdParam(options, event) {
|
|
3505
|
+
try {
|
|
3506
|
+
options?.onLegacyOptionIdParam?.(event);
|
|
3507
|
+
} catch {
|
|
3508
|
+
}
|
|
3509
|
+
}
|
|
3510
|
+
function assignSearchSelection(matrix, selectedByOptionId, search, options) {
|
|
3511
|
+
if (!search) return;
|
|
3512
|
+
const params = toSearchParams(search);
|
|
3513
|
+
assertNoAmbiguousSelectionParams(matrix, params);
|
|
3514
|
+
for (const [key, valueSlug] of params.entries()) {
|
|
3515
|
+
if (!key.startsWith("opt.")) continue;
|
|
3516
|
+
const optionToken = key.slice(4);
|
|
3517
|
+
const optionBySlug = matrix.optionBySlug.get(optionToken);
|
|
3518
|
+
if (optionBySlug) {
|
|
3519
|
+
assignSelectedValueSlugByOptionSlug(
|
|
3520
|
+
matrix,
|
|
3521
|
+
selectedByOptionId,
|
|
3522
|
+
optionBySlug.slug,
|
|
3523
|
+
valueSlug
|
|
3524
|
+
);
|
|
3525
|
+
continue;
|
|
3526
|
+
}
|
|
3527
|
+
const legacyOption = matrix.optionById.get(optionToken);
|
|
3528
|
+
if (!legacyOption) continue;
|
|
3529
|
+
const before = selectedByOptionId.get(legacyOption.id);
|
|
3530
|
+
assignSelectedValueSlugByOptionId(
|
|
3531
|
+
matrix,
|
|
3532
|
+
selectedByOptionId,
|
|
3533
|
+
legacyOption.id,
|
|
3534
|
+
valueSlug
|
|
3535
|
+
);
|
|
3536
|
+
if (selectedByOptionId.get(legacyOption.id) !== before) {
|
|
3537
|
+
emitLegacyOptionIdParam(options, {
|
|
3538
|
+
optionId: legacyOption.id,
|
|
3539
|
+
optionSlug: legacyOption.slug,
|
|
3540
|
+
valueSlug,
|
|
3541
|
+
searchParam: key
|
|
3542
|
+
});
|
|
3543
|
+
}
|
|
3544
|
+
}
|
|
3545
|
+
}
|
|
3546
|
+
function normalizeProductSelection(detail, selection = {}, options) {
|
|
3547
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
3548
|
+
const selectedByOptionId = /* @__PURE__ */ new Map();
|
|
3549
|
+
const variantSelection = getVariantSelection(matrix, selection.variantId);
|
|
3550
|
+
const variantId = variantSelection?.id ?? null;
|
|
3551
|
+
if (variantSelection) {
|
|
3552
|
+
for (const [optionId, valueId] of variantSelection.optionValueByOptionId) {
|
|
3553
|
+
selectedByOptionId.set(optionId, valueId);
|
|
3554
|
+
}
|
|
3555
|
+
}
|
|
3556
|
+
for (const rawValueId of selection.valueIds ?? []) {
|
|
3557
|
+
const valueId = getRelationID(rawValueId);
|
|
3558
|
+
if (!valueId) continue;
|
|
3559
|
+
const optionId = matrix.valueToOptionId.get(valueId);
|
|
3560
|
+
if (!optionId) continue;
|
|
3561
|
+
selectedByOptionId.set(optionId, valueId);
|
|
3562
|
+
}
|
|
3563
|
+
assignSearchSelection(matrix, selectedByOptionId, selection.search, options);
|
|
3564
|
+
for (const [rawOptionId, rawSelection] of Object.entries(
|
|
3565
|
+
selection.byOptionId ?? {}
|
|
3566
|
+
)) {
|
|
3567
|
+
const optionId = String(rawOptionId);
|
|
3568
|
+
if (!matrix.optionById.has(optionId)) continue;
|
|
3569
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
3570
|
+
assignSelectedValue(
|
|
3571
|
+
matrix,
|
|
3572
|
+
selectedByOptionId,
|
|
3573
|
+
optionId,
|
|
3574
|
+
rawSelection.valueId
|
|
3575
|
+
);
|
|
3576
|
+
continue;
|
|
3577
|
+
}
|
|
3578
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
3579
|
+
assignSelectedValue(matrix, selectedByOptionId, optionId, rawSelection);
|
|
3580
|
+
continue;
|
|
3581
|
+
}
|
|
3582
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
3583
|
+
assignSelectedValueSlugByOptionId(
|
|
3584
|
+
matrix,
|
|
3585
|
+
selectedByOptionId,
|
|
3586
|
+
optionId,
|
|
3587
|
+
rawSelection.valueSlug
|
|
3588
|
+
);
|
|
3589
|
+
}
|
|
3590
|
+
}
|
|
3591
|
+
for (const [rawOptionSlug, rawSelection] of Object.entries(
|
|
3592
|
+
selection.byOptionSlug ?? {}
|
|
3593
|
+
)) {
|
|
3594
|
+
const optionSlug = String(rawOptionSlug);
|
|
3595
|
+
if (!matrix.optionBySlug.has(optionSlug)) continue;
|
|
3596
|
+
if (rawSelection && typeof rawSelection === "object" && "valueId" in rawSelection && rawSelection.valueId != null) {
|
|
3597
|
+
const option = matrix.optionBySlug.get(optionSlug);
|
|
3598
|
+
if (option) {
|
|
3599
|
+
assignSelectedValue(
|
|
3600
|
+
matrix,
|
|
3601
|
+
selectedByOptionId,
|
|
3602
|
+
option.id,
|
|
3603
|
+
rawSelection.valueId
|
|
3604
|
+
);
|
|
3605
|
+
}
|
|
3606
|
+
continue;
|
|
3607
|
+
}
|
|
3608
|
+
if (rawSelection && typeof rawSelection === "object" && "valueSlug" in rawSelection) {
|
|
3609
|
+
assignSelectedValueSlugByOptionSlug(
|
|
3610
|
+
matrix,
|
|
3611
|
+
selectedByOptionId,
|
|
3612
|
+
optionSlug,
|
|
3613
|
+
rawSelection.valueSlug
|
|
3614
|
+
);
|
|
3615
|
+
continue;
|
|
3616
|
+
}
|
|
3617
|
+
if (typeof rawSelection === "string" || typeof rawSelection === "number") {
|
|
3618
|
+
assignSelectedValueSlugByOptionSlug(
|
|
3619
|
+
matrix,
|
|
3620
|
+
selectedByOptionId,
|
|
3621
|
+
optionSlug,
|
|
3622
|
+
String(rawSelection)
|
|
3623
|
+
);
|
|
3624
|
+
}
|
|
3625
|
+
}
|
|
3626
|
+
const byOptionId = Object.fromEntries(
|
|
3627
|
+
matrix.optionIds.map((optionId) => [optionId, selectedByOptionId.get(optionId)]).filter((entry) => Boolean(entry[1]))
|
|
3628
|
+
);
|
|
3629
|
+
const byOptionSlug = Object.fromEntries(
|
|
3630
|
+
matrix.options.map((option) => {
|
|
3631
|
+
const valueId = selectedByOptionId.get(option.id);
|
|
3632
|
+
const value = valueId ? matrix.valueById.get(valueId) : void 0;
|
|
3633
|
+
return [option.slug, value?.slug ?? void 0];
|
|
3634
|
+
}).filter((entry) => Boolean(entry[1]))
|
|
3635
|
+
);
|
|
3636
|
+
return {
|
|
3637
|
+
byOptionSlug,
|
|
3638
|
+
byOptionId,
|
|
3639
|
+
valueIds: matrix.optionIds.map((optionId) => byOptionId[optionId]).filter((valueId) => Boolean(valueId)),
|
|
3640
|
+
variantId
|
|
3641
|
+
};
|
|
3642
|
+
}
|
|
3643
|
+
function parseProductSelection(detail, search, options) {
|
|
3644
|
+
return normalizeProductSelection(detail, { search }, options);
|
|
3645
|
+
}
|
|
3646
|
+
function stringifyProductSelection(detail, selection = {}, options) {
|
|
3647
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
3648
|
+
const normalized = normalizeProductSelection(detail, selection, options);
|
|
3649
|
+
const params = new URLSearchParams();
|
|
3650
|
+
for (const optionId of matrix.optionIds) {
|
|
3651
|
+
const valueId = normalized.byOptionId[optionId];
|
|
3652
|
+
if (!valueId) continue;
|
|
3653
|
+
const option = matrix.optionById.get(optionId);
|
|
3654
|
+
const value = matrix.valueById.get(valueId);
|
|
3655
|
+
if (!option || !value) continue;
|
|
3656
|
+
params.append(`opt.${requireOptionSlug(option)}`, requireValueSlug(value));
|
|
3657
|
+
}
|
|
3658
|
+
return params.toString();
|
|
3659
|
+
}
|
|
3660
|
+
function createProductSelectionCodec(detail, options) {
|
|
3661
|
+
return {
|
|
3662
|
+
parse: (search) => parseProductSelection(detail, search, options),
|
|
3663
|
+
stringify: (selection = {}) => stringifyProductSelection(detail, selection, options)
|
|
3664
|
+
};
|
|
3665
|
+
}
|
|
3666
|
+
function selectedEntries(selection) {
|
|
3667
|
+
return Object.entries(selection.byOptionId);
|
|
3668
|
+
}
|
|
3669
|
+
function getMatchingVariantEntries(matrix, selection) {
|
|
3670
|
+
const entries = selectedEntries(selection);
|
|
3671
|
+
if (entries.length === 0) return matrix.variants;
|
|
3672
|
+
return matrix.variants.filter(
|
|
3673
|
+
(variant) => entries.every(
|
|
3674
|
+
([optionId, valueId]) => variant.optionValueByOptionId.get(optionId) === valueId
|
|
3675
|
+
)
|
|
3676
|
+
);
|
|
3677
|
+
}
|
|
3678
|
+
function activeVariantEntries(variants) {
|
|
3679
|
+
return variants.filter((variant) => variant.source.isActive !== false);
|
|
3680
|
+
}
|
|
3681
|
+
function getExactSelectedVariantEntry(matrix, selection, matchingVariants) {
|
|
3682
|
+
if (matrix.optionIds.length === 0) {
|
|
3683
|
+
return getVariantSelection(matrix, selection.variantId) ?? (matchingVariants.length === 1 ? matchingVariants[0] ?? null : null);
|
|
3684
|
+
}
|
|
3685
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
3686
|
+
(optionId) => Boolean(selection.byOptionId[optionId])
|
|
3687
|
+
);
|
|
3688
|
+
if (!allOptionsSelected) return null;
|
|
3689
|
+
return matchingVariants.find(
|
|
3690
|
+
(variant) => matrix.optionIds.every(
|
|
3691
|
+
(optionId) => variant.optionValueByOptionId.get(optionId) === selection.byOptionId[optionId]
|
|
3692
|
+
)
|
|
3693
|
+
) ?? null;
|
|
3694
|
+
}
|
|
3695
|
+
function buildSelectionPrice(variants) {
|
|
3696
|
+
const { min, max } = getMinMax(variants.map((variant) => variant.price));
|
|
3697
|
+
const { min: compareAtMin, max: compareAtMax } = getMinMax(
|
|
3698
|
+
variants.map((variant) => variant.compareAtPrice)
|
|
3699
|
+
);
|
|
3700
|
+
return {
|
|
3701
|
+
min,
|
|
3702
|
+
max,
|
|
3703
|
+
compareAtMin,
|
|
3704
|
+
compareAtMax,
|
|
3705
|
+
isRange: min !== null && max !== null ? min !== max : false
|
|
3706
|
+
};
|
|
3707
|
+
}
|
|
3708
|
+
function firstMedia(value) {
|
|
3709
|
+
if (value == null) return null;
|
|
3710
|
+
if (Array.isArray(value)) return firstMedia(value[0]);
|
|
3711
|
+
return value;
|
|
3712
|
+
}
|
|
3713
|
+
function isPresentMedia(value) {
|
|
3714
|
+
return value != null;
|
|
3715
|
+
}
|
|
3716
|
+
function mediaArray(values) {
|
|
3717
|
+
if (!Array.isArray(values)) return [];
|
|
3718
|
+
return values.filter(isPresentMedia);
|
|
3719
|
+
}
|
|
3720
|
+
function buildSelectionMedia(detail, selectedVariant, matchingVariants, selectedValues) {
|
|
3721
|
+
const selectedValueImages = selectedValues.flatMap(
|
|
3722
|
+
(value) => mediaArray(value.images)
|
|
3723
|
+
);
|
|
3724
|
+
const selectedValuePrimary = selectedValues.map((value) => firstMedia(value.thumbnail) ?? firstMedia(value.images)).find((value) => value != null) ?? null;
|
|
3725
|
+
const selectedVariantPrimary = firstMedia(selectedVariant?.thumbnail) ?? firstMedia(selectedVariant?.images);
|
|
3726
|
+
const matchingVariantPrimary = matchingVariants.map(
|
|
3727
|
+
(variant) => firstMedia(variant.thumbnail) ?? firstMedia(variant.images)
|
|
3728
|
+
).find((value) => value != null) ?? null;
|
|
3729
|
+
const detailImages = mediaArray(detail.images);
|
|
3730
|
+
const primaryImage = selectedVariantPrimary ?? selectedValuePrimary ?? firstMedia(detail.listing.primaryImage) ?? matchingVariantPrimary ?? firstMedia(detailImages);
|
|
3731
|
+
const images = mediaArray(selectedVariant?.images).length > 0 ? mediaArray(selectedVariant?.images) : selectedValueImages.length > 0 ? selectedValueImages : detailImages;
|
|
3732
|
+
return {
|
|
3733
|
+
primaryImage,
|
|
3734
|
+
images
|
|
3735
|
+
};
|
|
3736
|
+
}
|
|
3737
|
+
function buildSelectionStock(selectedVariant, matchingVariants) {
|
|
3738
|
+
if (selectedVariant) {
|
|
3739
|
+
const availableStock = selectedVariant.isUnlimited ? null : Math.max(0, selectedVariant.stock - selectedVariant.reservedStock);
|
|
3740
|
+
const isActive = selectedVariant.isActive !== false;
|
|
3741
|
+
return {
|
|
3742
|
+
availableForSale: isActive && (selectedVariant.isUnlimited || (availableStock ?? 0) > 0),
|
|
3743
|
+
isUnlimited: selectedVariant.isUnlimited,
|
|
3744
|
+
stock: selectedVariant.stock,
|
|
3745
|
+
reservedStock: selectedVariant.reservedStock,
|
|
3746
|
+
availableStock
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
return {
|
|
3750
|
+
availableForSale: matchingVariants.some(isVariantAvailableForSale),
|
|
3751
|
+
isUnlimited: matchingVariants.some((variant) => variant.isUnlimited),
|
|
3752
|
+
stock: null,
|
|
3753
|
+
reservedStock: null,
|
|
3754
|
+
availableStock: null
|
|
3755
|
+
};
|
|
3756
|
+
}
|
|
3757
|
+
function resolveProductSelection(detail, selection = {}, options) {
|
|
3758
|
+
const matrix = buildProductOptionMatrixFromDetail(detail);
|
|
3759
|
+
const effectiveSelection = hasExplicitSelection(selection) || detail.listing.selectionHintVariant == null ? selection : { ...selection, variantId: detail.listing.selectionHintVariant };
|
|
3760
|
+
const normalizedSelection = normalizeProductSelection(
|
|
3761
|
+
detail,
|
|
3762
|
+
effectiveSelection,
|
|
3763
|
+
options
|
|
3764
|
+
);
|
|
3765
|
+
const matchingVariantEntries = getMatchingVariantEntries(
|
|
3766
|
+
matrix,
|
|
3767
|
+
normalizedSelection
|
|
3768
|
+
);
|
|
3769
|
+
const activeMatchingVariantEntries = activeVariantEntries(
|
|
3770
|
+
matchingVariantEntries
|
|
3771
|
+
);
|
|
3772
|
+
const selectedVariantEntry = getExactSelectedVariantEntry(
|
|
3773
|
+
matrix,
|
|
3774
|
+
normalizedSelection,
|
|
3775
|
+
matchingVariantEntries
|
|
3776
|
+
);
|
|
3777
|
+
const selectedVariant = selectedVariantEntry?.source ?? null;
|
|
3778
|
+
const matchingVariants = activeMatchingVariantEntries.map(
|
|
3779
|
+
(variant) => variant.source
|
|
3780
|
+
);
|
|
3781
|
+
const selectedValues = matrix.optionIds.map((optionId) => normalizedSelection.byOptionId[optionId]).map((valueId) => valueId ? matrix.valueById.get(valueId) : void 0).filter((value) => value !== void 0);
|
|
3782
|
+
const availableValuesByOptionId = Object.fromEntries(
|
|
3783
|
+
matrix.options.map((option) => {
|
|
3784
|
+
const availableValueIds = new Set(
|
|
3785
|
+
getAvailableOptionValues(
|
|
3786
|
+
matrix,
|
|
3787
|
+
option.id,
|
|
3788
|
+
normalizedSelection.valueIds
|
|
3789
|
+
).map((value) => value.id)
|
|
3790
|
+
);
|
|
3791
|
+
return [
|
|
3792
|
+
option.id,
|
|
3793
|
+
option.values.map((value) => ({
|
|
3794
|
+
valueId: value.id,
|
|
3795
|
+
value: value.label,
|
|
3796
|
+
slug: requireValueSlug(value),
|
|
3797
|
+
selected: normalizedSelection.byOptionId[option.id] === value.id,
|
|
3798
|
+
available: availableValueIds.has(value.id),
|
|
3799
|
+
swatchColor: value.swatchColor ?? null,
|
|
3800
|
+
thumbnail: value.thumbnail ?? null,
|
|
3801
|
+
images: value.images ?? null
|
|
3802
|
+
}))
|
|
3803
|
+
];
|
|
3804
|
+
})
|
|
3805
|
+
);
|
|
3806
|
+
const availableValuesByOptionSlug = Object.fromEntries(
|
|
3807
|
+
matrix.options.map((option) => [
|
|
3808
|
+
option.slug,
|
|
3809
|
+
availableValuesByOptionId[option.id] ?? []
|
|
3810
|
+
])
|
|
3811
|
+
);
|
|
3812
|
+
const allOptionsSelected = matrix.optionIds.every(
|
|
3813
|
+
(optionId) => Boolean(normalizedSelection.byOptionId[optionId])
|
|
3814
|
+
);
|
|
3815
|
+
const priceVariants = selectedVariant ? [selectedVariant] : matchingVariants;
|
|
3816
|
+
return {
|
|
3817
|
+
normalizedSelection,
|
|
3818
|
+
selectedVariant,
|
|
3819
|
+
matchingVariants,
|
|
3820
|
+
partialVariants: selectedVariant ? [] : matchingVariants,
|
|
3821
|
+
availableValuesByOptionSlug,
|
|
3822
|
+
availableValuesByOptionId,
|
|
3823
|
+
allOptionsSelected,
|
|
3824
|
+
price: buildSelectionPrice(priceVariants),
|
|
3825
|
+
media: buildSelectionMedia(
|
|
3826
|
+
detail,
|
|
3827
|
+
selectedVariant,
|
|
3828
|
+
matchingVariants,
|
|
3829
|
+
selectedValues
|
|
3830
|
+
),
|
|
3831
|
+
stock: buildSelectionStock(selectedVariant, matchingVariants)
|
|
3832
|
+
};
|
|
3833
|
+
}
|
|
3298
3834
|
function compareVariantOrder(a, b) {
|
|
3299
3835
|
const aOrder = Number(a._order ?? Number.MAX_SAFE_INTEGER);
|
|
3300
3836
|
const bOrder = Number(b._order ?? Number.MAX_SAFE_INTEGER);
|