@fayz-ai/shop 0.9.0 → 0.11.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 (163) hide show
  1. package/README.md +18 -1
  2. package/dist/admin.d.ts +2 -0
  3. package/dist/admin.d.ts.map +1 -0
  4. package/dist/catalog.d.ts +1 -3
  5. package/dist/catalog.d.ts.map +1 -1
  6. package/dist/catalog.js +2 -2
  7. package/dist/{chunk-IYQHKMLB.js → chunk-2WACO2TG.js} +4 -4
  8. package/dist/chunk-2WACO2TG.js.map +1 -0
  9. package/dist/chunk-3GERSY5F.js +526 -0
  10. package/dist/chunk-3GERSY5F.js.map +1 -0
  11. package/dist/chunk-6BO2TMZ5.js +93 -0
  12. package/dist/chunk-6BO2TMZ5.js.map +1 -0
  13. package/dist/chunk-FQPGE3YN.js +3 -0
  14. package/dist/{chunk-JR25TISO.js.map → chunk-FQPGE3YN.js.map} +1 -1
  15. package/dist/{chunk-CTKZ6O2J.js → chunk-IKNGCRUM.js} +41 -15
  16. package/dist/chunk-IKNGCRUM.js.map +1 -0
  17. package/dist/chunk-KLDS7IPU.js +17 -0
  18. package/dist/chunk-KLDS7IPU.js.map +1 -0
  19. package/dist/chunk-NWW3WJ7Y.js +136 -0
  20. package/dist/chunk-NWW3WJ7Y.js.map +1 -0
  21. package/dist/chunk-STRNMFC4.js +1438 -0
  22. package/dist/chunk-STRNMFC4.js.map +1 -0
  23. package/dist/chunk-U6HV3LVP.js +1170 -0
  24. package/dist/chunk-U6HV3LVP.js.map +1 -0
  25. package/dist/client.d.ts +2 -0
  26. package/dist/client.d.ts.map +1 -0
  27. package/dist/data/customer-search.d.ts +3 -0
  28. package/dist/data/customer-search.d.ts.map +1 -0
  29. package/dist/data/functions.d.ts +9 -0
  30. package/dist/data/functions.d.ts.map +1 -0
  31. package/dist/data/media.d.ts +37 -0
  32. package/dist/data/media.d.ts.map +1 -0
  33. package/dist/{mock-catalog.d.ts → data/mock-catalog.d.ts} +21 -1
  34. package/dist/data/mock-catalog.d.ts.map +1 -0
  35. package/dist/data/mock-provider.d.ts +165 -0
  36. package/dist/data/mock-provider.d.ts.map +1 -0
  37. package/dist/data/mock.d.ts +3 -0
  38. package/dist/data/mock.d.ts.map +1 -0
  39. package/dist/data/provider.d.ts +335 -0
  40. package/dist/data/provider.d.ts.map +1 -0
  41. package/dist/data/supabase-provider.d.ts +208 -0
  42. package/dist/data/supabase-provider.d.ts.map +1 -0
  43. package/dist/data/tables.d.ts +38 -0
  44. package/dist/data/tables.d.ts.map +1 -0
  45. package/dist/data/tenant.d.ts.map +1 -0
  46. package/dist/entities/entity-reconcile.d.ts +32 -0
  47. package/dist/entities/entity-reconcile.d.ts.map +1 -0
  48. package/dist/entities/entity-sql.d.ts +50 -0
  49. package/dist/entities/entity-sql.d.ts.map +1 -0
  50. package/dist/entities/entity-store.d.ts +58 -0
  51. package/dist/entities/entity-store.d.ts.map +1 -0
  52. package/dist/entities/index.d.ts +10 -0
  53. package/dist/entities/index.d.ts.map +1 -0
  54. package/dist/entities/mock-entity-store.d.ts +17 -0
  55. package/dist/entities/mock-entity-store.d.ts.map +1 -0
  56. package/dist/entities/supabase-entity-store.d.ts +8 -0
  57. package/dist/entities/supabase-entity-store.d.ts.map +1 -0
  58. package/dist/entities.js +159 -0
  59. package/dist/entities.js.map +1 -0
  60. package/dist/index.d.ts +28 -12
  61. package/dist/index.d.ts.map +1 -1
  62. package/dist/index.js +1558 -40
  63. package/dist/index.js.map +1 -1
  64. package/dist/inventory/allocation.d.ts +98 -0
  65. package/dist/inventory/allocation.d.ts.map +1 -0
  66. package/dist/inventory/index.d.ts +13 -0
  67. package/dist/inventory/index.d.ts.map +1 -0
  68. package/dist/inventory/mock-stock-store.d.ts +89 -0
  69. package/dist/inventory/mock-stock-store.d.ts.map +1 -0
  70. package/dist/inventory/postgrest-double.d.ts +65 -0
  71. package/dist/inventory/postgrest-double.d.ts.map +1 -0
  72. package/dist/inventory/shopify-mapping.d.ts +67 -0
  73. package/dist/inventory/shopify-mapping.d.ts.map +1 -0
  74. package/dist/inventory/stock-contract.d.ts +46 -0
  75. package/dist/inventory/stock-contract.d.ts.map +1 -0
  76. package/dist/inventory/stock-store.d.ts +96 -0
  77. package/dist/inventory/stock-store.d.ts.map +1 -0
  78. package/dist/inventory/stock-types.d.ts +351 -0
  79. package/dist/inventory/stock-types.d.ts.map +1 -0
  80. package/dist/inventory/supabase-stock-store.d.ts +60 -0
  81. package/dist/inventory/supabase-stock-store.d.ts.map +1 -0
  82. package/dist/inventory/vocabulary.d.ts +86 -0
  83. package/dist/inventory/vocabulary.d.ts.map +1 -0
  84. package/dist/inventory.js +4 -0
  85. package/dist/inventory.js.map +1 -0
  86. package/dist/lib/catalog.d.ts +4 -0
  87. package/dist/lib/catalog.d.ts.map +1 -0
  88. package/dist/{commerce.d.ts → lib/commerce.d.ts} +2 -2
  89. package/dist/lib/commerce.d.ts.map +1 -0
  90. package/dist/lib/document.d.ts +13 -0
  91. package/dist/lib/document.d.ts.map +1 -0
  92. package/dist/lib/emails.d.ts +50 -0
  93. package/dist/lib/emails.d.ts.map +1 -0
  94. package/dist/lib/message-events.d.ts +4 -0
  95. package/dist/lib/message-events.d.ts.map +1 -0
  96. package/dist/lib/runtime.d.ts +5 -0
  97. package/dist/lib/runtime.d.ts.map +1 -0
  98. package/dist/lib/variant-matrix.d.ts +120 -0
  99. package/dist/lib/variant-matrix.d.ts.map +1 -0
  100. package/dist/mock.d.ts +1 -2
  101. package/dist/mock.d.ts.map +1 -1
  102. package/dist/mock.js +4 -2
  103. package/dist/provider.d.ts +1 -88
  104. package/dist/provider.d.ts.map +1 -1
  105. package/dist/providers/checkout.d.ts +129 -0
  106. package/dist/providers/checkout.d.ts.map +1 -0
  107. package/dist/providers/index.d.ts +5 -0
  108. package/dist/providers/index.d.ts.map +1 -0
  109. package/dist/providers/shipping.d.ts +43 -0
  110. package/dist/providers/shipping.d.ts.map +1 -0
  111. package/dist/providers.js +70 -0
  112. package/dist/providers.js.map +1 -0
  113. package/dist/rules/adapter.d.ts +36 -0
  114. package/dist/rules/adapter.d.ts.map +1 -0
  115. package/dist/rules/attributes.d.ts +64 -0
  116. package/dist/rules/attributes.d.ts.map +1 -0
  117. package/dist/rules/context.d.ts +43 -0
  118. package/dist/rules/context.d.ts.map +1 -0
  119. package/dist/rules/evaluator.d.ts +37 -0
  120. package/dist/rules/evaluator.d.ts.map +1 -0
  121. package/dist/rules/index.d.ts +13 -0
  122. package/dist/rules/index.d.ts.map +1 -0
  123. package/dist/rules/prices.d.ts +25 -0
  124. package/dist/rules/prices.d.ts.map +1 -0
  125. package/dist/rules/rule-types.d.ts +160 -0
  126. package/dist/rules/rule-types.d.ts.map +1 -0
  127. package/dist/rules.js +22 -0
  128. package/dist/rules.js.map +1 -0
  129. package/dist/runtime.d.ts +1 -4
  130. package/dist/runtime.d.ts.map +1 -1
  131. package/dist/runtime.js +5 -3
  132. package/dist/shipping/label-provider.d.ts +43 -0
  133. package/dist/shipping/label-provider.d.ts.map +1 -0
  134. package/dist/types.d.ts +688 -0
  135. package/dist/types.d.ts.map +1 -1
  136. package/package.json +30 -20
  137. package/dist/chunk-CTKZ6O2J.js.map +0 -1
  138. package/dist/chunk-IYQHKMLB.js.map +0 -1
  139. package/dist/chunk-JR25TISO.js +0 -3
  140. package/dist/chunk-SVTCJ6HP.js +0 -440
  141. package/dist/chunk-SVTCJ6HP.js.map +0 -1
  142. package/dist/commerce.d.ts.map +0 -1
  143. package/dist/mock-catalog.d.ts.map +0 -1
  144. package/dist/mock-provider.d.ts +0 -60
  145. package/dist/mock-provider.d.ts.map +0 -1
  146. package/dist/supabase-provider.d.ts +0 -56
  147. package/dist/supabase-provider.d.ts.map +0 -1
  148. package/dist/tables.d.ts +0 -12
  149. package/dist/tables.d.ts.map +0 -1
  150. package/dist/tenant.d.ts.map +0 -1
  151. package/src/catalog.ts +0 -3
  152. package/src/commerce.ts +0 -120
  153. package/src/index.ts +0 -40
  154. package/src/mock-catalog.ts +0 -266
  155. package/src/mock-provider.ts +0 -480
  156. package/src/mock.ts +0 -2
  157. package/src/provider.ts +0 -117
  158. package/src/runtime.ts +0 -20
  159. package/src/supabase-provider.ts +0 -754
  160. package/src/tables.ts +0 -17
  161. package/src/tenant.ts +0 -21
  162. package/src/types.ts +0 -623
  163. /package/dist/{tenant.d.ts → data/tenant.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules/rule-types.ts","../src/rules/attributes.ts","../src/rules/evaluator.ts","../src/rules/adapter.ts","../src/rules/context.ts"],"names":["Registry"],"mappings":"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","file":"chunk-3GERSY5F.js","sourcesContent":["// ---------------------------------------------------------------------------\n// The rule model (spine §3 L2, milestone M6) — ONE rule engine, as data.\n//\n// Adopted nearly verbatim from Medusa's promotion module because it is correct\n// (spine I8): `rule(attribute, operator)` with values in a child table, three\n// rule sets (eligibility / target / buy), and an application_method whose six\n// integers and two rule sets express buy-N-get-M, tiered, BOGO, percentage and\n// fixed across `each | across | once`. \"Buy 3 pay 2\" is pure data, zero code —\n// see evaluator.test.ts (the acceptance test).\n//\n// Everything in this module is SERIALIZABLE AND ENGINE-INDEPENDENT (spine I11):\n// the portable artifact is the rule schema, never the evaluator. A promotion\n// here survives JSON round-tripping, rides in the Store Document's\n// `promotions` section, and is evaluated identically by the TypeScript\n// reference implementation (evaluator.ts) and the SQL function\n// (migrations/0033) — the TS implementation is the spec, the SQL mirrors it.\n//\n// This is a LEAF module: types and enum vocabularies only, so the storefront\n// validator, the document section, and the evaluator can all import it\n// without dragging in providers.\n// ---------------------------------------------------------------------------\n\n/** The 7 rule operators. This is the closed set — widening it is a model\n * change (TS union + DB CHECK + SQL evaluator), not an insert. */\nexport const RULE_OPERATORS = ['gte', 'lte', 'gt', 'lt', 'eq', 'ne', 'in'] as const\nexport type RuleOperator = (typeof RULE_OPERATORS)[number]\n\n/** A rule value is a JSON scalar. The DB stores each in its own\n * plg_shop_rule_values row (values-in-a-child-table, per the model). */\nexport type RuleValue = string | number | boolean\n\n/**\n * One rule: `attribute` names a row of the rule_attribute VOCABULARY (I9 —\n * never a free-form dotted path; an unknown attribute is an I2 hard error\n * naming the valid set), `operator` is one of the 7, `values` is the child\n * list (a single-element list for scalar operators).\n */\nexport interface PromotionRule {\n attribute: string\n operator: RuleOperator\n values: RuleValue[]\n}\n\nexport const APPLICATION_METHOD_TYPES = ['percentage', 'fixed'] as const\nexport type ApplicationMethodType = (typeof APPLICATION_METHOD_TYPES)[number]\n\nexport const APPLICATION_METHOD_TARGET_TYPES = ['order', 'items', 'shipping'] as const\nexport type ApplicationMethodTargetType = (typeof APPLICATION_METHOD_TARGET_TYPES)[number]\n\nexport const APPLICATION_METHOD_ALLOCATIONS = ['each', 'across', 'once'] as const\nexport type ApplicationMethodAllocation = (typeof APPLICATION_METHOD_ALLOCATIONS)[number]\n\n/**\n * HOW an eligible promotion applies. The six numbers + two rule sets that make\n * buy-N-pay-M pure data:\n *\n * - `type` percentage (value = percentage points) or fixed (value = amount).\n * - `targetType` order (spread across all items) | items | shipping.\n * - `allocation` (items only):\n * each — per unit of each eligible line, `maxQuantity` units per line;\n * across — one amount over the eligible subtotal, split proportionally;\n * once — one unit of the cheapest eligible line, once.\n * - `buyRulesMinQuantity` + `buyRules` — what must be in the cart, and how\n * many, for the promotion to fire (buyget). The promotion applies\n * floor(matched buy quantity / buyRulesMinQuantity) times.\n * - `applyToQuantity` — how many target units each firing discounts.\n * - `targetRules` — which items the discount lands on. A buyget whose target\n * is not in the cart but is pinned to one product id emits addFreeItem.\n */\nexport interface ApplicationMethod {\n type: ApplicationMethodType\n value: number\n targetType: ApplicationMethodTargetType\n allocation?: ApplicationMethodAllocation\n /** Unit cap: per line for `each`; total discounted/free units for buyget. */\n maxQuantity?: number | null\n /** Buyget: target units discounted per firing. */\n applyToQuantity?: number | null\n /** Buyget: matched buy quantity required per firing. Null/absent = standard. */\n buyRulesMinQuantity?: number | null\n /** What gets discounted (the `target` pivot). Absent = every item. */\n targetRules?: PromotionRule[]\n /** What must be bought (the `buy` pivot). Item-scoped attributes only. */\n buyRules?: PromotionRule[]\n}\n\nexport const PROMOTION_TYPES = ['standard', 'buyget'] as const\nexport type PromotionType = (typeof PROMOTION_TYPES)[number]\n\nexport const PROMOTION_STATUSES = ['draft', 'active', 'inactive'] as const\nexport type PromotionStatus = (typeof PROMOTION_STATUSES)[number]\n\n/**\n * A promotion: eligibility rules (the `eligibility` pivot) + one application\n * method. `code` promotions fire only when the shopper entered the code\n * (cart.codes); `isAutomatic` promotions fire whenever eligible.\n */\nexport interface Promotion {\n id: string\n /** Coupon code. Null/absent = automatic-only. Matched case-insensitively. */\n code?: string | null\n isAutomatic?: boolean\n /** Derivable (buyRulesMinQuantity ⇒ buyget) but stored for the admin UI. */\n type?: PromotionType\n status?: PromotionStatus\n campaignId?: string | null\n /** Does this promotion apply at all? (the `eligibility` pivot). AND across\n * rules; a rule's values are OR. Absent/empty = always eligible. */\n rules?: PromotionRule[]\n applicationMethod: ApplicationMethod\n startsAt?: string | null\n endsAt?: string | null\n usageLimit?: number | null\n timesUsed?: number\n}\n\n/** A campaign groups promotions for reporting/budgets. Parent row only in M6;\n * budget enforcement is a later milestone. */\nexport interface Campaign {\n id: string\n name: string\n startsAt?: string | null\n endsAt?: string | null\n}\n\n// --- The cart snapshot the engine evaluates against ---------------------------\n\n/** One cart line as the engine sees it. Prices are authoritative-catalog\n * prices; the engine never trusts a client-computed total. */\nexport interface RuleCartItem {\n /** Line identity actions refer back to. Defaults to productId. */\n id?: string\n productId: string\n quantity: number\n unitPrice: number\n categoryId?: string | null\n collectionId?: string | null\n}\n\n/**\n * The evaluation input. Serializable on purpose: the SAME snapshot shape is\n * the TS evaluator's input and the SQL function's `p_cart` jsonb, so the\n * preview a shopper sees and the revalidation place_order runs read one\n * contract.\n */\nexport interface RuleCartSnapshot {\n items: RuleCartItem[]\n /** Quoted shipping — what a shipping-target promotion discounts. */\n shippingTotal?: number | null\n /** Shipping region/zone identifier (the shipping.region attribute). */\n region?: string | null\n customer?: { email?: string | null; tags?: string[] }\n /** Promotion codes the shopper entered (the code attribute; case-insensitive). */\n codes?: string[]\n currency?: string\n}\n\n// --- Compute actions (the engine's output) ------------------------------------\n\n/** Discount one cart line by an absolute amount (already allocation-resolved). */\nexport interface AddItemDiscountAction {\n action: 'addItemDiscount'\n promotionId: string\n code?: string | null\n itemId: string\n amount: number\n}\n\n/** Discount the shipping total by an absolute amount. */\nexport interface AddShippingDiscountAction {\n action: 'addShippingDiscount'\n promotionId: string\n code?: string | null\n amount: number\n}\n\n/** Add `quantity` free units of a product the buyget promotion grants but the\n * cart does not contain. The cart applies it by adding the line at price 0. */\nexport interface AddFreeItemAction {\n action: 'addFreeItem'\n promotionId: string\n code?: string | null\n productId: string\n quantity: number\n}\n\n/**\n * The ordered action list computePromotionActions returns. Deterministic:\n * buyget promotions first (they consume target units), then by specificity\n * (total rules count DESC), then id — so replaying the same cart against the\n * same promotions always yields byte-identical actions.\n */\nexport type ComputeAction = AddItemDiscountAction | AddShippingDiscountAction | AddFreeItemAction\n\n/** Sum of a cart's line totals, cents-rounded — the number `cart.subtotal`\n * rules compare against. */\nexport function cartSubtotal(cart: RuleCartSnapshot): number {\n return roundCents(cart.items.reduce((sum, item) => sum + item.unitPrice * item.quantity, 0))\n}\n\n/** Cents rounding, the house money convention (commerce.ts round2). */\nexport function roundCents(value: number): number {\n return Math.round(value * 100) / 100\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport { RULE_OPERATORS } from './rule-types'\nimport type { RuleOperator } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// The rule attribute VOCABULARY — data, not a hardcoded list (spine I9).\n//\n// Medusa's promotion engine accepts any dotted path, its admin whitelist is a\n// literal array in the API layer, and the real bound is a hardcoded 50-entry\n// field list that silently decides which rules can ever match — a rule on an\n// unknown attribute evaluates against `undefined` and just fails. The fix is\n// to publish `rule_attribute(key, path, label, field_type, value_source,\n// operators[])` as ROWS: the admin form renders itself from the same data the\n// AI reads (describeStore), validateStore checks documents against it, the\n// evaluator refuses attributes outside it (I2, never a silent non-match), and\n// WIDENING the vocabulary is an insert — defineRuleContextProvider for code,\n// a plain INSERT for SQL.\n//\n// The TS registry here and the plg_shop_rule_attributes seed rows (migration\n// 0032) carry the SAME core vocabulary; migrations.test.ts cross-checks the\n// two so they cannot drift.\n// ---------------------------------------------------------------------------\n\nexport const RULE_ATTRIBUTE_FIELD_TYPES = ['text', 'number', 'boolean'] as const\nexport type RuleAttributeFieldType = (typeof RULE_ATTRIBUTE_FIELD_TYPES)[number]\n\n/**\n * One vocabulary row.\n *\n * `path` locates the value in the evaluation context built from the cart\n * snapshot. Paths under `items.` are ITEM-scoped: they evaluate per cart line\n * (scoping target/buy rules to lines; for eligibility, \"some line matches\").\n * Every other path is CART-scoped, walked from the snapshot root. A context\n * provider's attributes resolve by KEY from the provided context instead.\n *\n * `value_source` names the id list an admin form offers as values\n * ('products', 'categories', …) — a UI hint, never an evaluation input.\n */\nexport interface RuleAttribute {\n key: string\n path: string\n label: string\n fieldType: RuleAttributeFieldType\n /** Where an admin form fetches candidate values. Null = free entry. */\n valueSource?: string | null\n /** The operators this attribute supports — a subset of the 7. */\n operators: readonly RuleOperator[]\n}\n\nconst ID_OPERATORS: readonly RuleOperator[] = ['eq', 'ne', 'in']\nconst NUMBER_OPERATORS: readonly RuleOperator[] = ['gte', 'lte', 'gt', 'lt', 'eq', 'ne']\n\n/** Prefix that marks an attribute path as item-scoped. */\nexport const ITEM_PATH_PREFIX = 'items.'\n\n/**\n * The core vocabulary — what every Fayz-backed store starts with, mirrored as\n * the plg_shop_rule_attributes seed rows in migration 0032.\n */\nexport const CORE_RULE_ATTRIBUTES: readonly RuleAttribute[] = [\n {\n key: 'code',\n path: 'codes',\n label: 'Cupom informado',\n fieldType: 'text',\n valueSource: null,\n operators: ['eq', 'in'],\n },\n {\n key: 'cart.subtotal',\n path: 'subtotal',\n label: 'Subtotal do carrinho',\n fieldType: 'number',\n valueSource: null,\n operators: NUMBER_OPERATORS,\n },\n {\n key: 'item.quantity',\n path: 'items.quantity',\n label: 'Quantidade do item',\n fieldType: 'number',\n valueSource: null,\n operators: NUMBER_OPERATORS,\n },\n {\n key: 'product.id',\n path: 'items.product_id',\n label: 'Produto',\n fieldType: 'text',\n valueSource: 'products',\n operators: ID_OPERATORS,\n },\n {\n key: 'product.category_id',\n path: 'items.category_id',\n label: 'Categoria do produto',\n fieldType: 'text',\n valueSource: 'categories',\n operators: ID_OPERATORS,\n },\n {\n key: 'product.collection_id',\n path: 'items.collection_id',\n label: 'Coleção do produto',\n fieldType: 'text',\n valueSource: 'collections',\n operators: ID_OPERATORS,\n },\n {\n key: 'customer.email',\n path: 'customer.email',\n label: 'E-mail do cliente',\n fieldType: 'text',\n valueSource: null,\n operators: ID_OPERATORS,\n },\n {\n key: 'customer.tag',\n path: 'customer.tags',\n label: 'Tag do cliente',\n fieldType: 'text',\n valueSource: 'customer_tags',\n operators: ['eq', 'ne', 'in'],\n },\n {\n key: 'shipping.region',\n path: 'shipping.region',\n label: 'Região de entrega',\n fieldType: 'text',\n valueSource: 'regions',\n operators: ID_OPERATORS,\n },\n]\n\n// --- The registry -------------------------------------------------------------\n// Uniform Registry<T> meta envelope (M1), same as blocks/loaders/handles, so\n// `describeRuleAttributes()` feeds describeStore through the same parser every\n// other registry uses.\n\nexport const ruleAttributeRegistry = new Registry<RuleAttribute>('ruleAttribute')\n\nlet coreRegistered = false\n\n/** Idempotent — mirrors registerStorefrontBlocks: consumers call before reads. */\nexport function registerCoreRuleAttributes(): void {\n if (coreRegistered && ruleAttributeRegistry.has('code')) return\n for (const attribute of CORE_RULE_ATTRIBUTES) {\n ruleAttributeRegistry.register(attribute.key, attribute, {\n title: attribute.label,\n description: `Rule attribute (${attribute.fieldType}) — operators: ${attribute.operators.join(', ')}`,\n source: 'sdk',\n })\n }\n coreRegistered = true\n}\n\nexport interface RegisterRuleAttributeOptions {\n meta?: RegistryMeta\n /** Re-registering an existing key is an error unless the caller owns it\n * (module re-evaluation under HMR re-registers the same declaration). */\n replace?: boolean\n}\n\n/**\n * Widen the vocabulary with one attribute. Duplicate keys are an I2 hard\n * error naming the taken set — two providers silently fighting over one key\n * would make rules mean different things on different renders.\n */\nexport function registerRuleAttribute(attribute: RuleAttribute, options: RegisterRuleAttributeOptions = {}): void {\n registerCoreRuleAttributes()\n if (!attribute.key) throw new Error('registerRuleAttribute: attribute.key is required')\n if (!RULE_ATTRIBUTE_FIELD_TYPES.includes(attribute.fieldType)) {\n throw new Error(\n `registerRuleAttribute: \"${attribute.key}\" has unknown fieldType \"${attribute.fieldType}\". ` +\n `Valid field types: ${RULE_ATTRIBUTE_FIELD_TYPES.join(', ')}`,\n )\n }\n for (const op of attribute.operators) {\n if (!RULE_OPERATORS.includes(op)) {\n throw new Error(\n `registerRuleAttribute: \"${attribute.key}\" declares unknown operator \"${op}\". ` +\n `Valid operators: ${RULE_OPERATORS.join(', ')}`,\n )\n }\n }\n if (ruleAttributeRegistry.has(attribute.key) && !options.replace) {\n throw new Error(\n `registerRuleAttribute: attribute \"${attribute.key}\" is already registered. ` +\n `Registered attributes: ${listRuleAttributeKeys().join(', ')}`,\n )\n }\n ruleAttributeRegistry.register(attribute.key, attribute, {\n title: attribute.label,\n description: `Rule attribute (${attribute.fieldType}) — operators: ${attribute.operators.join(', ')}`,\n source: 'app',\n ...options.meta,\n })\n}\n\n/** The attribute for a key, or an I2 hard error naming the valid set — the\n * only lookup the evaluator and the validators may use. */\nexport function resolveRuleAttribute(key: string): RuleAttribute {\n registerCoreRuleAttributes()\n const attribute = ruleAttributeRegistry.get(key)\n if (!attribute) {\n throw new Error(`Unknown rule attribute \"${key}\". Registered attributes: ${listRuleAttributeKeys().join(', ')}`)\n }\n return attribute\n}\n\nexport function hasRuleAttribute(key: string): boolean {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry.has(key)\n}\n\n/** Every attribute key, sorted — the valid set I2 errors name. */\nexport function listRuleAttributeKeys(): string[] {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry.ids().sort()\n}\n\n/** Every vocabulary row, sorted by key — what describeStore publishes and the\n * admin promotion form renders itself from. */\nexport function listRuleAttributes(): RuleAttribute[] {\n registerCoreRuleAttributes()\n return ruleAttributeRegistry\n .list()\n .map((entry) => entry.value)\n .sort((a, b) => a.key.localeCompare(b.key))\n}\n\n/** True when the attribute evaluates per cart line. */\nexport function isItemScopedAttribute(attribute: RuleAttribute): boolean {\n return attribute.path.startsWith(ITEM_PATH_PREFIX)\n}\n\n/** Test seam — suites that widen the vocabulary must not leak into the next.\n * Core attributes re-register on the next read. */\nexport function clearRuleAttributes(): void {\n ruleAttributeRegistry.clear()\n coreRegistered = false\n}\n","import { isItemScopedAttribute, resolveRuleAttribute } from './attributes'\nimport type { RuleAttribute } from './attributes'\nimport type { RuleContext } from './context'\nimport {\n cartSubtotal,\n roundCents,\n} from './rule-types'\nimport type {\n ApplicationMethod,\n ComputeAction,\n Promotion,\n PromotionRule,\n RuleCartItem,\n RuleCartSnapshot,\n RuleValue,\n} from './rule-types'\n\n// ---------------------------------------------------------------------------\n// computePromotionActions — the TypeScript REFERENCE implementation of the\n// rule engine (spine §3 L2, M6). Pure: (cart snapshot, promotions, context) →\n// ordered actions. No provider, no clock (injected), no I/O — which is what\n// makes it exhaustively unit-testable and runnable in the storefront preview,\n// the mock provider and (later) a server, unchanged.\n//\n// THIS FILE IS THE SPEC. The SQL function shop_compute_promotion_actions\n// (packages/shop/migrations/0033_rule_engine_eval.sql) mirrors these\n// semantics for the trust boundary — place_order revalidates server-side so\n// the browser can never set what it pays. Change semantics HERE first, with\n// tests, then re-emit the SQL to match.\n//\n// Semantics, precisely:\n// - A promotion is eligible when its status/date/usage gates pass, its code\n// (if any) was entered, and ALL eligibility rules match (a rule's values\n// are OR; item-scoped eligibility rules match when SOME line matches).\n// - Unknown rule attributes are an I2 hard error naming the valid set —\n// never a silent non-match (the Medusa failure I9 exists to end).\n// - Buyget (buyRulesMinQuantity ≥ 1): the promotion fires\n// floor(matched buy qty / buyRulesMinQuantity) times, discounting\n// applyToQuantity target units per firing, cheapest-eligible-units first,\n// capped by maxQuantity. A target pinned to one product id that is absent\n// from the cart becomes addFreeItem instead.\n// - Standard: percentage|fixed over order (spread proportionally) | items\n// (each: per-unit per line, maxQuantity units per line; across: one amount\n// split proportionally over the eligible subtotal; once: one unit of the\n// cheapest eligible line) | shipping.\n// - Output order is deterministic: buyget before standard (they consume\n// units), then total rules count DESC (specific before generic), then id.\n// ---------------------------------------------------------------------------\n\nexport interface EvaluateOptions {\n /** Extra attribute values, keyed by attribute KEY — the validated output of\n * buildRuleContext (context providers). Wins over snapshot resolution. */\n context?: RuleContext\n /** ISO timestamp the date-window gates compare against. Default: now. */\n now?: string\n}\n\n// --- Attribute resolution -----------------------------------------------------\n\n/** items.* path suffix → RuleCartItem field. The vocabulary speaks the wire's\n * snake_case (it is also the SQL function's vocabulary); the TS snapshot is\n * camelCase. */\nconst ITEM_FIELDS: Record<string, keyof RuleCartItem> = {\n quantity: 'quantity',\n unit_price: 'unitPrice',\n product_id: 'productId',\n category_id: 'categoryId',\n collection_id: 'collectionId',\n}\n\nfunction itemValue(item: RuleCartItem, attribute: RuleAttribute): unknown {\n const field = ITEM_FIELDS[attribute.path.slice('items.'.length)]\n return field ? item[field] : undefined\n}\n\n/** Cart-scoped resolution: context (by key) wins, then a dot-walk of the\n * snapshot-derived root. Returns a flat list — array values (codes, tags)\n * flatten so `eq` means membership. */\nfunction cartValues(cart: RuleCartSnapshot, attribute: RuleAttribute, context: RuleContext): unknown[] {\n if (attribute.key in context) return flatten(context[attribute.key])\n const root: Record<string, unknown> = {\n subtotal: cartSubtotal(cart),\n codes: (cart.codes ?? []).map((code) => code.trim().toUpperCase()),\n customer: { email: cart.customer?.email ?? null, tags: cart.customer?.tags ?? [] },\n shipping: { region: cart.region ?? null, total: cart.shippingTotal ?? null },\n }\n let value: unknown = root\n for (const segment of attribute.path.split('.')) {\n if (value === null || typeof value !== 'object') return []\n value = (value as Record<string, unknown>)[segment]\n }\n return flatten(value)\n}\n\nfunction flatten(value: unknown): unknown[] {\n if (value === null || value === undefined) return []\n return Array.isArray(value) ? value.filter((v) => v !== null && v !== undefined) : [value]\n}\n\n// --- Operator matching --------------------------------------------------------\n\nfunction scalarEq(a: unknown, b: RuleValue): boolean {\n if (typeof a === 'string' && typeof b === 'string') return a.toUpperCase() === b.toUpperCase()\n return a === b\n}\n\nfunction numeric(op: (a: number, b: number) => boolean, values: unknown[], ruleValues: RuleValue[]): boolean {\n const bound = Number(ruleValues[0])\n if (!Number.isFinite(bound)) return false\n return values.some((value) => {\n const n = Number(value)\n return Number.isFinite(n) && op(n, bound)\n })\n}\n\n/** One rule against a flat value list. Values are OR; `ne` is a negative and\n * therefore holds only when NO value equals any rule value. */\nexport function matchRuleValues(rule: PromotionRule, values: unknown[]): boolean {\n switch (rule.operator) {\n case 'eq':\n case 'in':\n return values.some((value) => rule.values.some((rv) => scalarEq(value, rv)))\n case 'ne':\n return !values.some((value) => rule.values.some((rv) => scalarEq(value, rv)))\n case 'gt':\n return numeric((a, b) => a > b, values, rule.values)\n case 'gte':\n return numeric((a, b) => a >= b, values, rule.values)\n case 'lt':\n return numeric((a, b) => a < b, values, rule.values)\n case 'lte':\n return numeric((a, b) => a <= b, values, rule.values)\n }\n}\n\nfunction ruleMatchesCart(rule: PromotionRule, cart: RuleCartSnapshot, context: RuleContext): boolean {\n const attribute = resolveRuleAttribute(rule.attribute)\n if (isItemScopedAttribute(attribute) && !(attribute.key in context)) {\n // Item-scoped at cart level: \"some line matches\".\n return cart.items.some((item) => matchRuleValues(rule, flatten(itemValue(item, attribute))))\n }\n return matchRuleValues(rule, cartValues(cart, attribute, context))\n}\n\nfunction ruleMatchesItem(rule: PromotionRule, item: RuleCartItem, cart: RuleCartSnapshot, context: RuleContext): boolean {\n const attribute = resolveRuleAttribute(rule.attribute)\n if (isItemScopedAttribute(attribute) && !(attribute.key in context)) {\n return matchRuleValues(rule, flatten(itemValue(item, attribute)))\n }\n // A cart-scoped rule inside a target/buy set gates the whole set.\n return matchRuleValues(rule, cartValues(cart, attribute, context))\n}\n\n/** ALL rules match (AND). Empty/absent = match. */\nexport function rulesMatchCart(rules: PromotionRule[] | undefined, cart: RuleCartSnapshot, context: RuleContext = {}): boolean {\n return (rules ?? []).every((rule) => ruleMatchesCart(rule, cart, context))\n}\n\nfunction itemsMatching(rules: PromotionRule[] | undefined, cart: RuleCartSnapshot, context: RuleContext): RuleCartItem[] {\n const list = rules ?? []\n return cart.items.filter((item) => list.every((rule) => ruleMatchesItem(rule, item, cart, context)))\n}\n\n// --- Eligibility gates --------------------------------------------------------\n\nexport type PromotionGateReason =\n | 'inactive'\n | 'not_started'\n | 'expired'\n | 'usage_limit'\n | 'code_not_entered'\n | 'rules_not_matched'\n\n/**\n * Why a promotion does NOT fire against this cart, or null when it does.\n * Shared by the evaluator and the coupon-surface adapter so a rejected code\n * reports the same machine-readable reason everywhere.\n */\nexport function promotionGateReason(\n promotion: Promotion,\n cart: RuleCartSnapshot,\n options: EvaluateOptions = {},\n): PromotionGateReason | null {\n const now = options.now ?? new Date().toISOString()\n if (promotion.status !== undefined && promotion.status !== 'active') return 'inactive'\n if (promotion.startsAt && promotion.startsAt > now) return 'not_started'\n if (promotion.endsAt && promotion.endsAt < now) return 'expired'\n if (promotion.usageLimit != null && (promotion.timesUsed ?? 0) >= promotion.usageLimit) return 'usage_limit'\n if (promotion.code) {\n const entered = (cart.codes ?? []).map((code) => code.trim().toUpperCase())\n if (!entered.includes(promotion.code.trim().toUpperCase())) return 'code_not_entered'\n } else if (promotion.isAutomatic === false) {\n // No code AND not automatic: nothing can ever trigger it.\n return 'code_not_entered'\n }\n if (!rulesMatchCart(promotion.rules, cart, options.context ?? {})) return 'rules_not_matched'\n return null\n}\n\n// --- Money helpers ------------------------------------------------------------\n\nfunction clampPercent(value: number): number {\n return Math.min(Math.max(value, 0), 100)\n}\n\n/** Split `total` across weights, in cents, largest-remainder — the parts sum\n * to exactly `total` so a spread discount never loses or invents a cent. */\nexport function splitProportionally(total: number, weights: number[]): number[] {\n const sum = weights.reduce((acc, weight) => acc + weight, 0)\n if (sum <= 0 || total <= 0) return weights.map(() => 0)\n const cents = Math.round(total * 100)\n const raw = weights.map((weight) => (cents * weight) / sum)\n const floors = raw.map(Math.floor)\n let remainder = cents - floors.reduce((acc, value) => acc + value, 0)\n const order = raw\n .map((value, index) => ({ index, frac: value - Math.floor(value) }))\n .sort((a, b) => b.frac - a.frac || a.index - b.index)\n for (const { index } of order) {\n if (remainder <= 0) break\n floors[index] += 1\n remainder -= 1\n }\n return floors.map((value) => value / 100)\n}\n\n// --- Per-promotion application ------------------------------------------------\n\ninterface ItemLike {\n item: RuleCartItem\n id: string\n}\n\nfunction lineId(item: RuleCartItem): string {\n return item.id ?? item.productId\n}\n\nfunction unitDiscount(method: ApplicationMethod, unitPrice: number): number {\n if (method.type === 'percentage') return roundCents((unitPrice * clampPercent(method.value)) / 100)\n return Math.min(method.value, unitPrice)\n}\n\nfunction isBuyget(promotion: Promotion): boolean {\n const min = promotion.applicationMethod.buyRulesMinQuantity\n return promotion.type === 'buyget' || (min != null && min >= 1)\n}\n\nfunction applyBuyget(promotion: Promotion, cart: RuleCartSnapshot, context: RuleContext): ComputeAction[] {\n const method = promotion.applicationMethod\n const min = method.buyRulesMinQuantity ?? 1\n const per = method.applyToQuantity ?? 1\n const buyQty = itemsMatching(method.buyRules, cart, context).reduce((sum, item) => sum + item.quantity, 0)\n const times = Math.floor(buyQty / Math.max(min, 1))\n if (times <= 0 || per <= 0) return []\n\n let grantUnits = times * per\n if (method.maxQuantity != null) grantUnits = Math.min(grantUnits, method.maxQuantity)\n if (grantUnits <= 0) return []\n\n // Cheapest eligible units first — the shopper pays for the dearer ones.\n const targets: ItemLike[] = itemsMatching(method.targetRules, cart, context)\n .map((item) => ({ item, id: lineId(item) }))\n .sort((a, b) => a.item.unitPrice - b.item.unitPrice || a.id.localeCompare(b.id))\n\n if (targets.length === 0) {\n // Nothing to discount in the cart. A target pinned to exactly one product\n // id is a grant of something addable — the free-item case.\n const pins = (method.targetRules ?? [])\n .filter((rule) => rule.attribute === 'product.id' && (rule.operator === 'eq' || rule.operator === 'in'))\n .flatMap((rule) => rule.values)\n if (pins.length === 1 && typeof pins[0] === 'string') {\n return [{\n action: 'addFreeItem',\n promotionId: promotion.id,\n code: promotion.code ?? null,\n productId: pins[0],\n quantity: grantUnits,\n }]\n }\n return []\n }\n\n const actions: ComputeAction[] = []\n let remaining = grantUnits\n for (const target of targets) {\n if (remaining <= 0) break\n const units = Math.min(target.item.quantity, remaining)\n const amount = roundCents(unitDiscount(method, target.item.unitPrice) * units)\n remaining -= units\n if (amount <= 0) continue\n actions.push({\n action: 'addItemDiscount',\n promotionId: promotion.id,\n code: promotion.code ?? null,\n itemId: target.id,\n amount,\n })\n }\n return actions\n}\n\nfunction applyStandard(promotion: Promotion, cart: RuleCartSnapshot, context: RuleContext): ComputeAction[] {\n const method = promotion.applicationMethod\n const code = promotion.code ?? null\n\n if (method.targetType === 'shipping') {\n const shipping = cart.shippingTotal ?? 0\n if (shipping <= 0) return []\n const amount =\n method.type === 'percentage'\n ? roundCents((shipping * clampPercent(method.value)) / 100)\n : roundCents(Math.min(method.value, shipping))\n if (amount <= 0) return []\n return [{ action: 'addShippingDiscount', promotionId: promotion.id, code, amount }]\n }\n\n const eligible: ItemLike[] =\n method.targetType === 'order'\n ? cart.items.map((item) => ({ item, id: lineId(item) }))\n : itemsMatching(method.targetRules, cart, context).map((item) => ({ item, id: lineId(item) }))\n if (eligible.length === 0) return []\n\n const lineTotals = eligible.map(({ item }) => roundCents(item.unitPrice * item.quantity))\n const eligibleTotal = roundCents(lineTotals.reduce((sum, value) => sum + value, 0))\n const emit = (amounts: number[]): ComputeAction[] =>\n amounts.flatMap((amount, index) =>\n amount > 0\n ? [{ action: 'addItemDiscount' as const, promotionId: promotion.id, code, itemId: eligible[index].id, amount }]\n : [],\n )\n\n const allocation = method.targetType === 'order' ? 'across' : (method.allocation ?? 'across')\n\n if (allocation === 'each') {\n return emit(\n eligible.map(({ item }) => {\n const units = method.maxQuantity != null ? Math.min(item.quantity, method.maxQuantity) : item.quantity\n return roundCents(unitDiscount(method, item.unitPrice) * units)\n }),\n )\n }\n\n if (allocation === 'once') {\n // One unit of the cheapest eligible line, once.\n let cheapest = 0\n for (let i = 1; i < eligible.length; i++) {\n if (eligible[i].item.unitPrice < eligible[cheapest].item.unitPrice) cheapest = i\n }\n const amounts = eligible.map(() => 0)\n amounts[cheapest] = unitDiscount(method, eligible[cheapest].item.unitPrice)\n return emit(amounts)\n }\n\n // across (and order): one amount over the eligible subtotal, split\n // proportionally so the parts sum exactly.\n const total =\n method.type === 'percentage'\n ? roundCents((eligibleTotal * clampPercent(method.value)) / 100)\n : roundCents(Math.min(method.value, eligibleTotal))\n return emit(splitProportionally(total, lineTotals))\n}\n\n// --- Ordering -----------------------------------------------------------------\n\nfunction totalRulesCount(promotion: Promotion): number {\n return (\n (promotion.rules?.length ?? 0) +\n (promotion.applicationMethod.targetRules?.length ?? 0) +\n (promotion.applicationMethod.buyRules?.length ?? 0)\n )\n}\n\n/** Deterministic evaluation order: buyget first, specific before generic, id\n * as the final tiebreak. Exported so the SQL mirror's ORDER BY has a named\n * TS counterpart to test against. */\nexport function orderPromotions(promotions: Promotion[]): Promotion[] {\n return [...promotions].sort((a, b) => {\n const buyget = Number(isBuyget(b)) - Number(isBuyget(a))\n if (buyget !== 0) return buyget\n const specificity = totalRulesCount(b) - totalRulesCount(a)\n if (specificity !== 0) return specificity\n return a.id.localeCompare(b.id)\n })\n}\n\n// --- The engine ---------------------------------------------------------------\n\n/**\n * Evaluate promotions against a cart snapshot → the ordered compute-actions\n * the cart applies. Pure and deterministic (inject `now` for date gates).\n */\nexport function computePromotionActions(\n cart: RuleCartSnapshot,\n promotions: Promotion[],\n options: EvaluateOptions = {},\n): ComputeAction[] {\n const context = options.context ?? {}\n const actions: ComputeAction[] = []\n for (const promotion of orderPromotions(promotions)) {\n if (promotionGateReason(promotion, cart, options) !== null) continue\n actions.push(\n ...(isBuyget(promotion)\n ? applyBuyget(promotion, cart, context)\n : applyStandard(promotion, cart, context)),\n )\n }\n return actions\n}\n\n/** Total item+shipping discount an action list carries — the preview number a\n * cart shows. Free items carry no amount (they enter the cart at price 0). */\nexport function actionsDiscountTotal(actions: ComputeAction[]): number {\n return roundCents(\n actions.reduce((sum, action) => sum + ('amount' in action ? action.amount : 0), 0),\n )\n}\n","import { promotionGateReason } from './evaluator'\nimport type { EvaluateOptions } from './evaluator'\nimport type { Discount } from '../types'\nimport type { ApplicationMethod, Promotion, PromotionRule, RuleCartSnapshot } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// The legacy-coupon adapter — how the EXISTING discount surface keeps working\n// while its internals retire onto the rule engine (M6 deliverable: \"a percent\n// code = promotion with one eligibility rule on code + percentage application\n// method\").\n//\n// Nothing here invents semantics: every plg_shop_discounts row (and every\n// StorefrontDiscountConfig `{code, percent}` entry) is EXPRESSIBLE in the rule\n// model, so the adapter is a pure translation:\n//\n// code → eligibility rule {code eq CODE} (method 'code')\n// minSubtotal → eligibility rule {cart.subtotal gte N}\n// percentage → applicationMethod {type percentage, value}\n// fixed_amount → applicationMethod {type fixed, value}\n// free_shipping → applicationMethod {type percentage, value 100, target shipping}\n// appliesTo products/categories → target rules {product.id/category_id in [...]}\n//\n// `buy_x_get_y` is the one legacy type that CANNOT ride: the legacy row has no\n// fields for X or Y (which is itself the evidence the old model was too\n// small). The adapter returns null for it; a store that wants buyget writes a\n// real promotion, where buy-3-pay-2 is six integers of data.\n//\n// `oncePerCustomer` does not translate either — it is a usage constraint on\n// (promotion, customer), not a cart predicate, and stays enforced where it\n// always was: server-side in shop_place_order.\n// ---------------------------------------------------------------------------\n\n/** The storefront config shape (`config.discounts` entries). */\nexport interface StorefrontPercentCode {\n code: string\n percent: number\n title?: string\n}\n\n/** `{code, percent}` — the simplest promotion there is. */\nexport function storefrontDiscountToPromotion(discount: StorefrontPercentCode): Promotion {\n const code = discount.code.trim().toUpperCase()\n return {\n id: `config-discount:${code}`,\n code,\n type: 'standard',\n status: 'active',\n rules: [{ attribute: 'code', operator: 'eq', values: [code] }],\n applicationMethod: { type: 'percentage', value: discount.percent, targetType: 'order' },\n }\n}\n\n/**\n * A provider Discount row as a Promotion, or null for the untranslatable\n * legacy `buy_x_get_y` (see header). Pure; tested against the existing\n * coupon fixtures in adapter.test.ts.\n */\nexport function discountToPromotion(discount: Discount): Promotion | null {\n if (discount.type === 'buy_x_get_y') return null\n\n const rules: PromotionRule[] = []\n const code = discount.code ? discount.code.trim().toUpperCase() : null\n if (discount.method !== 'automatic' && code) {\n rules.push({ attribute: 'code', operator: 'eq', values: [code] })\n }\n if (discount.minSubtotal != null) {\n rules.push({ attribute: 'cart.subtotal', operator: 'gte', values: [discount.minSubtotal] })\n }\n\n let targetRules: PromotionRule[] | undefined\n if (discount.appliesTo === 'products' && discount.productIds.length > 0) {\n targetRules = [{ attribute: 'product.id', operator: 'in', values: [...discount.productIds] }]\n } else if (discount.appliesTo === 'categories' && discount.categoryIds.length > 0) {\n targetRules = [{ attribute: 'product.category_id', operator: 'in', values: [...discount.categoryIds] }]\n }\n\n let applicationMethod: ApplicationMethod\n if (discount.type === 'free_shipping') {\n applicationMethod = { type: 'percentage', value: 100, targetType: 'shipping' }\n } else {\n applicationMethod = {\n type: discount.type === 'percentage' ? 'percentage' : 'fixed',\n value: discount.value,\n // A scoped legacy coupon discounts its eligible lines; an unscoped one\n // is order-wide. Same split 0027 makes in shop_place_order.\n targetType: targetRules ? 'items' : 'order',\n ...(targetRules ? { allocation: 'across' as const, targetRules } : {}),\n }\n }\n\n return {\n id: `discount:${discount.id}`,\n code,\n isAutomatic: discount.method === 'automatic',\n type: 'standard',\n status: discount.status === 'active' ? 'active' : 'inactive',\n rules,\n applicationMethod,\n startsAt: discount.startsAt || null,\n endsAt: discount.endsAt || null,\n usageLimit: discount.usageLimit,\n timesUsed: discount.timesUsed,\n }\n}\n\n// --- The percent-preview surface ----------------------------------------------\n\nexport interface CouponPreview {\n valid: boolean\n /** Percentage points, when the promotion is an order-wide percentage — the\n * only shape the client-side percent cart model can preview. */\n percent: number\n /** Machine-readable reason when invalid — the useDiscountValidator message\n * vocabulary: not_found | not_started | expired | usage_limit | inactive |\n * rules_not_matched | unsupported. */\n reason?: string\n}\n\n/**\n * The client-side coupon preview, engine-backed: find the promotion carrying\n * the code, gate it through the SAME eligibility path the evaluator runs, and\n * report the percent when (and only when) it is an order-wide percentage —\n * anything richer is previewed as compute-actions and always revalidated\n * server-side at place_order.\n */\nexport function previewCouponPercent(\n promotions: Promotion[],\n code: string,\n cart: RuleCartSnapshot = { items: [] },\n options: EvaluateOptions = {},\n): CouponPreview {\n const normalized = code.trim().toUpperCase()\n if (!normalized) return { valid: false, percent: 0, reason: 'not_found' }\n const promotion = promotions.find((p) => (p.code ?? '').trim().toUpperCase() === normalized)\n if (!promotion) return { valid: false, percent: 0, reason: 'not_found' }\n\n const reason = promotionGateReason(promotion, { ...cart, codes: [normalized] }, options)\n if (reason === 'inactive') return { valid: false, percent: 0, reason: 'not_found' }\n if (reason !== null) return { valid: false, percent: 0, reason }\n\n const method = promotion.applicationMethod\n if (method.type !== 'percentage' || method.targetType !== 'order' || method.buyRulesMinQuantity != null) {\n return { valid: false, percent: 0, reason: 'unsupported' }\n }\n return { valid: true, percent: method.value }\n}\n","import { Registry } from '@fayz-ai/core'\nimport type { RegistryMeta } from '@fayz-ai/core'\nimport { registerRuleAttribute, ruleAttributeRegistry } from './attributes'\nimport type { RuleAttribute, RuleAttributeFieldType } from './attributes'\nimport type { RuleCartSnapshot } from './rule-types'\n\n// ---------------------------------------------------------------------------\n// Context providers — how the vocabulary WIDENS (spine I9, the fix over\n// Medusa).\n//\n// A context provider is ~15 lines of validated code (Medusa's\n// setPromotionContext shape) after which every rule referencing the new\n// attribute is pure data. Two deliberate fixes over the original:\n//\n// 1. The result is VALIDATED against the provider's own declaration — never\n// z.record(z.any()). A provider may only return the attribute keys it\n// declared, each value type-checked against the declared field type.\n// A violation is an I2 hard error naming the declared set, because a\n// context value of the wrong shape makes every rule on it silently\n// non-matching — the exact failure mode I9 exists to end.\n//\n// 2. Registration REGISTERS THE ATTRIBUTES BACK into the vocabulary registry,\n// so the admin form, validateStore and describeStore discover them the\n// moment the provider exists. In Medusa the context can carry attributes\n// the admin whitelist has never heard of; here the two cannot diverge\n// because they are one write.\n//\n// Registry on the uniform meta envelope, same as blocks/loaders (M1).\n// ---------------------------------------------------------------------------\n\n/** What a provider contributes: one context value per declared attribute key.\n * A value may be a scalar of the declared field type or an array of them\n * (e.g. customer tags); null/undefined mean \"no value\" and are dropped. */\nexport type RuleContext = Record<string, unknown>\n\nexport interface RuleContextAttributeInput extends Omit<RuleAttribute, 'path'> {\n /** Optional — context attributes resolve by KEY from the provided context,\n * so the path is display metadata only. Defaults to `context.<key>`. */\n path?: string\n}\n\nexport interface RuleContextProvider {\n /** Attributes this provider contributes — registered into the vocabulary. */\n attributes: readonly RuleContextAttributeInput[]\n /** Compute the context values for one evaluation. Pure of side effects. */\n resolve(cart: RuleCartSnapshot): Promise<RuleContext> | RuleContext\n}\n\nexport const ruleContextRegistry = new Registry<RuleContextProvider>('ruleContext')\n\n/**\n * Register a context provider. Its attributes enter the vocabulary\n * immediately (duplicate keys are the I2 hard error registerRuleAttribute\n * throws), so discovery and evaluation can never disagree about what exists.\n */\nexport function defineRuleContextProvider(\n id: string,\n provider: RuleContextProvider,\n meta: RegistryMeta = {},\n): void {\n if (!id) throw new Error('defineRuleContextProvider: id is required')\n if (!provider.attributes.length) {\n throw new Error(`defineRuleContextProvider: \"${id}\" declares no attributes — a provider that contributes nothing cannot widen the vocabulary`)\n }\n const replacing = ruleContextRegistry.has(id)\n for (const input of provider.attributes) {\n registerRuleAttribute(\n { ...input, path: input.path ?? `context.${input.key}` },\n { meta: { source: 'app', ...meta }, replace: replacing },\n )\n }\n ruleContextRegistry.register(id, provider, {\n description: `Rule context provider — contributes: ${provider.attributes.map((a) => a.key).join(', ')}`,\n source: 'app',\n ...meta,\n })\n}\n\nfunction typeOk(value: unknown, fieldType: RuleAttributeFieldType): boolean {\n if (fieldType === 'number') return typeof value === 'number' && Number.isFinite(value)\n if (fieldType === 'boolean') return typeof value === 'boolean'\n return typeof value === 'string'\n}\n\n/**\n * Validate one provider's resolved context against its declaration (I2):\n * unknown keys name the declared set; wrong-typed values name the expected\n * type. Returns only the declared, typed, non-null entries.\n */\nexport function validateRuleContextResult(id: string, provider: RuleContextProvider, result: RuleContext): RuleContext {\n const declared = new Map(provider.attributes.map((attribute) => [attribute.key, attribute]))\n const validated: RuleContext = {}\n for (const [key, value] of Object.entries(result)) {\n const attribute = declared.get(key)\n if (!attribute) {\n throw new Error(\n `Rule context provider \"${id}\" returned undeclared attribute \"${key}\". ` +\n `Declared attributes: ${[...declared.keys()].sort().join(', ')}`,\n )\n }\n if (value === null || value === undefined) continue\n const values = Array.isArray(value) ? value : [value]\n for (const entry of values) {\n if (!typeOk(entry, attribute.fieldType)) {\n throw new Error(\n `Rule context provider \"${id}\" returned ${JSON.stringify(entry)} for \"${key}\", ` +\n `which is declared as ${attribute.fieldType}`,\n )\n }\n }\n validated[key] = value\n }\n return validated\n}\n\n/**\n * Run every registered provider against the cart and merge the validated\n * results into one context record, keyed by attribute key — the `context`\n * input of computePromotionActions. Key collisions cannot happen here: the\n * vocabulary registry already refused the second registration.\n */\nexport async function buildRuleContext(cart: RuleCartSnapshot): Promise<RuleContext> {\n const context: RuleContext = {}\n for (const entry of ruleContextRegistry.list()) {\n const result = await entry.value.resolve(cart)\n Object.assign(context, validateRuleContextResult(entry.id, entry.value, result))\n }\n return context\n}\n\n/** Test seam. Clearing providers does not un-register their vocabulary rows —\n * pair with clearRuleAttributes() in suite teardown. */\nexport function clearRuleContextProviders(): void {\n ruleContextRegistry.clear()\n}\n"]}
@@ -0,0 +1,93 @@
1
+ import { getShopTenantId } from './chunk-KLDS7IPU.js';
2
+ import { EntityValidationError, slugifyEntityTitle } from './chunk-NWW3WJ7Y.js';
3
+ import { validateEntityRecordData, getSupabaseClientOptional, resolveEntityDeclaration } from '@fayz-ai/core';
4
+
5
+ function getDb() {
6
+ const supabase = getSupabaseClientOptional();
7
+ if (!supabase) throw new Error("@fayz-ai/shop: Supabase client not initialized. Call setGlobalSupabaseClient() first.");
8
+ return supabase;
9
+ }
10
+ function rowToRecord(row) {
11
+ return {
12
+ id: row.id,
13
+ tenantId: row.tenant_id,
14
+ entity: row.entity,
15
+ slug: row.slug ?? null,
16
+ data: row.data ?? {},
17
+ createdAt: row.created_at,
18
+ updatedAt: row.updated_at
19
+ };
20
+ }
21
+ function createSupabaseEntityStore(entity) {
22
+ const declaration = () => resolveEntityDeclaration(entity);
23
+ const gate = (data) => {
24
+ const issues = validateEntityRecordData(declaration(), data);
25
+ if (issues.length > 0) throw new EntityValidationError(entity, issues);
26
+ };
27
+ const deriveSlug = (input) => {
28
+ if (input.slug !== void 0) return input.slug;
29
+ const titleField = declaration().titleField;
30
+ const title = titleField ? input.data[titleField] : void 0;
31
+ return typeof title === "string" && title ? slugifyEntityTitle(title) : null;
32
+ };
33
+ const table = () => declaration().storage.table;
34
+ return {
35
+ async list(options = {}) {
36
+ let q = getDb().from(table()).select("*").eq("entity", entity);
37
+ for (const [field, value] of Object.entries(options.filter ?? {})) {
38
+ q = q.eq(`data->>${field}`, value);
39
+ }
40
+ if (options.orderBy) q = q.order(`data->>${options.orderBy}`, { ascending: options.order !== "desc" });
41
+ else q = q.order("created_at", { ascending: true });
42
+ if (options.limit != null) {
43
+ const offset = options.offset ?? 0;
44
+ q = q.range(offset, offset + options.limit - 1);
45
+ } else if (options.offset) {
46
+ q = q.range(options.offset, Number.MAX_SAFE_INTEGER);
47
+ }
48
+ const { data, error } = await q;
49
+ if (error) throw error;
50
+ return (data ?? []).map(rowToRecord);
51
+ },
52
+ async get(id) {
53
+ const { data, error } = await getDb().from(table()).select("*").eq("entity", entity).eq("id", id).maybeSingle();
54
+ if (error) throw error;
55
+ return data ? rowToRecord(data) : null;
56
+ },
57
+ async getBySlug(slug) {
58
+ const { data, error } = await getDb().from(table()).select("*").eq("entity", entity).eq("slug", slug).maybeSingle();
59
+ if (error) throw error;
60
+ return data ? rowToRecord(data) : null;
61
+ },
62
+ async create(input) {
63
+ gate(input.data);
64
+ const { data, error } = await getDb().from(table()).insert({
65
+ tenant_id: getShopTenantId(),
66
+ entity,
67
+ slug: deriveSlug(input),
68
+ data: input.data
69
+ }).select().single();
70
+ if (error) throw error;
71
+ return rowToRecord(data);
72
+ },
73
+ async update(id, input) {
74
+ const updates = {};
75
+ if (input.data !== void 0) {
76
+ gate(input.data);
77
+ updates.data = input.data;
78
+ }
79
+ if (input.slug !== void 0) updates.slug = input.slug;
80
+ const { data, error } = await getDb().from(table()).update(updates).eq("entity", entity).eq("id", id).select().single();
81
+ if (error) throw error;
82
+ return rowToRecord(data);
83
+ },
84
+ async delete(id) {
85
+ const { error } = await getDb().from(table()).delete().eq("entity", entity).eq("id", id);
86
+ if (error) throw error;
87
+ }
88
+ };
89
+ }
90
+
91
+ export { createSupabaseEntityStore };
92
+ //# sourceMappingURL=chunk-6BO2TMZ5.js.map
93
+ //# sourceMappingURL=chunk-6BO2TMZ5.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/entities/supabase-entity-store.ts"],"names":[],"mappings":"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","file":"chunk-6BO2TMZ5.js","sourcesContent":["import { getSupabaseClientOptional, resolveEntityDeclaration, validateEntityRecordData } from '@fayz-ai/core'\nimport { getShopTenantId } from '../data/tenant'\nimport { EntityValidationError, slugifyEntityTitle } from './entity-store'\nimport type { EntityRecord, EntityRecordInput, EntityStore, ListEntityRecordsOptions } from './entity-store'\n\n// ---------------------------------------------------------------------------\n// The generic Supabase EntityStore — one implementation for every declared\n// entity, over the shared jsonb records table (migration 0030). Row scoping is\n// enforced by the RLS generated from the declaration (entity-sql.ts, I6); the\n// tenant_id written here is the same one every plg_shop_* write stamps.\n//\n// The write gate runs HERE too, not only in the mock: RLS decides who may\n// write, the declared schema decides WHAT may be written, and the two are\n// independent layers on purpose.\n// ---------------------------------------------------------------------------\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nfunction getDb(): any {\n const supabase = getSupabaseClientOptional()\n if (!supabase) throw new Error('@fayz-ai/shop: Supabase client not initialized. Call setGlobalSupabaseClient() first.')\n return supabase\n}\n\nfunction rowToRecord(row: any): EntityRecord {\n return {\n id: row.id,\n tenantId: row.tenant_id,\n entity: row.entity,\n slug: row.slug ?? null,\n data: row.data ?? {},\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n }\n}\n\n/**\n * The generic store for one entity over the shared records table. Promotion to\n * typed columns replaces THIS with a dedicated store via registerEntityStore —\n * the callers' contract (this interface) is what stays.\n */\nexport function createSupabaseEntityStore(entity: string): EntityStore {\n const declaration = () => resolveEntityDeclaration(entity)\n\n const gate = (data: Record<string, unknown>): void => {\n const issues = validateEntityRecordData(declaration(), data)\n if (issues.length > 0) throw new EntityValidationError(entity, issues)\n }\n\n const deriveSlug = (input: EntityRecordInput): string | null => {\n if (input.slug !== undefined) return input.slug\n const titleField = declaration().titleField\n const title = titleField ? input.data[titleField] : undefined\n return typeof title === 'string' && title ? slugifyEntityTitle(title) : null\n }\n\n const table = () => declaration().storage.table\n\n return {\n async list(options: ListEntityRecordsOptions = {}) {\n let q = getDb().from(table()).select('*').eq('entity', entity)\n for (const [field, value] of Object.entries(options.filter ?? {})) {\n // Declared data fields live under the jsonb column; ->> compares text,\n // which matches the declared string/ref field types filters target.\n q = q.eq(`data->>${field}`, value)\n }\n if (options.orderBy) q = q.order(`data->>${options.orderBy}`, { ascending: options.order !== 'desc' })\n else q = q.order('created_at', { ascending: true })\n if (options.limit != null) {\n const offset = options.offset ?? 0\n q = q.range(offset, offset + options.limit - 1)\n } else if (options.offset) {\n q = q.range(options.offset, Number.MAX_SAFE_INTEGER)\n }\n const { data, error } = await q\n if (error) throw error\n return (data ?? []).map(rowToRecord)\n },\n\n async get(id) {\n const { data, error } = await getDb().from(table()).select('*').eq('entity', entity).eq('id', id).maybeSingle()\n if (error) throw error\n return data ? rowToRecord(data) : null\n },\n\n async getBySlug(slug) {\n const { data, error } = await getDb().from(table()).select('*').eq('entity', entity).eq('slug', slug).maybeSingle()\n if (error) throw error\n return data ? rowToRecord(data) : null\n },\n\n async create(input) {\n gate(input.data)\n const { data, error } = await getDb()\n .from(table())\n .insert({\n tenant_id: getShopTenantId(),\n entity,\n slug: deriveSlug(input),\n data: input.data,\n })\n .select()\n .single()\n if (error) throw error\n return rowToRecord(data)\n },\n\n async update(id, input) {\n const updates: Record<string, unknown> = {}\n if (input.data !== undefined) {\n gate(input.data)\n updates.data = input.data\n }\n if (input.slug !== undefined) updates.slug = input.slug\n const { data, error } = await getDb()\n .from(table())\n .update(updates)\n .eq('entity', entity)\n .eq('id', id)\n .select()\n .single()\n if (error) throw error\n return rowToRecord(data)\n },\n\n async delete(id) {\n const { error } = await getDb().from(table()).delete().eq('entity', entity).eq('id', id)\n if (error) throw error\n },\n }\n}\n"]}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=chunk-FQPGE3YN.js.map
3
+ //# sourceMappingURL=chunk-FQPGE3YN.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-JR25TISO.js"}
1
+ {"version":3,"sources":[],"names":[],"mappings":"","file":"chunk-FQPGE3YN.js"}
@@ -1,4 +1,4 @@
1
- // src/mock-catalog.ts
1
+ // src/data/mock-catalog.ts
2
2
  var T0 = "2026-01-01T12:00:00.000Z";
3
3
  function svgPlaceholder(label, hueA, hueB) {
4
4
  const initials = label.split(/\s+/).map((w) => w[0]).slice(0, 2).join("").toUpperCase();
@@ -94,15 +94,37 @@ var MOCK_DISCOUNTS = [
94
94
  updatedAt: T0
95
95
  }
96
96
  ];
97
+ var MOCK_VARIANTS = {
98
+ "p-01": {
99
+ options: [{ id: "opt-p01-size", name: "Tamanho", values: ["P", "M", "G"], position: 0 }],
100
+ variants: [
101
+ { id: "var-p01-p", productId: "p-01", options: { Tamanho: "P" }, sku: "TSH-001-P", price: null, compareAtPrice: null, inventoryCount: 20, position: 0, status: "active", imageId: null },
102
+ { id: "var-p01-m", productId: "p-01", options: { Tamanho: "M" }, sku: "TSH-001-M", price: null, compareAtPrice: null, inventoryCount: 30, position: 1, status: "active", imageId: null },
103
+ { id: "var-p01-g", productId: "p-01", options: { Tamanho: "G" }, sku: "TSH-001-G", price: null, compareAtPrice: null, inventoryCount: 0, position: 2, status: "active", imageId: null }
104
+ ]
105
+ },
106
+ "p-02": {
107
+ options: [
108
+ { id: "opt-p02-size", name: "Tamanho", values: ["M", "G"], position: 0 },
109
+ { id: "opt-p02-color", name: "Cor", values: ["Cru", "Azul"], position: 1 }
110
+ ],
111
+ variants: [
112
+ { id: "var-p02-m-cru", productId: "p-02", options: { Tamanho: "M", Cor: "Cru" }, sku: "LNS-002-MC", price: null, compareAtPrice: null, inventoryCount: 8, position: 0, status: "active", imageId: null },
113
+ { id: "var-p02-m-azul", productId: "p-02", options: { Tamanho: "M", Cor: "Azul" }, sku: "LNS-002-MA", price: 199.9, compareAtPrice: null, inventoryCount: 6, position: 1, status: "active", imageId: null },
114
+ { id: "var-p02-g-cru", productId: "p-02", options: { Tamanho: "G", Cor: "Cru" }, sku: "LNS-002-GC", price: null, compareAtPrice: null, inventoryCount: 6, position: 2, status: "active", imageId: null },
115
+ { id: "var-p02-g-azul", productId: "p-02", options: { Tamanho: "G", Cor: "Azul" }, sku: "LNS-002-GA", price: 199.9, compareAtPrice: null, inventoryCount: 4, position: 3, status: "active", imageId: null }
116
+ ]
117
+ }
118
+ };
97
119
  function slugifyName(name) {
98
120
  return name.toLowerCase().normalize("NFD").replace(/[̀-ͯ]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
99
121
  }
100
122
  function buildMockCatalog(input) {
101
123
  const categories = input.categories.map((c, i) => ({
102
- id: `cat-${slugifyName(c.name)}`,
124
+ id: `cat-${c.slug ?? slugifyName(c.name)}`,
103
125
  tenantId: "mock",
104
126
  name: c.name,
105
- slug: slugifyName(c.name),
127
+ slug: c.slug ?? slugifyName(c.name),
106
128
  description: c.description ?? null,
107
129
  parentId: null,
108
130
  sortOrder: i,
@@ -119,7 +141,11 @@ function buildMockCatalog(input) {
119
141
  id,
120
142
  tenantId: "mock",
121
143
  name: p.name,
122
- slug: slugifyName(p.name),
144
+ // An explicit slug wins over the name-derived one. This matters for a
145
+ // replatform: the store's existing product URLs, inbound links and ad
146
+ // destinations are all keyed on ITS slug, and deriving a new one from
147
+ // the name silently 404s every one of them.
148
+ slug: p.slug ?? (typeof p.metadata?.slug === "string" && p.metadata.slug ? p.metadata.slug : slugifyName(p.name)),
123
149
  description: p.description,
124
150
  price: p.price,
125
151
  compareAtPrice: p.compareAtPrice ?? null,
@@ -129,17 +155,17 @@ function buildMockCatalog(input) {
129
155
  sku: p.sku,
130
156
  sortOrder: i,
131
157
  metadata: p.metadata ?? {},
132
- images: [
133
- {
134
- id: `img-${id}-0`,
158
+ images: (p.images?.length ? p.images : [p.image ?? svgPlaceholder(p.name, hues[0], hues[1])]).map(
159
+ (url, n) => ({
160
+ id: `img-${id}-${n}`,
135
161
  productId: id,
136
- url: p.image ?? svgPlaceholder(p.name, hues[0], hues[1]),
162
+ url,
137
163
  altText: p.name,
138
- sortOrder: 0,
139
- isPrimary: true,
164
+ sortOrder: n,
165
+ isPrimary: n === 0,
140
166
  createdAt: T0
141
- }
142
- ],
167
+ })
168
+ ),
143
169
  categoryId: cat?.id ?? null,
144
170
  categoryName: cat?.name ?? null,
145
171
  isPhysical: true,
@@ -173,6 +199,6 @@ function buildMockCatalog(input) {
173
199
  return { products, categories, discounts };
174
200
  }
175
201
 
176
- export { MOCK_CATEGORIES, MOCK_DISCOUNTS, MOCK_PRODUCTS, buildMockCatalog, svgPlaceholder };
177
- //# sourceMappingURL=chunk-CTKZ6O2J.js.map
178
- //# sourceMappingURL=chunk-CTKZ6O2J.js.map
202
+ export { MOCK_CATEGORIES, MOCK_DISCOUNTS, MOCK_PRODUCTS, MOCK_VARIANTS, buildMockCatalog, svgPlaceholder };
203
+ //# sourceMappingURL=chunk-IKNGCRUM.js.map
204
+ //# sourceMappingURL=chunk-IKNGCRUM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/data/mock-catalog.ts"],"names":[],"mappings":"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","file":"chunk-IKNGCRUM.js","sourcesContent":["import type { Product, ProductImage, ProductVariantSet, Category, Discount } from '../types'\n\n// ---------------------------------------------------------------------------\n// Deterministic, offline product catalog for the mock provider.\n// Product names sourced from the Fayz client repo references\n// (apps/web ShopSection mockups + original shop seeds), expanded to a\n// 16-product / 4-category Shopify-style catalog.\n// ---------------------------------------------------------------------------\n\nconst T0 = '2026-01-01T12:00:00.000Z'\n\n/** 600x600 gradient SVG with product initials — no network, e2e-stable. */\nexport function svgPlaceholder(label: string, hueA: number, hueB: number): string {\n const initials = label\n .split(/\\s+/)\n .map((w) => w[0])\n .slice(0, 2)\n .join('')\n .toUpperCase()\n const svg =\n `<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"600\" height=\"600\">` +\n `<defs><linearGradient id=\"g\" x1=\"0\" y1=\"0\" x2=\"1\" y2=\"1\">` +\n `<stop offset=\"0\" stop-color=\"hsl(${hueA},45%,82%)\"/>` +\n `<stop offset=\"1\" stop-color=\"hsl(${hueB},50%,62%)\"/>` +\n `</linearGradient></defs>` +\n `<rect width=\"600\" height=\"600\" fill=\"url(#g)\"/>` +\n `<text x=\"300\" y=\"330\" font-family=\"sans-serif\" font-size=\"120\" font-weight=\"700\" ` +\n `fill=\"rgba(255,255,255,0.85)\" text-anchor=\"middle\">${initials}</text>` +\n `</svg>`\n return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`\n}\n\nfunction image(productId: string, name: string, hueA: number, hueB: number, idx = 0): ProductImage {\n return {\n id: `img-${productId}-${idx}`,\n productId,\n url: svgPlaceholder(name, hueA, hueB),\n altText: name,\n sortOrder: idx,\n isPrimary: idx === 0,\n createdAt: T0,\n }\n}\n\nexport const MOCK_CATEGORIES: Category[] = [\n { id: 'cat-apparel', tenantId: 'mock', name: 'Apparel', slug: 'apparel', description: 'Clothing and wearables', parentId: null, sortOrder: 0, createdAt: T0 },\n { id: 'cat-bags', tenantId: 'mock', name: 'Bags & Accessories', slug: 'bags-accessories', description: 'Bags, wallets and carry gear', parentId: null, sortOrder: 1, createdAt: T0 },\n { id: 'cat-home', tenantId: 'mock', name: 'Home & Kitchen', slug: 'home-kitchen', description: 'Everyday home goods', parentId: null, sortOrder: 2, createdAt: T0 },\n { id: 'cat-tech', tenantId: 'mock', name: 'Stationery & Tech', slug: 'stationery-tech', description: 'Desk, audio and paper goods', parentId: null, sortOrder: 3, createdAt: T0 },\n]\n\ninterface SeedProduct {\n id: string\n name: string\n slug: string\n description: string\n price: number\n compareAtPrice?: number\n inventoryCount: number\n sku: string\n categoryId: string\n hues: [number, number]\n weight: number\n}\n\nconst SEEDS: SeedProduct[] = [\n // --- Apparel ---\n { id: 'p-01', name: 'Classic T-Shirt', slug: 'classic-t-shirt', description: 'A timeless crew-neck tee in heavyweight combed cotton. Pre-shrunk, breathable, and built to outlast trends.', price: 59.9, compareAtPrice: 79.9, inventoryCount: 50, sku: 'TSH-001', categoryId: 'cat-apparel', hues: [210, 240], weight: 0.2 },\n { id: 'p-02', name: 'Linen Shirt', slug: 'linen-shirt', description: 'A relaxed-fit shirt in 100% European linen. Naturally breathable and softer with every wash.', price: 189.9, inventoryCount: 24, sku: 'LNS-002', categoryId: 'cat-apparel', hues: [35, 20], weight: 0.3 },\n { id: 'p-03', name: 'Denim Jacket', slug: 'denim-jacket', description: 'A structured trucker jacket in 12oz raw denim. Ages beautifully with hidden interior pockets.', price: 299.9, inventoryCount: 12, sku: 'DNJ-003', categoryId: 'cat-apparel', hues: [215, 200], weight: 0.8 },\n { id: 'p-04', name: 'Wool Beanie', slug: 'wool-beanie', description: 'A double-layer merino wool beanie. Warm without itch, with a low-profile cuffed fit.', price: 79.9, inventoryCount: 0, sku: 'WBN-004', categoryId: 'cat-apparel', hues: [0, 350], weight: 0.1 },\n\n // --- Bags & Accessories ---\n { id: 'p-05', name: 'Canvas Tote Bag', slug: 'canvas-tote-bag', description: 'An everyday carryall in 16oz waxed canvas with reinforced straps and an interior zip pocket.', price: 39.9, inventoryCount: 30, sku: 'BAG-001', categoryId: 'cat-bags', hues: [80, 110], weight: 0.3 },\n { id: 'p-06', name: 'Leather Backpack', slug: 'leather-backpack', description: 'Full-grain leather backpack with a padded 15\" laptop sleeve, magnetic closures and brass hardware.', price: 549.9, inventoryCount: 8, sku: 'LBP-006', categoryId: 'cat-bags', hues: [25, 15], weight: 1.4 },\n { id: 'p-07', name: 'Leather Card Wallet', slug: 'leather-card-wallet', description: 'A slim six-card wallet in vegetable-tanned leather. RFID-shielded with a center cash slot.', price: 89.9, compareAtPrice: 119.9, inventoryCount: 45, sku: 'LCW-007', categoryId: 'cat-bags', hues: [30, 45], weight: 0.08 },\n { id: 'p-08', name: 'Travel Duffel', slug: 'travel-duffel', description: 'A 40L weekender in ballistic nylon with a trolley sleeve, shoe compartment and detachable strap.', price: 329.9, inventoryCount: 15, sku: 'TDF-008', categoryId: 'cat-bags', hues: [150, 170], weight: 1.1 },\n\n // --- Home & Kitchen ---\n { id: 'p-09', name: 'Ceramic Coffee Mug', slug: 'ceramic-coffee-mug', description: 'A 350ml stoneware mug with a matte glaze and comfortable wide handle. Dishwasher and microwave safe.', price: 34.9, inventoryCount: 0, sku: 'MUG-001', categoryId: 'cat-home', hues: [190, 210], weight: 0.4 },\n { id: 'p-10', name: 'Scented Soy Candle', slug: 'scented-soy-candle', description: 'Hand-poured soy wax candle with cedar and bergamot notes. 45-hour burn time in a reusable glass jar.', price: 69.9, inventoryCount: 60, sku: 'SSC-010', categoryId: 'cat-home', hues: [45, 30], weight: 0.5 },\n { id: 'p-11', name: 'Bamboo Cutting Board', slug: 'bamboo-cutting-board', description: 'An end-grain bamboo board that is gentle on knives, with a juice groove and inset handles.', price: 119.9, inventoryCount: 22, sku: 'BCB-011', categoryId: 'cat-home', hues: [90, 70], weight: 1.2 },\n { id: 'p-12', name: 'Stoneware Plate Set', slug: 'stoneware-plate-set', description: 'A set of four reactive-glaze dinner plates. Each piece is unique, oven-safe to 220°C.', price: 249.9, inventoryCount: 10, sku: 'SPS-012', categoryId: 'cat-home', hues: [200, 180], weight: 2.4 },\n\n // --- Stationery & Tech ---\n { id: 'p-13', name: 'Wireless Headphones', slug: 'wireless-headphones', description: 'Over-ear headphones with active noise cancelling, 40-hour battery and multipoint Bluetooth 5.3.', price: 899.9, inventoryCount: 18, sku: 'WHP-013', categoryId: 'cat-tech', hues: [260, 280], weight: 0.35 },\n { id: 'p-14', name: 'Notebook Set', slug: 'notebook-set', description: 'Three A5 dot-grid notebooks with 120gsm paper, lay-flat binding and numbered pages.', price: 29.9, inventoryCount: 80, sku: 'NBS-014', categoryId: 'cat-tech', hues: [330, 310], weight: 0.45 },\n { id: 'p-15', name: 'Bluetooth Speaker', slug: 'bluetooth-speaker', description: 'A pocket-size speaker with surprising low end, IPX7 waterproofing and 14-hour playtime.', price: 199.9, compareAtPrice: 259.9, inventoryCount: 26, sku: 'BTS-015', categoryId: 'cat-tech', hues: [170, 190], weight: 0.3 },\n { id: 'p-16', name: 'Felt Desk Mat', slug: 'felt-desk-mat', description: 'A 80x30cm merino felt desk mat with a cork base. Protects the desk and quiets the keyboard.', price: 99.9, inventoryCount: 34, sku: 'FDM-016', categoryId: 'cat-tech', hues: [220, 230], weight: 0.6 },\n]\n\nconst CATEGORY_NAME: Record<string, string> = Object.fromEntries(\n MOCK_CATEGORIES.map((c) => [c.id, c.name]),\n)\n\nexport const MOCK_PRODUCTS: Product[] = SEEDS.map((s, i) => ({\n id: s.id,\n tenantId: 'mock',\n name: s.name,\n slug: s.slug,\n description: s.description,\n price: s.price,\n compareAtPrice: s.compareAtPrice ?? null,\n currency: 'BRL',\n status: 'active',\n inventoryCount: s.inventoryCount,\n sku: s.sku,\n sortOrder: i,\n metadata: {},\n images: [image(s.id, s.name, s.hues[0], s.hues[1])],\n categoryId: s.categoryId,\n categoryName: CATEGORY_NAME[s.categoryId] ?? null,\n isPhysical: true,\n weight: s.weight,\n weightUnit: 'kg',\n // staggered timestamps → deterministic 'newest' sort (p-16 newest)\n createdAt: `2026-01-${String(i + 1).padStart(2, '0')}T12:00:00.000Z`,\n updatedAt: `2026-01-${String(i + 1).padStart(2, '0')}T12:00:00.000Z`,\n}))\n\nexport const MOCK_DISCOUNTS: Discount[] = [\n {\n id: 'disc-welcome',\n tenantId: 'mock',\n title: 'Welcome 10%',\n code: 'BEMVINDO10',\n type: 'percentage',\n method: 'code',\n value: 10,\n usageLimit: null,\n oncePerCustomer: false,\n minSubtotal: null,\n appliesTo: 'all',\n productIds: [],\n categoryIds: [],\n startsAt: T0,\n endsAt: null,\n status: 'active',\n timesUsed: 0,\n createdAt: T0,\n updatedAt: T0,\n },\n]\n\n// ---------------------------------------------------------------------------\n// Variant fixtures (spine M5, mirroring 0028's tables). Two shapes on\n// purpose, because they exercise the two semantics that matter:\n//\n// p-01 Classic T-Shirt — one axis, per-size STOCK (uniform price: every\n// variant inherits via price null). The sum of variant stock (50)\n// equals the product's inventoryCount, like the 0028 sync trigger\n// keeps it on live data — with size G at 0 to prove one sold-out size\n// does not lie about the others.\n// p-02 Linen Shirt — two axes with a per-variant PRICE override, because\n// \"every size must cost the same\" is exactly the constraint variants\n// exist to remove.\n// ---------------------------------------------------------------------------\n\nexport const MOCK_VARIANTS: Record<string, ProductVariantSet> = {\n 'p-01': {\n options: [{ id: 'opt-p01-size', name: 'Tamanho', values: ['P', 'M', 'G'], position: 0 }],\n variants: [\n { id: 'var-p01-p', productId: 'p-01', options: { Tamanho: 'P' }, sku: 'TSH-001-P', price: null, compareAtPrice: null, inventoryCount: 20, position: 0, status: 'active', imageId: null },\n { id: 'var-p01-m', productId: 'p-01', options: { Tamanho: 'M' }, sku: 'TSH-001-M', price: null, compareAtPrice: null, inventoryCount: 30, position: 1, status: 'active', imageId: null },\n { id: 'var-p01-g', productId: 'p-01', options: { Tamanho: 'G' }, sku: 'TSH-001-G', price: null, compareAtPrice: null, inventoryCount: 0, position: 2, status: 'active', imageId: null },\n ],\n },\n 'p-02': {\n options: [\n { id: 'opt-p02-size', name: 'Tamanho', values: ['M', 'G'], position: 0 },\n { id: 'opt-p02-color', name: 'Cor', values: ['Cru', 'Azul'], position: 1 },\n ],\n variants: [\n { id: 'var-p02-m-cru', productId: 'p-02', options: { Tamanho: 'M', Cor: 'Cru' }, sku: 'LNS-002-MC', price: null, compareAtPrice: null, inventoryCount: 8, position: 0, status: 'active', imageId: null },\n { id: 'var-p02-m-azul', productId: 'p-02', options: { Tamanho: 'M', Cor: 'Azul' }, sku: 'LNS-002-MA', price: 199.9, compareAtPrice: null, inventoryCount: 6, position: 1, status: 'active', imageId: null },\n { id: 'var-p02-g-cru', productId: 'p-02', options: { Tamanho: 'G', Cor: 'Cru' }, sku: 'LNS-002-GC', price: null, compareAtPrice: null, inventoryCount: 6, position: 2, status: 'active', imageId: null },\n { id: 'var-p02-g-azul', productId: 'p-02', options: { Tamanho: 'G', Cor: 'Azul' }, sku: 'LNS-002-GA', price: 199.9, compareAtPrice: null, inventoryCount: 4, position: 3, status: 'active', imageId: null },\n ],\n },\n}\n\n// ---------------------------------------------------------------------------\n// buildMockCatalog — terse per-store catalog definition. Generates ids,\n// slugs, SVG imagery and staggered timestamps so every store gets a\n// deterministic, e2e-stable catalog from a few lines of data.\n// ---------------------------------------------------------------------------\n\nexport interface CatalogCategoryInput {\n name: string\n /** URL slug. Defaults to a slugified name — see CatalogProductInput.slug. */\n slug?: string\n description?: string\n image?: string\n}\n\nexport interface CatalogProductInput {\n name: string\n /**\n * URL slug. Defaults to a slugified name, but a store with live URLs must pin\n * it: the slug IS the product's address, so deriving it from the name means\n * renaming a product silently breaks every link to it. `metadata.slug` is\n * honoured too, for stores written that way before this field existed.\n */\n slug?: string\n description: string\n price: number\n compareAtPrice?: number\n inventory: number\n sku: string\n /** Category name — must match one of the categories */\n category: string\n hues?: [number, number]\n weight?: number\n image?: string\n /**\n * Full gallery, primary first. `image` stays as the single-shot shorthand;\n * when both are set `images` wins, so a store can move to a real gallery\n * without rewriting its catalog module.\n *\n * Without this a mock catalog could only ever describe one photo per\n * product, which made the gallery's thumbnail strip and arrows unreachable\n * for every store that had not migrated to a live backend yet.\n */\n images?: readonly string[]\n metadata?: Record<string, unknown>\n}\n\nexport interface CatalogInput {\n categories: CatalogCategoryInput[]\n products: CatalogProductInput[]\n /** Percentage discount codes, e.g. [{ code: 'BEMVINDO10', percent: 10 }] */\n discounts?: Array<{ code: string; percent: number; title?: string }>\n currency?: string\n}\n\nfunction slugifyName(name: string): string {\n return name\n .toLowerCase()\n .normalize('NFD')\n .replace(/[̀-ͯ]/g, '')\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '')\n}\n\nexport function buildMockCatalog(input: CatalogInput): { products: Product[]; categories: Category[]; discounts: Discount[] } {\n const categories: Category[] = input.categories.map((c, i) => ({\n id: `cat-${c.slug ?? slugifyName(c.name)}`,\n tenantId: 'mock',\n name: c.name,\n slug: c.slug ?? slugifyName(c.name),\n description: c.description ?? null,\n parentId: null,\n sortOrder: i,\n imageUrl: c.image ?? null,\n createdAt: T0,\n }))\n const byName = Object.fromEntries(categories.map((c) => [c.name, c]))\n\n const products: Product[] = input.products.map((p, i) => {\n const cat = byName[p.category]\n const id = `p-${String(i + 1).padStart(2, '0')}`\n const hues = p.hues ?? [(i * 47) % 360, (i * 47 + 50) % 360]\n const stamp = `2026-01-${String((i % 27) + 1).padStart(2, '0')}T12:00:00.000Z`\n return {\n id,\n tenantId: 'mock',\n name: p.name,\n // An explicit slug wins over the name-derived one. This matters for a\n // replatform: the store's existing product URLs, inbound links and ad\n // destinations are all keyed on ITS slug, and deriving a new one from\n // the name silently 404s every one of them.\n slug:\n p.slug ??\n (typeof p.metadata?.slug === 'string' && p.metadata.slug ? p.metadata.slug : slugifyName(p.name)),\n description: p.description,\n price: p.price,\n compareAtPrice: p.compareAtPrice ?? null,\n currency: input.currency ?? 'BRL',\n status: 'active',\n inventoryCount: p.inventory,\n sku: p.sku,\n sortOrder: i,\n metadata: p.metadata ?? {},\n images: (p.images?.length ? p.images : [p.image ?? svgPlaceholder(p.name, hues[0], hues[1])]).map(\n (url, n) => ({\n id: `img-${id}-${n}`,\n productId: id,\n url,\n altText: p.name,\n sortOrder: n,\n isPrimary: n === 0,\n createdAt: T0,\n }),\n ),\n categoryId: cat?.id ?? null,\n categoryName: cat?.name ?? null,\n isPhysical: true,\n weight: p.weight ?? 0.5,\n weightUnit: 'kg',\n createdAt: stamp,\n updatedAt: stamp,\n }\n })\n\n const discounts: Discount[] = (input.discounts ?? []).map((d, i) => ({\n id: `disc-${slugifyName(d.code)}`,\n tenantId: 'mock',\n title: d.title ?? `${d.percent}% off`,\n code: d.code,\n type: 'percentage',\n method: 'code',\n value: d.percent,\n usageLimit: null,\n oncePerCustomer: false,\n minSubtotal: null,\n appliesTo: 'all',\n productIds: [],\n categoryIds: [],\n startsAt: T0,\n endsAt: null,\n status: 'active',\n timesUsed: 0,\n createdAt: T0,\n updatedAt: T0,\n }))\n\n return { products, categories, discounts }\n}\n"]}
@@ -0,0 +1,17 @@
1
+ // src/data/tenant.ts
2
+ var _resolver = null;
3
+ function setShopTenantResolver(resolver) {
4
+ _resolver = resolver;
5
+ }
6
+ function getShopTenantId() {
7
+ if (!_resolver) return void 0;
8
+ try {
9
+ return _resolver();
10
+ } catch {
11
+ return void 0;
12
+ }
13
+ }
14
+
15
+ export { getShopTenantId, setShopTenantResolver };
16
+ //# sourceMappingURL=chunk-KLDS7IPU.js.map
17
+ //# sourceMappingURL=chunk-KLDS7IPU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/data/tenant.ts"],"names":[],"mappings":";AAOA,IAAI,SAAA,GAAmC,IAAA;AAEhC,SAAS,sBAAsB,QAAA,EAAgC;AACpE,EAAA,SAAA,GAAY,QAAA;AACd;AAEO,SAAS,eAAA,GAAsC;AACpD,EAAA,IAAI,CAAC,WAAW,OAAO,MAAA;AACvB,EAAA,IAAI;AACF,IAAA,OAAO,SAAA,EAAU;AAAA,EACnB,CAAA,CAAA,MAAQ;AACN,IAAA,OAAO,MAAA;AAAA,EACT;AACF","file":"chunk-KLDS7IPU.js","sourcesContent":["// Tenant scoping for shop queries. @fayz-ai/shop has no dependency on @fayz-ai/admin —\n// the host app (or @fayz-ai/plugin-shop) registers a resolver that returns the\n// current tenant id. When no resolver is set, queries are unscoped (RLS still\n// applies server-side).\n\nexport type TenantResolver = () => string | undefined\n\nlet _resolver: TenantResolver | null = null\n\nexport function setShopTenantResolver(resolver: TenantResolver): void {\n _resolver = resolver\n}\n\nexport function getShopTenantId(): string | undefined {\n if (!_resolver) return undefined\n try {\n return _resolver()\n } catch {\n return undefined\n }\n}\n"]}
@@ -0,0 +1,136 @@
1
+ import { validateEntityRecordData, resolveEntityDeclaration } from '@fayz-ai/core';
2
+
3
+ // src/entities/entity-store.ts
4
+ var EntityValidationError = class extends Error {
5
+ constructor(entity, issues) {
6
+ super(
7
+ `Invalid ${entity} record:
8
+ ${issues.map((i) => ` - ${i.path || "(record)"}: ${i.message}${i.valid ? ` (valid: ${i.valid.join(", ")})` : ""}`).join("\n")}`
9
+ );
10
+ this.entity = entity;
11
+ this.issues = issues;
12
+ this.name = "EntityValidationError";
13
+ }
14
+ };
15
+ var storeOverrides = /* @__PURE__ */ new Map();
16
+ function registerEntityStore(entity, store) {
17
+ storeOverrides.set(entity, store);
18
+ }
19
+ function getRegisteredEntityStore(entity) {
20
+ return storeOverrides.get(entity);
21
+ }
22
+ function resolveEntityStore(entity, generic) {
23
+ return storeOverrides.get(entity) ?? generic(entity);
24
+ }
25
+ function clearEntityStores() {
26
+ storeOverrides.clear();
27
+ }
28
+ function slugifyEntityTitle(value) {
29
+ return value.toLowerCase().normalize("NFD").replace(/[\u0300-\u036f]/g, "").replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
30
+ }
31
+ function uid() {
32
+ return Math.random().toString(36).slice(2);
33
+ }
34
+ function createMockEntityStore(entity, options = {}) {
35
+ const tenantId = options.tenantId ?? "mock";
36
+ const records = [];
37
+ const declaration = () => resolveEntityDeclaration(entity);
38
+ const gate = (data) => {
39
+ const issues = validateEntityRecordData(declaration(), data);
40
+ if (issues.length > 0) throw new EntityValidationError(entity, issues);
41
+ };
42
+ const deriveSlug = (input) => {
43
+ if (input.slug !== void 0) return input.slug;
44
+ const titleField = declaration().titleField;
45
+ const title = titleField ? input.data[titleField] : void 0;
46
+ return typeof title === "string" && title ? slugifyEntityTitle(title) : null;
47
+ };
48
+ const uniqueSlug = (slug, exceptId) => {
49
+ if (slug === null) return;
50
+ if (records.some((r) => r.slug === slug && r.id !== exceptId)) {
51
+ throw new Error(`Entity "${entity}" already has a record with slug "${slug}".`);
52
+ }
53
+ };
54
+ const store = {
55
+ async list(listOptions = {}) {
56
+ let out = [...records];
57
+ for (const [field, value] of Object.entries(listOptions.filter ?? {})) {
58
+ out = out.filter((r) => r.data[field] === value);
59
+ }
60
+ if (listOptions.orderBy) {
61
+ const key = listOptions.orderBy;
62
+ const dir = listOptions.order === "desc" ? -1 : 1;
63
+ out.sort((a, b) => {
64
+ const av = a.data[key];
65
+ const bv = b.data[key];
66
+ if (av === bv) return 0;
67
+ if (av === void 0 || av === null) return dir;
68
+ if (bv === void 0 || bv === null) return -dir;
69
+ return (av < bv ? -1 : 1) * dir;
70
+ });
71
+ }
72
+ const offset = listOptions.offset ?? 0;
73
+ return out.slice(offset, listOptions.limit != null ? offset + listOptions.limit : void 0);
74
+ },
75
+ async get(id) {
76
+ return records.find((r) => r.id === id) ?? null;
77
+ },
78
+ async getBySlug(slug) {
79
+ return records.find((r) => r.slug === slug) ?? null;
80
+ },
81
+ async create(input) {
82
+ gate(input.data);
83
+ const slug = deriveSlug(input);
84
+ uniqueSlug(slug);
85
+ const now = (/* @__PURE__ */ new Date()).toISOString();
86
+ const record = {
87
+ id: uid(),
88
+ tenantId,
89
+ entity,
90
+ slug,
91
+ data: { ...input.data },
92
+ createdAt: now,
93
+ updatedAt: now
94
+ };
95
+ records.push(record);
96
+ return record;
97
+ },
98
+ async update(id, input) {
99
+ const record = records.find((r) => r.id === id);
100
+ if (!record) throw new Error(`Entity "${entity}" has no record "${id}".`);
101
+ const data = input.data !== void 0 ? { ...input.data } : record.data;
102
+ gate(data);
103
+ const slug = input.slug !== void 0 ? input.slug : record.slug;
104
+ uniqueSlug(slug, id);
105
+ record.data = data;
106
+ record.slug = slug;
107
+ record.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
108
+ return record;
109
+ },
110
+ async delete(id) {
111
+ const index = records.findIndex((r) => r.id === id);
112
+ if (index >= 0) records.splice(index, 1);
113
+ }
114
+ };
115
+ for (const seed of options.seed ?? []) {
116
+ const issues = validateEntityRecordData(declaration(), seed.data);
117
+ if (issues.length > 0) throw new EntityValidationError(entity, issues);
118
+ const slug = deriveSlug({ slug: seed.slug, data: seed.data });
119
+ uniqueSlug(slug);
120
+ const now = (/* @__PURE__ */ new Date()).toISOString();
121
+ records.push({
122
+ id: seed.id ?? uid(),
123
+ tenantId,
124
+ entity,
125
+ slug,
126
+ data: { ...seed.data },
127
+ createdAt: now,
128
+ updatedAt: now
129
+ });
130
+ }
131
+ return store;
132
+ }
133
+
134
+ export { EntityValidationError, clearEntityStores, createMockEntityStore, getRegisteredEntityStore, registerEntityStore, resolveEntityStore, slugifyEntityTitle };
135
+ //# sourceMappingURL=chunk-NWW3WJ7Y.js.map
136
+ //# sourceMappingURL=chunk-NWW3WJ7Y.js.map