medusa-plugin-ordinant 0.1.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/.medusa/server/src/admin/index.js +1636 -0
- package/.medusa/server/src/admin/index.mjs +1635 -0
- package/.medusa/server/src/api/admin/ordinant/classifications/[sku]/confirm/route.js +25 -0
- package/.medusa/server/src/api/admin/ordinant/classifications/bulk-confirm/route.js +29 -0
- package/.medusa/server/src/api/admin/ordinant/classifications/route.js +23 -0
- package/.medusa/server/src/api/admin/ordinant/classify-all/route.js +108 -0
- package/.medusa/server/src/api/admin/ordinant/dictionary/route.js +17 -0
- package/.medusa/server/src/api/admin/ordinant/generate-all-skus/route.js +31 -0
- package/.medusa/server/src/api/admin/ordinant/overview/route.js +69 -0
- package/.medusa/server/src/api/admin/ordinant/products/[id]/classify/route.js +18 -0
- package/.medusa/server/src/api/admin/ordinant/products/[id]/generate-skus/route.js +34 -0
- package/.medusa/server/src/api/admin/ordinant/products/[id]/overview/route.js +59 -0
- package/.medusa/server/src/api/admin/ordinant/stats/route.js +17 -0
- package/.medusa/server/src/api/admin/ordinant/status/route.js +52 -0
- package/.medusa/server/src/api/middlewares.js +14 -0
- package/.medusa/server/src/api/store/ordinant/preview/route.js +110 -0
- package/.medusa/server/src/api/store/ordinant/preview/validators.js +34 -0
- package/.medusa/server/src/lib/client.js +136 -0
- package/.medusa/server/src/modules/ordinant/index.js +14 -0
- package/.medusa/server/src/modules/ordinant/service.js +27 -0
- package/.medusa/server/src/storefront.d.ts +61 -0
- package/.medusa/server/src/storefront.js +61 -0
- package/.medusa/server/src/subscribers/product-upsert.js +52 -0
- package/.medusa/server/src/workflows/bulk-operations.js +59 -0
- package/.medusa/server/src/workflows/classify-product.js +80 -0
- package/.medusa/server/src/workflows/confirm-classification.js +20 -0
- package/.medusa/server/src/workflows/generate-skus.js +81 -0
- package/.medusa/server/src/workflows/hooks/validate-cart-completion.js +127 -0
- package/README.md +495 -0
- package/package.json +92 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.classifyProductWorkflow = exports.classifyItemsStep = exports.prepareClassifyItemsStep = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
6
|
+
const ordinant_1 = require("../modules/ordinant");
|
|
7
|
+
/**
|
|
8
|
+
* prepareClassifyItemsStep maps a Medusa product and its variants into
|
|
9
|
+
* Ordinant classification inputs. The unit of classification is the variant
|
|
10
|
+
* SKU — variants without a SKU cannot be gated and are skipped.
|
|
11
|
+
*/
|
|
12
|
+
exports.prepareClassifyItemsStep = (0, workflows_sdk_1.createStep)("ordinant-prepare-classify-items", async (input, { container }) => {
|
|
13
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
14
|
+
const { data: products } = await query.graph({
|
|
15
|
+
entity: "product",
|
|
16
|
+
fields: [
|
|
17
|
+
"id",
|
|
18
|
+
"title",
|
|
19
|
+
"description",
|
|
20
|
+
"type.value",
|
|
21
|
+
"collection.title",
|
|
22
|
+
"variants.id",
|
|
23
|
+
"variants.sku",
|
|
24
|
+
"variants.title",
|
|
25
|
+
"variants.options.value",
|
|
26
|
+
"variants.options.option.title",
|
|
27
|
+
],
|
|
28
|
+
filters: { id: input.product_id },
|
|
29
|
+
});
|
|
30
|
+
const product = products[0];
|
|
31
|
+
if (!product) {
|
|
32
|
+
return new workflows_sdk_1.StepResponse([]);
|
|
33
|
+
}
|
|
34
|
+
const items = [];
|
|
35
|
+
for (const variant of product.variants ?? []) {
|
|
36
|
+
if (!variant?.sku) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const specs = {};
|
|
40
|
+
for (const option of variant.options ?? []) {
|
|
41
|
+
if (option?.option?.title && option.value) {
|
|
42
|
+
specs[option.option.title] = option.value;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
items.push({
|
|
46
|
+
sku: variant.sku,
|
|
47
|
+
title: variant.title && variant.title !== "Default variant"
|
|
48
|
+
? `${product.title} — ${variant.title}`
|
|
49
|
+
: product.title,
|
|
50
|
+
description: product.description ?? undefined,
|
|
51
|
+
product_type: product.type?.value ?? product.collection?.title ?? undefined,
|
|
52
|
+
specs: Object.keys(specs).length > 0 ? specs : undefined,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return new workflows_sdk_1.StepResponse(items);
|
|
56
|
+
});
|
|
57
|
+
/**
|
|
58
|
+
* classifyItemsStep submits the inputs to Ordinant. The result is a set of
|
|
59
|
+
* PROPOSALS with confidence scores — nothing participates in checkout
|
|
60
|
+
* decisions until the merchant confirms in the product's Ordinant widget.
|
|
61
|
+
*/
|
|
62
|
+
exports.classifyItemsStep = (0, workflows_sdk_1.createStep)("ordinant-classify-items", async (items, { container }) => {
|
|
63
|
+
if (items.length === 0) {
|
|
64
|
+
return new workflows_sdk_1.StepResponse([]);
|
|
65
|
+
}
|
|
66
|
+
const ordinant = container.resolve(ordinant_1.ORDINANT_MODULE);
|
|
67
|
+
const response = await ordinant.client.classifyCatalog(items);
|
|
68
|
+
return new workflows_sdk_1.StepResponse(response.results);
|
|
69
|
+
});
|
|
70
|
+
/**
|
|
71
|
+
* classifyProductWorkflow sends a product's variants to Ordinant for
|
|
72
|
+
* attribute classification. Run from the product.created/updated
|
|
73
|
+
* subscribers and from the admin widget's "Classify" action.
|
|
74
|
+
*/
|
|
75
|
+
exports.classifyProductWorkflow = (0, workflows_sdk_1.createWorkflow)("ordinant-classify-product", function (input) {
|
|
76
|
+
const items = (0, exports.prepareClassifyItemsStep)(input);
|
|
77
|
+
const results = (0, exports.classifyItemsStep)(items);
|
|
78
|
+
return new workflows_sdk_1.WorkflowResponse(results);
|
|
79
|
+
});
|
|
80
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3NpZnktcHJvZHVjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvY2xhc3NpZnktcHJvZHVjdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFLMEM7QUFDMUMscURBQXFFO0FBQ3JFLGtEQUFxRDtBQVFyRDs7OztHQUlHO0FBQ1UsUUFBQSx3QkFBd0IsR0FBRyxJQUFBLDBCQUFVLEVBQ2hELGlDQUFpQyxFQUNqQyxLQUFLLEVBQUUsS0FBMkIsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDbkQsTUFBTSxLQUFLLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBQyxpQ0FBeUIsQ0FBQyxLQUFLLENBQUMsQ0FBQTtJQUNoRSxNQUFNLEVBQUUsSUFBSSxFQUFFLFFBQVEsRUFBRSxHQUFHLE1BQU0sS0FBSyxDQUFDLEtBQUssQ0FBQztRQUMzQyxNQUFNLEVBQUUsU0FBUztRQUNqQixNQUFNLEVBQUU7WUFDTixJQUFJO1lBQ0osT0FBTztZQUNQLGFBQWE7WUFDYixZQUFZO1lBQ1osa0JBQWtCO1lBQ2xCLGFBQWE7WUFDYixjQUFjO1lBQ2QsZ0JBQWdCO1lBQ2hCLHdCQUF3QjtZQUN4QiwrQkFBK0I7U0FDaEM7UUFDRCxPQUFPLEVBQUUsRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLFVBQVUsRUFBRTtLQUNsQyxDQUFDLENBQUE7SUFDRixNQUFNLE9BQU8sR0FBRyxRQUFRLENBQUMsQ0FBQyxDQUFDLENBQUE7SUFDM0IsSUFBSSxDQUFDLE9BQU8sRUFBRSxDQUFDO1FBQ2IsT0FBTyxJQUFJLDRCQUFZLENBQWlCLEVBQUUsQ0FBQyxDQUFBO0lBQzdDLENBQUM7SUFFRCxNQUFNLEtBQUssR0FBbUIsRUFBRSxDQUFBO0lBQ2hDLEtBQUssTUFBTSxPQUFPLElBQUksT0FBTyxDQUFDLFFBQVEsSUFBSSxFQUFFLEVBQUUsQ0FBQztRQUM3QyxJQUFJLENBQUMsT0FBTyxFQUFFLEdBQUcsRUFBRSxDQUFDO1lBQ2xCLFNBQVE7UUFDVixDQUFDO1FBQ0QsTUFBTSxLQUFLLEdBQTJCLEVBQUUsQ0FBQTtRQUN4QyxLQUFLLE1BQU0sTUFBTSxJQUFJLE9BQU8sQ0FBQyxPQUFPLElBQUksRUFBRSxFQUFFLENBQUM7WUFDM0MsSUFBSSxNQUFNLEVBQUUsTUFBTSxFQUFFLEtBQUssSUFBSSxNQUFNLENBQUMsS0FBSyxFQUFFLENBQUM7Z0JBQzFDLEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUE7WUFDM0MsQ0FBQztRQUNILENBQUM7UUFDRCxLQUFLLENBQUMsSUFBSSxDQUFDO1lBQ1QsR0FBRyxFQUFFLE9BQU8sQ0FBQyxHQUFHO1lBQ2hCLEtBQUssRUFDSCxPQUFPLENBQUMsS0FBSyxJQUFJLE9BQU8sQ0FBQyxLQUFLLEtBQUssaUJBQWlCO2dCQUNsRCxDQUFDLENBQUMsR0FBRyxPQUFPLENBQUMsS0FBSyxNQUFNLE9BQU8sQ0FBQyxLQUFLLEVBQUU7Z0JBQ3ZDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSztZQUNuQixXQUFXLEVBQUUsT0FBTyxDQUFDLFdBQVcsSUFBSSxTQUFTO1lBQzdDLFlBQVksRUFBRSxPQUFPLENBQUMsSUFBSSxFQUFFLEtBQUssSUFBSSxPQUFPLENBQUMsVUFBVSxFQUFFLEtBQUssSUFBSSxTQUFTO1lBQzNFLEtBQUssRUFBRSxNQUFNLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDLE1BQU0sR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsU0FBUztTQUN6RCxDQUFDLENBQUE7SUFDSixDQUFDO0lBQ0QsT0FBTyxJQUFJLDRCQUFZLENBQUMsS0FBSyxDQUFDLENBQUE7QUFDaEMsQ0FBQyxDQUNGLENBQUE7QUFFRDs7OztHQUlHO0FBQ1UsUUFBQSxpQkFBaUIsR0FBRyxJQUFBLDBCQUFVLEVBQ3pDLHlCQUF5QixFQUN6QixLQUFLLEVBQUUsS0FBcUIsRUFBRSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUU7SUFDN0MsSUFBSSxLQUFLLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3ZCLE9BQU8sSUFBSSw0QkFBWSxDQUFtQixFQUFFLENBQUMsQ0FBQTtJQUMvQyxDQUFDO0lBQ0QsTUFBTSxRQUFRLEdBQUcsU0FBUyxDQUFDLE9BQU8sQ0FBd0IsMEJBQWUsQ0FBQyxDQUFBO0lBQzFFLE1BQU0sUUFBUSxHQUFHLE1BQU0sUUFBUSxDQUFDLE1BQU0sQ0FBQyxlQUFlLENBQUMsS0FBSyxDQUFDLENBQUE7SUFDN0QsT0FBTyxJQUFJLDRCQUFZLENBQUMsUUFBUSxDQUFDLE9BQU8sQ0FBQyxDQUFBO0FBQzNDLENBQUMsQ0FDRixDQUFBO0FBRUQ7Ozs7R0FJRztBQUNVLFFBQUEsdUJBQXVCLEdBQUcsSUFBQSw4QkFBYyxFQUNuRCwyQkFBMkIsRUFDM0IsVUFBVSxLQUEyQjtJQUNuQyxNQUFNLEtBQUssR0FBRyxJQUFBLGdDQUF3QixFQUFDLEtBQUssQ0FBQyxDQUFBO0lBQzdDLE1BQU0sT0FBTyxHQUFHLElBQUEseUJBQWlCLEVBQUMsS0FBSyxDQUFDLENBQUE7SUFDeEMsT0FBTyxJQUFJLGdDQUFnQixDQUFDLE9BQU8sQ0FBQyxDQUFBO0FBQ3RDLENBQUMsQ0FDRixDQUFBIn0=
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.confirmClassificationWorkflow = exports.confirmClassificationStep = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const ordinant_1 = require("../modules/ordinant");
|
|
6
|
+
exports.confirmClassificationStep = (0, workflows_sdk_1.createStep)("ordinant-confirm-classification", async (input, { container }) => {
|
|
7
|
+
const ordinant = container.resolve(ordinant_1.ORDINANT_MODULE);
|
|
8
|
+
const record = await ordinant.client.confirmClassification(input.sku, input.attributes);
|
|
9
|
+
return new workflows_sdk_1.StepResponse(record);
|
|
10
|
+
});
|
|
11
|
+
/**
|
|
12
|
+
* confirmClassificationWorkflow records the merchant's attestation with
|
|
13
|
+
* Ordinant. From then on, checkout decisions for this SKU resolve the
|
|
14
|
+
* confirmed attributes server-side.
|
|
15
|
+
*/
|
|
16
|
+
exports.confirmClassificationWorkflow = (0, workflows_sdk_1.createWorkflow)("ordinant-confirm-classification", function (input) {
|
|
17
|
+
const record = (0, exports.confirmClassificationStep)(input);
|
|
18
|
+
return new workflows_sdk_1.WorkflowResponse(record);
|
|
19
|
+
});
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uZmlybS1jbGFzc2lmaWNhdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvY29uZmlybS1jbGFzc2lmaWNhdGlvbi50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFLMEM7QUFDMUMsa0RBQXFEO0FBU3hDLFFBQUEseUJBQXlCLEdBQUcsSUFBQSwwQkFBVSxFQUNqRCxpQ0FBaUMsRUFDakMsS0FBSyxFQUFFLEtBQWlDLEVBQUUsRUFBRSxTQUFTLEVBQUUsRUFBRSxFQUFFO0lBQ3pELE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQXdCLDBCQUFlLENBQUMsQ0FBQTtJQUMxRSxNQUFNLE1BQU0sR0FBRyxNQUFNLFFBQVEsQ0FBQyxNQUFNLENBQUMscUJBQXFCLENBQ3hELEtBQUssQ0FBQyxHQUFHLEVBQ1QsS0FBSyxDQUFDLFVBQVUsQ0FDakIsQ0FBQTtJQUNELE9BQU8sSUFBSSw0QkFBWSxDQUFDLE1BQU0sQ0FBQyxDQUFBO0FBQ2pDLENBQUMsQ0FDRixDQUFBO0FBRUQ7Ozs7R0FJRztBQUNVLFFBQUEsNkJBQTZCLEdBQUcsSUFBQSw4QkFBYyxFQUN6RCxpQ0FBaUMsRUFDakMsVUFBVSxLQUFpQztJQUN6QyxNQUFNLE1BQU0sR0FBRyxJQUFBLGlDQUF5QixFQUFDLEtBQUssQ0FBQyxDQUFBO0lBQy9DLE9BQU8sSUFBSSxnQ0FBZ0IsQ0FBQyxNQUFNLENBQUMsQ0FBQTtBQUNyQyxDQUFDLENBQ0YsQ0FBQSJ9
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSkusWorkflow = exports.prepareGeneratedSkusStep = void 0;
|
|
4
|
+
const workflows_sdk_1 = require("@medusajs/framework/workflows-sdk");
|
|
5
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
6
|
+
const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
7
|
+
function slug(s) {
|
|
8
|
+
return s
|
|
9
|
+
.toUpperCase()
|
|
10
|
+
.replace(/[^A-Z0-9]+/g, "-")
|
|
11
|
+
.replace(/^-+|-+$/g, "");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* generateUniqueSku derives a readable SKU from the product and variant
|
|
15
|
+
* titles (e.g. "Thermal Suppressors N3WTON" / "Quiet" → "THERMAL-SUPPRESSORS-
|
|
16
|
+
* N3WTON-QUIET"), avoiding collisions with SKUs already in use.
|
|
17
|
+
*/
|
|
18
|
+
function generateUniqueSku(productTitle, variantTitle, used) {
|
|
19
|
+
const p = slug(productTitle).split("-").filter(Boolean).slice(0, 3).join("-") || "PROD";
|
|
20
|
+
const v = variantTitle && variantTitle !== "Default variant" ? slug(variantTitle) : "";
|
|
21
|
+
const base = (v ? `${p}-${v}` : p).slice(0, 48) || "PROD";
|
|
22
|
+
let sku = base;
|
|
23
|
+
let i = 1;
|
|
24
|
+
while (used.has(sku)) {
|
|
25
|
+
sku = `${base}-${i}`;
|
|
26
|
+
i++;
|
|
27
|
+
}
|
|
28
|
+
return sku;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* prepareGeneratedSkusStep gathers a product's SKU-less variants and assigns
|
|
32
|
+
* each a unique SKU, checking against every SKU already in the store so a
|
|
33
|
+
* generated value can never collide.
|
|
34
|
+
*/
|
|
35
|
+
exports.prepareGeneratedSkusStep = (0, workflows_sdk_1.createStep)("ordinant-prepare-generated-skus", async (input, { container }) => {
|
|
36
|
+
const query = container.resolve(utils_1.ContainerRegistrationKeys.QUERY);
|
|
37
|
+
const [{ data: products }, { data: allVariants }] = await Promise.all([
|
|
38
|
+
query.graph({
|
|
39
|
+
entity: "product",
|
|
40
|
+
fields: ["id", "title", "variants.id", "variants.sku", "variants.title"],
|
|
41
|
+
filters: { id: input.product_id },
|
|
42
|
+
}),
|
|
43
|
+
query.graph({
|
|
44
|
+
entity: "product_variant",
|
|
45
|
+
fields: ["sku"],
|
|
46
|
+
pagination: { take: 10000, skip: 0 },
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
49
|
+
const used = new Set(allVariants.map((v) => v?.sku).filter((s) => Boolean(s)));
|
|
50
|
+
const product = products[0];
|
|
51
|
+
const generated = [];
|
|
52
|
+
for (const variant of product?.variants ?? []) {
|
|
53
|
+
if (!variant || variant.sku) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const sku = generateUniqueSku(product.title, variant.title ?? null, used);
|
|
57
|
+
used.add(sku);
|
|
58
|
+
generated.push({
|
|
59
|
+
variant_id: variant.id,
|
|
60
|
+
variant_title: variant.title ?? null,
|
|
61
|
+
sku,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
return new workflows_sdk_1.StepResponse(generated);
|
|
65
|
+
});
|
|
66
|
+
/**
|
|
67
|
+
* generateSkusWorkflow assigns SKUs to a product's SKU-less variants — the
|
|
68
|
+
* "Generate SKU" helper. It writes them back through the core variant-update
|
|
69
|
+
* workflow, so a SKU-less product becomes classifiable and sellable in one
|
|
70
|
+
* click. A product whose variants already all have SKUs is a no-op.
|
|
71
|
+
*/
|
|
72
|
+
exports.generateSkusWorkflow = (0, workflows_sdk_1.createWorkflow)("ordinant-generate-skus", function (input) {
|
|
73
|
+
const generated = (0, exports.prepareGeneratedSkusStep)(input);
|
|
74
|
+
// Core variant update keys on `id`; our step returns `variant_id`.
|
|
75
|
+
const updates = (0, workflows_sdk_1.transform)({ generated }, (data) => data.generated.map((g) => ({ id: g.variant_id, sku: g.sku })));
|
|
76
|
+
core_flows_1.updateProductVariantsWorkflow.runAsStep({
|
|
77
|
+
input: { product_variants: updates },
|
|
78
|
+
});
|
|
79
|
+
return new workflows_sdk_1.WorkflowResponse(generated);
|
|
80
|
+
});
|
|
81
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZ2VuZXJhdGUtc2t1cy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy93b3JrZmxvd3MvZ2VuZXJhdGUtc2t1cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxxRUFNMEM7QUFDMUMscURBQXFFO0FBQ3JFLDREQUEyRTtBQVkzRSxTQUFTLElBQUksQ0FBQyxDQUFTO0lBQ3JCLE9BQU8sQ0FBQztTQUNMLFdBQVcsRUFBRTtTQUNiLE9BQU8sQ0FBQyxhQUFhLEVBQUUsR0FBRyxDQUFDO1NBQzNCLE9BQU8sQ0FBQyxVQUFVLEVBQUUsRUFBRSxDQUFDLENBQUE7QUFDNUIsQ0FBQztBQUVEOzs7O0dBSUc7QUFDSCxTQUFTLGlCQUFpQixDQUN4QixZQUFvQixFQUNwQixZQUEyQixFQUMzQixJQUFpQjtJQUVqQixNQUFNLENBQUMsR0FDTCxJQUFJLENBQUMsWUFBWSxDQUFDLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxPQUFPLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxNQUFNLENBQUE7SUFDL0UsTUFBTSxDQUFDLEdBQ0wsWUFBWSxJQUFJLFlBQVksS0FBSyxpQkFBaUIsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUE7SUFDOUUsTUFBTSxJQUFJLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxJQUFJLE1BQU0sQ0FBQTtJQUN6RCxJQUFJLEdBQUcsR0FBRyxJQUFJLENBQUE7SUFDZCxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUE7SUFDVCxPQUFPLElBQUksQ0FBQyxHQUFHLENBQUMsR0FB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
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_flows_1 = require("@medusajs/medusa/core-flows");
|
|
4
|
+
const utils_1 = require("@medusajs/framework/utils");
|
|
5
|
+
const ordinant_1 = require("../../modules/ordinant");
|
|
6
|
+
/**
|
|
7
|
+
* The checkout gate (Ordinant use case U1): before a cart becomes an order,
|
|
8
|
+
* every line item is checked against the destination's rules. BLOCK or
|
|
9
|
+
* unmet CONDITIONAL requirements stop completion with a citation-bearing
|
|
10
|
+
* message the storefront surfaces as an ordinary checkout error — no
|
|
11
|
+
* storefront changes required.
|
|
12
|
+
*
|
|
13
|
+
* Items are sent as bare SKUs: Ordinant resolves the merchant's CONFIRMED
|
|
14
|
+
* classifications server-side. Unclassified SKUs fail closed by design —
|
|
15
|
+
* classify the catalog (including non-regulated items as accessories)
|
|
16
|
+
* before selling through the gate.
|
|
17
|
+
*
|
|
18
|
+
* Buyer verification assertions (age verification, FFL routing) can be
|
|
19
|
+
* supplied by upstream integrations via cart.metadata.ordinant_buyer; the
|
|
20
|
+
* decision response's `requirement` strings say exactly what is missing.
|
|
21
|
+
*/
|
|
22
|
+
core_flows_1.completeCartWorkflow.hooks.validate(async ({ cart }, { container }) => {
|
|
23
|
+
const logger = container.resolve("logger");
|
|
24
|
+
const ordinant = container.resolve(ordinant_1.ORDINANT_MODULE);
|
|
25
|
+
const address = cart?.shipping_address;
|
|
26
|
+
const countryCode = (address?.country_code ?? "").toLowerCase();
|
|
27
|
+
if (countryCode && countryCode !== "us") {
|
|
28
|
+
// Ordinant coverage is US-only (PRD v1 scope); international carts are
|
|
29
|
+
// outside the gate's jurisdiction model.
|
|
30
|
+
logger.warn(`[ordinant] cart ${cart?.id}: non-US destination (${countryCode}) — gate skipped`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const items = [];
|
|
34
|
+
const skuless = [];
|
|
35
|
+
for (const item of cart?.items ?? []) {
|
|
36
|
+
if (!item?.variant_sku) {
|
|
37
|
+
// A cart line with no SKU cannot be identified, and therefore cannot
|
|
38
|
+
// be gated. Skipping it would be a silent fail-open on a compliance
|
|
39
|
+
// gate, so by default it fails closed (skuMode "block"). Merchants who
|
|
40
|
+
// knowingly sell unregulated SKU-less items opt into skuMode "allow".
|
|
41
|
+
skuless.push(item?.title ?? item?.id ?? "unknown item");
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
items.push({
|
|
45
|
+
sku: item.variant_sku,
|
|
46
|
+
quantity: item.quantity ?? 1,
|
|
47
|
+
// Empty attributes: the engine resolves the merchant's confirmed
|
|
48
|
+
// classification server-side; unclassified SKUs fail closed.
|
|
49
|
+
attributes: {},
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
if (skuless.length > 0 && ordinant.skuMode === "block") {
|
|
53
|
+
logger.warn(`[ordinant] cart ${cart?.id}: ${skuless.length} item(s) without a SKU — ` +
|
|
54
|
+
`blocking (skuMode=block): ${skuless.join(", ")}`);
|
|
55
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `This order cannot be completed: ${skuless.join(", ")} ` +
|
|
56
|
+
`${skuless.length === 1 ? "has" : "have"} no SKU and cannot be ` +
|
|
57
|
+
`checked for compliance. Assign a SKU to every product.`);
|
|
58
|
+
}
|
|
59
|
+
if (items.length === 0) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const state = (address?.province ?? "").toUpperCase();
|
|
63
|
+
if (!state) {
|
|
64
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.INVALID_DATA, "Compliance check requires a shipping address with a US state.");
|
|
65
|
+
}
|
|
66
|
+
const buyer = (cart?.metadata?.ordinant_buyer ?? {});
|
|
67
|
+
let decision;
|
|
68
|
+
try {
|
|
69
|
+
decision = await ordinant.client.decide({
|
|
70
|
+
destination: {
|
|
71
|
+
state,
|
|
72
|
+
zip: address?.postal_code ?? undefined,
|
|
73
|
+
city: address?.city ?? undefined,
|
|
74
|
+
line1: address?.address_1 ?? undefined,
|
|
75
|
+
},
|
|
76
|
+
buyer,
|
|
77
|
+
items,
|
|
78
|
+
}, `medusa-cart:${cart.id}`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
82
|
+
if (ordinant.failMode === "open") {
|
|
83
|
+
logger.error(`[ordinant] cart ${cart.id}: decision engine unavailable (${message}) — ` +
|
|
84
|
+
`failMode=open, completing WITHOUT compliance check`);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
logger.error(`[ordinant] cart ${cart.id}: decision engine unavailable (${message}) — failing closed`);
|
|
88
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, "Compliance check is temporarily unavailable. Please try again shortly.");
|
|
89
|
+
}
|
|
90
|
+
if (decision.disposition === "ALLOW") {
|
|
91
|
+
logger.info(`[ordinant] cart ${cart.id}: ALLOW (decision ${decision.decision_id}, ` +
|
|
92
|
+
`ruleset ${decision.ruleset_version})`);
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const problems = [];
|
|
96
|
+
for (const item of decision.items) {
|
|
97
|
+
problems.push(...describeItem(item));
|
|
98
|
+
}
|
|
99
|
+
logger.warn(`[ordinant] cart ${cart.id}: ${decision.disposition} ` +
|
|
100
|
+
`(decision ${decision.decision_id}): ${problems.join(" | ")}`);
|
|
101
|
+
throw new utils_1.MedusaError(utils_1.MedusaError.Types.NOT_ALLOWED, `This order cannot be completed: ${problems.join(" ")}`);
|
|
102
|
+
});
|
|
103
|
+
function describeItem(item) {
|
|
104
|
+
const out = [];
|
|
105
|
+
for (const effect of item.effects) {
|
|
106
|
+
if (effect.satisfied) {
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (effect.fail_closed) {
|
|
110
|
+
out.push(`${item.sku}: product is not yet classified for compliance ` +
|
|
111
|
+
`(missing ${effect.unknown_attributes?.join(", ") ?? "attributes"}).`);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
const cites = effect.citations.map((c) => c.source).join("; ");
|
|
115
|
+
if (effect.effect.type === "BLOCK") {
|
|
116
|
+
out.push(`${item.sku}: ${effect.description} [${cites}].`);
|
|
117
|
+
}
|
|
118
|
+
else if (effect.requirement) {
|
|
119
|
+
out.push(`${item.sku}: ${effect.requirement} [${cites}].`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
if (out.length === 0 && item.disposition !== "ALLOW") {
|
|
123
|
+
out.push(`${item.sku}: not permitted for this destination.`);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidmFsaWRhdGUtY2FydC1jb21wbGV0aW9uLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vc3JjL3dvcmtmbG93cy9ob29rcy92YWxpZGF0ZS1jYXJ0LWNvbXBsZXRpb24udHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw0REFBa0U7QUFDbEUscURBQXVEO0FBQ3ZELHFEQUF3RDtBQUl4RDs7Ozs7Ozs7Ozs7Ozs7O0dBZUc7QUFDSCxpQ0FBb0IsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEtBQUssRUFBRSxFQUFFLElBQUksRUFBRSxFQUFFLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRTtJQUNwRSxNQUFNLE1BQU0sR0FBRyxTQUFTLENBQUMsT0FBTyxDQUFDLFFBQVEsQ0FBQyxDQUFBO0lBQzFDLE1BQU0sUUFBUSxHQUFHLFNBQVMsQ0FBQyxPQUFPLENBQXdCLDBCQUFlLENBQUMsQ0FBQTtJQUUxRSxNQUFNLE9BQU8sR0FBRyxJQUFJLEVBQUUsZ0JBQWdCLENBQUE7SUFDdEMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxPQUFPLEVBQUUsWUFBWSxJQUFJLEVBQUUsQ0FBQyxDQUFDLFdBQVcsRUFBRSxDQUFBO0lBQy9ELElBQUksV0FBVyxJQUFJLFdBQVcsS0FBSyxJQUFJLEVBQUUsQ0FBQztRQUN4Qyx1RUFBdUU7UUFDdkUseUNBQXlDO1FBQ3pDLE1BQU0sQ0FBQyxJQUFJLENBQ1QsbUJBQW1CLElBQUksRUFBRSxFQUFFLHlCQUF5QixXQUFXLGtCQUFrQixDQUNsRixDQUFBO1FBQ0QsT0FBTTtJQUNSLENBQUM7SUFFRCxNQUFNLEtBQUssR0FBbUIsRUFBRSxDQUFBO0lBQ2hDLE1BQU0sT0FBTyxHQUFhLEVBQUUsQ0FBQTtJQUM1QixLQUFLLE1BQU0sSUFBSSxJQUFJLElBQUksRUFBRSxLQUFLLElBQUksRUFBRSxFQUFFLENBQUM7UUFDckMsSUFBSSxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsQ0FBQztZQUN2QixxRUFB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
|