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.
Files changed (30) hide show
  1. package/.medusa/server/src/admin/index.js +1636 -0
  2. package/.medusa/server/src/admin/index.mjs +1635 -0
  3. package/.medusa/server/src/api/admin/ordinant/classifications/[sku]/confirm/route.js +25 -0
  4. package/.medusa/server/src/api/admin/ordinant/classifications/bulk-confirm/route.js +29 -0
  5. package/.medusa/server/src/api/admin/ordinant/classifications/route.js +23 -0
  6. package/.medusa/server/src/api/admin/ordinant/classify-all/route.js +108 -0
  7. package/.medusa/server/src/api/admin/ordinant/dictionary/route.js +17 -0
  8. package/.medusa/server/src/api/admin/ordinant/generate-all-skus/route.js +31 -0
  9. package/.medusa/server/src/api/admin/ordinant/overview/route.js +69 -0
  10. package/.medusa/server/src/api/admin/ordinant/products/[id]/classify/route.js +18 -0
  11. package/.medusa/server/src/api/admin/ordinant/products/[id]/generate-skus/route.js +34 -0
  12. package/.medusa/server/src/api/admin/ordinant/products/[id]/overview/route.js +59 -0
  13. package/.medusa/server/src/api/admin/ordinant/stats/route.js +17 -0
  14. package/.medusa/server/src/api/admin/ordinant/status/route.js +52 -0
  15. package/.medusa/server/src/api/middlewares.js +14 -0
  16. package/.medusa/server/src/api/store/ordinant/preview/route.js +110 -0
  17. package/.medusa/server/src/api/store/ordinant/preview/validators.js +34 -0
  18. package/.medusa/server/src/lib/client.js +136 -0
  19. package/.medusa/server/src/modules/ordinant/index.js +14 -0
  20. package/.medusa/server/src/modules/ordinant/service.js +27 -0
  21. package/.medusa/server/src/storefront.d.ts +61 -0
  22. package/.medusa/server/src/storefront.js +61 -0
  23. package/.medusa/server/src/subscribers/product-upsert.js +52 -0
  24. package/.medusa/server/src/workflows/bulk-operations.js +59 -0
  25. package/.medusa/server/src/workflows/classify-product.js +80 -0
  26. package/.medusa/server/src/workflows/confirm-classification.js +20 -0
  27. package/.medusa/server/src/workflows/generate-skus.js +81 -0
  28. package/.medusa/server/src/workflows/hooks/validate-cart-completion.js +127 -0
  29. package/README.md +495 -0
  30. 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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