@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,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/lib/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AAMpD,wBAAgB,eAAe,IAAI,YAAY,CAI9C;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,YAAY,GAAG,IAAI,CAEhE;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAGxC"}
@@ -0,0 +1,120 @@
1
+ import type { ShopProvider } from '../data/provider';
2
+ import type { ProductOption, ProductVariant } from '../types';
3
+ /** Shopify's ceiling, carried so a future connector never has to reject a product it can't map. */
4
+ export declare const MAX_VARIANT_AXES = 3;
5
+ export declare const MAX_VARIANT_COMBINATIONS = 100;
6
+ /** An axis as the editor holds it: no id yet, values not yet cleaned. */
7
+ export interface VariantAxisInput {
8
+ name: string;
9
+ values: string[];
10
+ }
11
+ export type VariantCombination = Record<string, string>;
12
+ /**
13
+ * The canonical string for a combination. Sorted by key because 0028 stores
14
+ * `options` as jsonb, which normalises key order on write — so
15
+ * {"Cor":"Azul","Tamanho":"P"} and {"Tamanho":"P","Cor":"Azul"} are ONE row to
16
+ * the UNIQUE index, and anything comparing them in TypeScript has to agree.
17
+ */
18
+ export declare function variantOptionsKey(options: VariantCombination): string;
19
+ /**
20
+ * Trim, drop the blanks, dedupe the values. An axis with no name or no value is
21
+ * removed outright: it cannot contribute a combination, and keeping it would
22
+ * multiply the matrix by zero.
23
+ */
24
+ export declare function normalizeVariantAxes(axes: readonly VariantAxisInput[]): VariantAxisInput[];
25
+ /**
26
+ * The cartesian product, LAST axis varying fastest — P/Cru, P/Azul, M/Cru… That
27
+ * is the order Shopify lists variants in and the order a merchant reads a size
28
+ * chart in; deriving it any other way makes the grid look shuffled.
29
+ */
30
+ export declare function variantCombinations(axes: readonly VariantAxisInput[]): VariantCombination[];
31
+ /** Why the axes cannot be saved, or null. Both limits are Shopify's. */
32
+ export declare function validateVariantAxes(axes: readonly VariantAxisInput[]): string | null;
33
+ /** One cell of the matrix: a combination and the row that carries it, if any. */
34
+ export interface VariantMatrixRow {
35
+ options: VariantCombination;
36
+ /** Grid order, written back to `position` so the DB reads in the same order. */
37
+ position: number;
38
+ variant: ProductVariant | null;
39
+ }
40
+ export interface VariantMatrixPlan {
41
+ /** The whole matrix, in grid order. */
42
+ rows: VariantMatrixRow[];
43
+ /** Combinations with no row at all — INSERT. */
44
+ create: VariantMatrixRow[];
45
+ /** Combinations whose row exists but is archived — UPDATE back to active,
46
+ * because 0028's UNIQUE (product_id, options) refuses a second row and a
47
+ * DELETE would take the archived row's stock balances with it (0034 CASCADEs). */
48
+ reactivate: VariantMatrixRow[];
49
+ /** Active rows the axes still describe. Nothing to do but reposition. */
50
+ keep: VariantMatrixRow[];
51
+ /** Active rows the axes no longer describe — ARCHIVE, never delete, for the
52
+ * same reason 0037 §4 archives the default variant. */
53
+ archive: ProductVariant[];
54
+ /**
55
+ * The default variant (0037), if the product has one. It is NEVER in any of
56
+ * the lists above: it carries no options, so the matrix can neither describe
57
+ * it nor orphan it. Its lifecycle is reconciled separately — see
58
+ * applyVariantMatrix.
59
+ */
60
+ defaultVariant: ProductVariant | null;
61
+ }
62
+ /**
63
+ * What has to happen to make the variant rows match the axes.
64
+ *
65
+ * `existing` must include ARCHIVED rows. A merchant who removes "GG" and puts
66
+ * it back an hour later is not creating a new SKU — and INSERTing one would hit
67
+ * 0028's UNIQUE (product_id, options) rather than gracefully doing anything.
68
+ */
69
+ export declare function planVariantMatrix(axes: readonly VariantAxisInput[], existing: readonly ProductVariant[]): VariantMatrixPlan;
70
+ /**
71
+ * Whether the product is option-less as far as the merchant is concerned: one
72
+ * variant, and it is 0037's placeholder. The admin shows the empty state for
73
+ * this, never a one-row grid — 0037 is explicit that the default variant MUST
74
+ * BE INVISIBLE (no picker, no admin column, no options label).
75
+ */
76
+ export declare function hasOnlyDefaultVariant(variants: readonly ProductVariant[]): boolean;
77
+ /**
78
+ * Whether applying the plan would leave the product with NOTHING ACTIVE to
79
+ * sell, and no default variant to fall back on.
80
+ *
81
+ * FOUND BY READING 0039, NOT BY USING THE SCREEN, and the failure is silent:
82
+ * with every real variant archived and 0037's default archived too (§4 archived
83
+ * it the moment the first real variant appeared), shop_place_order's resolution
84
+ * pass finds no default, falls through to the DEPRECATED product-scalar leg,
85
+ * and 0028's sync trigger then recomputes plg_shop_products.inventory_count as
86
+ * the sum of ZERO active variants. The merchant deletes an option and the
87
+ * product goes permanently sold out, with no error anywhere.
88
+ *
89
+ * applyVariantMatrix closes the case where the default row EXISTS (it revives
90
+ * it). This predicate is the residue: a product that predates 0037's backfill
91
+ * has no such row, and no client can mint one — 0047 is what fixes that.
92
+ */
93
+ export declare function leavesNothingSellable(plan: VariantMatrixPlan): boolean;
94
+ /**
95
+ * The axes a variant set implies, position-ordered — what the editor opens
96
+ * with. Taken from `options` rows rather than derived from the variants: 0028
97
+ * makes the ORDER of the values meaningful ("P before M before G is not
98
+ * alphabetical and not arbitrary, so it cannot be derived").
99
+ */
100
+ export declare function axesFromOptions(options: readonly ProductOption[]): VariantAxisInput[];
101
+ /**
102
+ * Save the axes and reconcile the variant rows to them, INCLUDING 0037's
103
+ * default variant. The whole rule lives here rather than in the screen because
104
+ * every step of it is a database invariant, and a second screen that
105
+ * re-implemented four of the five steps would be right until the day it wasn't.
106
+ *
107
+ * Order is load-bearing:
108
+ * 1. axes first, so a variant referring to a value the axes no longer declare
109
+ * is never visible even for one render;
110
+ * 2. INSERT the new combinations — which is also what fires 0037 §4 and
111
+ * archives the default variant, so we never do that by hand on this path;
112
+ * 3. revive the archived combinations (an UPDATE, which fires NO trigger —
113
+ * hence step 5);
114
+ * 4. archive what the axes dropped;
115
+ * 5. reconcile the default variant in whichever direction is now true.
116
+ */
117
+ export declare function applyVariantMatrix(provider: ShopProvider, productId: string, axes: readonly VariantAxisInput[], existing: readonly ProductVariant[]): Promise<VariantMatrixPlan>;
118
+ /** The merchant-facing label for a combination: "Tamanho: P · Cor: Azul". */
119
+ export declare function formatVariantCombination(options: VariantCombination, axes?: readonly VariantAxisInput[]): string;
120
+ //# sourceMappingURL=variant-matrix.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variant-matrix.d.ts","sourceRoot":"","sources":["../../src/lib/variant-matrix.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAc7D,mGAAmG;AACnG,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,wBAAwB,MAAM,CAAA;AAE3C,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,EAAE,CAAA;CACjB;AAED,MAAM,MAAM,kBAAkB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;AAEvD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,CAMrE;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,gBAAgB,EAAE,CAmB1F;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,kBAAkB,EAAE,CAY3F;AAED,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,GAAG,IAAI,CAUpF;AAED,iFAAiF;AACjF,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,kBAAkB,CAAA;IAC3B,gFAAgF;IAChF,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;CAC/B;AAED,MAAM,WAAW,iBAAiB;IAChC,uCAAuC;IACvC,IAAI,EAAE,gBAAgB,EAAE,CAAA;IACxB,gDAAgD;IAChD,MAAM,EAAE,gBAAgB,EAAE,CAAA;IAC1B;;uFAEmF;IACnF,UAAU,EAAE,gBAAgB,EAAE,CAAA;IAC9B,yEAAyE;IACzE,IAAI,EAAE,gBAAgB,EAAE,CAAA;IACxB;4DACwD;IACxD,OAAO,EAAE,cAAc,EAAE,CAAA;IACzB;;;;;OAKG;IACH,cAAc,EAAE,cAAc,GAAG,IAAI,CAAA;CACtC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,iBAAiB,CAgCnB;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAAG,OAAO,CAGlF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAGtE;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,SAAS,aAAa,EAAE,GAAG,gBAAgB,EAAE,CAIrF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,kBAAkB,CACtC,QAAQ,EAAE,YAAY,EACtB,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,gBAAgB,EAAE,EACjC,QAAQ,EAAE,SAAS,cAAc,EAAE,GAClC,OAAO,CAAC,iBAAiB,CAAC,CA0C5B;AAED,6EAA6E;AAC7E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,kBAAkB,EAAE,IAAI,CAAC,EAAE,SAAS,gBAAgB,EAAE,GAAG,MAAM,CAMhH"}
package/dist/mock.d.ts CHANGED
@@ -1,3 +1,2 @@
1
- export { MockShopProvider, createMockShopProvider } from './mock-provider';
2
- export type { MockShopSeed } from './mock-provider';
1
+ export * from './data/mock';
3
2
  //# sourceMappingURL=mock.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC1E,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"AACA,cAAc,aAAa,CAAA"}
package/dist/mock.js CHANGED
@@ -1,4 +1,6 @@
1
- export { MockShopProvider, createMockShopProvider } from './chunk-SVTCJ6HP.js';
2
- import './chunk-CTKZ6O2J.js';
1
+ export { MockShopProvider, createMockShopProvider } from './chunk-U6HV3LVP.js';
2
+ import './chunk-IKNGCRUM.js';
3
+ import './chunk-NWW3WJ7Y.js';
4
+ import './chunk-3GERSY5F.js';
3
5
  //# sourceMappingURL=mock.js.map
4
6
  //# sourceMappingURL=mock.js.map
@@ -1,89 +1,2 @@
1
- import type { Product, ProductImage, Category, Order, ShopCustomer, Discount, ProductEnquiry, CreateProductInput, UpdateProductInput, ListProductsOptions, CreateProductEnquiryInput, ListProductEnquiriesOptions, CreateCategoryInput, UpdateCategoryInput, CreateOrderInput, UpdateOrderInput, ListOrdersOptions, PlaceOrderInput, CreateCustomerInput, UpdateCustomerInput, ListCustomersOptions, ResolveCustomerInput, CustomerAddress, ShippingQuoteOption, ShippingZone, CreateShippingZoneInput, UpdateShippingZoneInput, CreateDiscountInput, UpdateDiscountInput, ListDiscountsOptions, SalesChannel, CreateSalesChannelInput, UpdateSalesChannelInput } from './types';
2
- export interface ShopProvider {
3
- listProducts(options?: ListProductsOptions): Promise<Product[]>;
4
- getProduct(id: string): Promise<Product | null>;
5
- createProduct(input: CreateProductInput): Promise<Product>;
6
- updateProduct(id: string, input: UpdateProductInput): Promise<Product>;
7
- deleteProduct(id: string): Promise<void>;
8
- uploadProductImage(productId: string, file: File): Promise<ProductImage>;
9
- deleteProductImage(imageId: string): Promise<void>;
10
- /**
11
- * Reorder a gallery or promote a photo to primary. Optional so a provider
12
- * that only ever holds one image per product need not implement it.
13
- *
14
- * Setting `isPrimary` is exclusive: the previous primary is demoted in the
15
- * same call, because two primaries make "the product photo" ambiguous
16
- * everywhere it is read (card, cart line, order item, admin table).
17
- */
18
- updateProductImage?(imageId: string, input: {
19
- isPrimary?: boolean;
20
- sortOrder?: number;
21
- altText?: string | null;
22
- }): Promise<ProductImage>;
23
- createProductEnquiry?(input: CreateProductEnquiryInput): Promise<ProductEnquiry>;
24
- listProductEnquiries?(options?: ListProductEnquiriesOptions): Promise<ProductEnquiry[]>;
25
- listCategories(): Promise<Category[]>;
26
- createCategory(input: CreateCategoryInput): Promise<Category>;
27
- updateCategory(id: string, input: UpdateCategoryInput): Promise<Category>;
28
- deleteCategory(id: string): Promise<void>;
29
- listOrders(options?: ListOrdersOptions): Promise<Order[]>;
30
- getOrder(id: string): Promise<Order | null>;
31
- createOrder(input: CreateOrderInput): Promise<Order>;
32
- updateOrder(id: string, input: UpdateOrderInput): Promise<Order>;
33
- /**
34
- * Mock/dev payment seam: pending->paid transition via a whitelisted RPC
35
- * (anon storefronts have no UPDATE grant on the orders table). Real PSPs
36
- * confirm via webhook with service_role instead.
37
- */
38
- confirmPayment?(id: string, reference?: string): Promise<Order | null>;
39
- /**
40
- * Trusted order placement: the server re-reads product prices + stock,
41
- * validates the discount, computes totals, and decrements inventory
42
- * atomically. Storefront checkout MUST use this instead of createOrder so the
43
- * browser cannot tamper with prices. createOrder remains for trusted admin entry.
44
- */
45
- placeOrder(input: PlaceOrderInput): Promise<Order>;
46
- listCustomers(options?: ListCustomersOptions): Promise<ShopCustomer[]>;
47
- getCustomer(id: string): Promise<ShopCustomer | null>;
48
- createCustomer(input: CreateCustomerInput): Promise<ShopCustomer>;
49
- updateCustomer(id: string, input: UpdateCustomerInput): Promise<ShopCustomer>;
50
- deleteCustomer(id: string): Promise<void>;
51
- /**
52
- * Find-or-create the customer for a storefront shopper and (on Supabase) link
53
- * it to auth.uid server-side, so order reads can be scoped to the owner by RLS.
54
- * Optional: providers without it fall back to listCustomers+createCustomer.
55
- */
56
- resolveCustomer?(input: ResolveCustomerInput): Promise<ShopCustomer>;
57
- /**
58
- * The signed-in shopper's saved delivery addresses, so checkout can offer
59
- * them instead of inventing a placeholder. Optional: a provider without it
60
- * (or an anonymous request) yields an empty book and a blank form.
61
- */
62
- listCustomerAddresses?(customerId: string): Promise<CustomerAddress[]>;
63
- /**
64
- * Delivery options for a postal code at a given subtotal. An empty array
65
- * means the store does not deliver there — shown before the buyer commits to
66
- * a cart, which is the whole point of quoting on the product page.
67
- *
68
- * This is for DISPLAY. shop_place_order recomputes the freight from the same
69
- * zones and ignores whatever the client sends, so a tampered quote changes
70
- * what the shopper sees and nothing about what they are charged.
71
- */
72
- quoteShipping?(postalCode: string, subtotal: number): Promise<ShippingQuoteOption[]>;
73
- /** Delivery zones the merchant maintains. Admin-only: the storefront reads
74
- * quotes, never the ranges themselves. */
75
- listShippingZones?(): Promise<ShippingZone[]>;
76
- createShippingZone?(input: CreateShippingZoneInput): Promise<ShippingZone>;
77
- updateShippingZone?(id: string, input: UpdateShippingZoneInput): Promise<ShippingZone>;
78
- deleteShippingZone?(id: string): Promise<void>;
79
- listDiscounts(options?: ListDiscountsOptions): Promise<Discount[]>;
80
- getDiscount(id: string): Promise<Discount | null>;
81
- createDiscount(input: CreateDiscountInput): Promise<Discount>;
82
- updateDiscount(id: string, input: UpdateDiscountInput): Promise<Discount>;
83
- deleteDiscount(id: string): Promise<void>;
84
- listSalesChannels?(): Promise<SalesChannel[]>;
85
- createSalesChannel?(input: CreateSalesChannelInput): Promise<SalesChannel>;
86
- updateSalesChannel?(id: string, input: UpdateSalesChannelInput): Promise<SalesChannel>;
87
- deleteSalesChannel?(id: string): Promise<void>;
88
- }
1
+ export * from './data/provider';
89
2
  //# sourceMappingURL=provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AACA,cAAc,iBAAiB,CAAA"}
@@ -0,0 +1,129 @@
1
+ import type { EffectLedger, EffectOutcome, RequestOutcome } from '@fayz-ai/core';
2
+ import { RequestBlockedError } from '@fayz-ai/core';
3
+ import type { RequestPlaneConnector } from './shipping';
4
+ /** What the buyer picked. Open on purpose — a gateway declares what it can do. */
5
+ export type CheckoutMethod = 'pix' | 'boleto' | 'credit_card' | (string & {});
6
+ /**
7
+ * The payable artifacts, one shape per method.
8
+ *
9
+ * TRANSPARENT VS REDIRECT (commerce-stack.md §8.1, open until now): Pix and
10
+ * boleto are transparent because nothing sensitive passes through us — a QR
11
+ * code and a barcode are public strings. CARD IS REDIRECT: taking a card number
12
+ * on our page moves us into PCI-DSS SAQ A-EP, which is architecture (browser
13
+ * tokenisation, scope, an audit) rather than a week of work, and this milestone
14
+ * is the contract, not the compliance programme.
15
+ *
16
+ * The decision is reversible for exactly the reason it is expressed here as an
17
+ * artifact and not as a checkout flow: transparent card becomes
18
+ * `card.tokenizationKey` alongside `card.redirectUrl`, and the storefront that
19
+ * renders whatever came back does not change.
20
+ */
21
+ export interface CheckoutArtifacts {
22
+ pix?: {
23
+ qrCode: string;
24
+ copyPaste: string;
25
+ expiresAt?: string;
26
+ };
27
+ boleto?: {
28
+ url: string;
29
+ barcode?: string;
30
+ dueDate?: string;
31
+ };
32
+ card?: {
33
+ redirectUrl: string;
34
+ installments?: number;
35
+ };
36
+ }
37
+ export type CheckoutSessionStatus = 'pending' | 'authorized' | 'paid' | 'failed' | 'expired';
38
+ export interface CheckoutSession {
39
+ sessionId: string;
40
+ method: CheckoutMethod;
41
+ status: CheckoutSessionStatus;
42
+ /** Minor units. The recomputed one — never a number that arrived from a client. */
43
+ amountCents: number;
44
+ currency: string;
45
+ artifacts: CheckoutArtifacts;
46
+ /** When the total was recomputed server-side for THIS session. The proof, carried with the value. */
47
+ amountRecomputedAt: string;
48
+ }
49
+ export interface CheckoutStartRequest {
50
+ orderId: string;
51
+ method: CheckoutMethod;
52
+ installments?: number;
53
+ }
54
+ export interface CheckoutStarterOptions {
55
+ connector: RequestPlaneConnector;
56
+ /** Recompute the payable total from the order, server-side. The ONLY source of the amount. */
57
+ recompute(orderId: string): Promise<{
58
+ amountCents: number;
59
+ currency: string;
60
+ }>;
61
+ /** The data-plane call that opens the session at the gateway. */
62
+ open(input: CheckoutStartRequest & {
63
+ amountCents: number;
64
+ currency: string;
65
+ }, signal: AbortSignal): Promise<Omit<CheckoutSession, 'amountRecomputedAt'>>;
66
+ now?(): Date;
67
+ }
68
+ /**
69
+ * Open a payment session under the gateway's declared budget.
70
+ *
71
+ * Returns the full {@link RequestOutcome} rather than the bare session so the
72
+ * caller can report `elapsedMs`; `source` is always `'provider'` here, because
73
+ * a `block` connector never degrades — it throws {@link RequestBlockedError},
74
+ * which carries the reason the buyer is shown.
75
+ */
76
+ export declare function createCheckoutStarter(options: CheckoutStarterOptions): (request: CheckoutStartRequest) => Promise<RequestOutcome<CheckoutSession>>;
77
+ export { RequestBlockedError };
78
+ export interface CapturePaymentRequest {
79
+ /** The plg_connections row acting. The tenant is resolved from it server-side. */
80
+ connectionId: string;
81
+ orderId: string;
82
+ amountCents: number;
83
+ /** A gateway webhook by default; 'manual' when an operator captures from the panel. */
84
+ triggerKind?: 'webhook' | 'manual';
85
+ }
86
+ export interface CapturePaymentOptions {
87
+ ledger: EffectLedger;
88
+ /** The data-plane call that captures at the gateway. Reached at most once per key. */
89
+ capture(request: CapturePaymentRequest, runId: string): Promise<{
90
+ status: CheckoutSessionStatus;
91
+ providerRef?: string;
92
+ }>;
93
+ }
94
+ /**
95
+ * Capture at most once per (order, amount).
96
+ *
97
+ * The key is the BUYER's — `capture:<order>:<cents>` — and not the gateway's
98
+ * transaction id, because that id only exists after the call that must not
99
+ * happen twice. A re-delivered webhook and a double-clicked button both hash to
100
+ * the same string and the second one never reaches the gateway.
101
+ */
102
+ export declare function createPaymentCapturer(options: CapturePaymentOptions): (request: CapturePaymentRequest) => Promise<EffectOutcome<{
103
+ status: CheckoutSessionStatus;
104
+ providerRef?: string;
105
+ }>>;
106
+ /**
107
+ * Buy the shipping label at most once per order.
108
+ *
109
+ * The second effect on the plane, and the reason the ledger lives in
110
+ * `@fayz-ai/core` rather than inside the gateway: a label bought twice is money
111
+ * spent twice, by the same mechanism.
112
+ */
113
+ export declare function createLabelBuyer(options: {
114
+ ledger: EffectLedger;
115
+ buy(request: {
116
+ connectionId: string;
117
+ orderId: string;
118
+ }, runId: string): Promise<{
119
+ trackingCode?: string;
120
+ labelUrl?: string;
121
+ }>;
122
+ }): (request: {
123
+ connectionId: string;
124
+ orderId: string;
125
+ }) => Promise<EffectOutcome<{
126
+ trackingCode?: string;
127
+ labelUrl?: string;
128
+ }>>;
129
+ //# sourceMappingURL=checkout.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkout.d.ts","sourceRoot":"","sources":["../../src/providers/checkout.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { createShippingQuoter } from './shipping';
2
+ export type { ShippingQuoteRequest, QuotedShipping, ShippingQuoterOptions, RequestPlaneConnector, } from './shipping';
3
+ export { createCheckoutStarter, createPaymentCapturer, createLabelBuyer, RequestBlockedError } from './checkout';
4
+ export type { CheckoutMethod, CheckoutArtifacts, CheckoutSession, CheckoutSessionStatus, CheckoutStartRequest, CheckoutStarterOptions, CapturePaymentRequest, CapturePaymentOptions, } from './checkout';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,YAAY,EACV,oBAAoB,EACpB,cAAc,EACd,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAE,qBAAqB,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChH,YAAY,EACV,cAAc,EACd,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAA"}
@@ -0,0 +1,43 @@
1
+ import type { ConnectorDefinition, DegradeReason } from '@fayz-ai/core';
2
+ import type { ShippingQuoteOption } from '../types';
3
+ export type RequestPlaneConnector = Pick<ConnectorDefinition, 'id' | 'planes' | 'latencyBudgetMs' | 'onFailure'>;
4
+ export interface ShippingQuoteRequest {
5
+ /** Eight bare digits. */
6
+ postalCode: string;
7
+ /** Decides free-above thresholds; the ORDER is recomputed by shop_place_order regardless. */
8
+ subtotal: number;
9
+ }
10
+ export interface QuotedShipping {
11
+ options: ShippingQuoteOption[];
12
+ /** Where the prices came from. A fallback price shown as a carrier price is how a store quotes one number and charges another. */
13
+ source: 'provider' | 'fallback';
14
+ degraded?: {
15
+ reason: DegradeReason;
16
+ detail?: string;
17
+ };
18
+ }
19
+ export interface ShippingQuoterOptions {
20
+ connector: RequestPlaneConnector;
21
+ /** The data-plane call. Receives the abort signal the budget fires. */
22
+ quote(request: ShippingQuoteRequest, signal: AbortSignal): Promise<ShippingQuoteOption[]>;
23
+ /**
24
+ * The configured zones. Must be LOCAL — a fallback that itself calls a network
25
+ * is a second budget nobody declared.
26
+ */
27
+ zones(request: ShippingQuoteRequest): Promise<ShippingQuoteOption[]>;
28
+ /** False when there is no live connection: quote from zones without spending the budget. */
29
+ connected?(): boolean;
30
+ }
31
+ /**
32
+ * A quoter that always answers.
33
+ *
34
+ * An EMPTY provider answer is a provider answer, not a failure: a carrier that
35
+ * does not serve a postcode has told us something, and the storefront already
36
+ * knows what to do with zero options (delivery.store.ts falls back to the
37
+ * store-wide rate). Inventing a merge here would be policy nobody asked for.
38
+ *
39
+ * Writes nothing, on either path. A quote is not a run — see
40
+ * packages/db/migrations/029_connections.sql.
41
+ */
42
+ export declare function createShippingQuoter(options: ShippingQuoterOptions): (request: ShippingQuoteRequest) => Promise<QuotedShipping>;
43
+ //# sourceMappingURL=shipping.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shipping.d.ts","sourceRoot":"","sources":["../../src/providers/shipping.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AAEnD,MAAM,MAAM,qBAAqB,GAAG,IAAI,CACtC,mBAAmB,EACnB,IAAI,GAAG,QAAQ,GAAG,iBAAiB,GAAG,WAAW,CAClD,CAAA;AAED,MAAM,WAAW,oBAAoB;IACnC,yBAAyB;IACzB,UAAU,EAAE,MAAM,CAAA;IAClB,6FAA6F;IAC7F,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,mBAAmB,EAAE,CAAA;IAC9B,kIAAkI;IAClI,MAAM,EAAE,UAAU,GAAG,UAAU,CAAA;IAC/B,QAAQ,CAAC,EAAE;QAAE,MAAM,EAAE,aAAa,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CACtD;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,qBAAqB,CAAA;IAChC,uEAAuE;IACvE,KAAK,CAAC,OAAO,EAAE,oBAAoB,EAAE,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;IACzF;;;OAGG;IACH,KAAK,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;IACpE,4FAA4F;IAC5F,SAAS,CAAC,IAAI,OAAO,CAAA;CACtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,qBAAqB,IAC7B,SAAS,oBAAoB,KAAG,OAAO,CAAC,cAAc,CAAC,CAQ5F"}
@@ -0,0 +1,70 @@
1
+ import { runRequest, runEffect, buyerIdempotencyKey } from '@fayz-ai/core';
2
+ export { RequestBlockedError } from '@fayz-ai/core';
3
+
4
+ // src/providers/shipping.ts
5
+ function createShippingQuoter(options) {
6
+ return async function quoteShipping(request) {
7
+ const outcome = await runRequest(options.connector, {
8
+ skip: options.connected ? !options.connected() : false,
9
+ call: (signal) => options.quote(request, signal),
10
+ fallback: () => options.zones(request)
11
+ });
12
+ return { options: outcome.value, source: outcome.source, degraded: outcome.degraded };
13
+ };
14
+ }
15
+ function createCheckoutStarter(options) {
16
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
17
+ return async function startCheckout(request) {
18
+ const { amountCents, currency } = await options.recompute(request.orderId);
19
+ const recomputedAt = now().toISOString();
20
+ return runRequest(options.connector, {
21
+ call: async (signal) => {
22
+ const session = await options.open({ ...request, amountCents, currency }, signal);
23
+ if (session.amountCents !== amountCents || session.currency !== currency) {
24
+ throw new Error(
25
+ `gateway opened a session for ${session.amountCents} ${session.currency}, order is ${amountCents} ${currency}`
26
+ );
27
+ }
28
+ return { ...session, amountRecomputedAt: recomputedAt };
29
+ }
30
+ });
31
+ };
32
+ }
33
+ function createPaymentCapturer(options) {
34
+ return async function capturePayment(request) {
35
+ return runEffect(
36
+ options.ledger,
37
+ {
38
+ connectionId: request.connectionId,
39
+ idempotencyKey: buyerIdempotencyKey({
40
+ effect: "capture",
41
+ subject: request.orderId,
42
+ amountCents: request.amountCents
43
+ }),
44
+ direction: "outbound",
45
+ triggerKind: request.triggerKind ?? "webhook",
46
+ stream: "payment"
47
+ },
48
+ (runId) => options.capture(request, runId)
49
+ );
50
+ };
51
+ }
52
+ function createLabelBuyer(options) {
53
+ return async function buyLabel(request) {
54
+ return runEffect(
55
+ options.ledger,
56
+ {
57
+ connectionId: request.connectionId,
58
+ idempotencyKey: buyerIdempotencyKey({ effect: "label", subject: request.orderId }),
59
+ direction: "outbound",
60
+ triggerKind: "manual",
61
+ stream: "shipment"
62
+ },
63
+ (runId) => options.buy(request, runId)
64
+ );
65
+ };
66
+ }
67
+
68
+ export { createCheckoutStarter, createLabelBuyer, createPaymentCapturer, createShippingQuoter };
69
+ //# sourceMappingURL=providers.js.map
70
+ //# sourceMappingURL=providers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/providers/shipping.ts","../src/providers/checkout.ts"],"names":["runRequest"],"mappings":"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","file":"providers.js","sourcesContent":["// ---------------------------------------------------------------------------\n// Freight on the request plane — the quote the buyer is waiting for\n// ---------------------------------------------------------------------------\n// FAY-1301 is explicit: quoting freight NEVER blocks the purchase. So the\n// carrier connector declares `onFailure: 'degrade'`, and this is what it\n// degrades TO — the shipping zones already in our own database, which is the\n// answer the store gave before any carrier was connected.\n//\n// Nothing here talks to a carrier. `quote` is the data-plane call (an edge\n// function holding the credential); the storefront never learns a provider's\n// URL, let alone its key — commerce-stack.md §4.\nimport { runRequest } from '@fayz-ai/core'\nimport type { ConnectorDefinition, DegradeReason } from '@fayz-ai/core'\nimport type { ShippingQuoteOption } from '../types'\n\nexport type RequestPlaneConnector = Pick<\n ConnectorDefinition,\n 'id' | 'planes' | 'latencyBudgetMs' | 'onFailure'\n>\n\nexport interface ShippingQuoteRequest {\n /** Eight bare digits. */\n postalCode: string\n /** Decides free-above thresholds; the ORDER is recomputed by shop_place_order regardless. */\n subtotal: number\n}\n\nexport interface QuotedShipping {\n options: ShippingQuoteOption[]\n /** Where the prices came from. A fallback price shown as a carrier price is how a store quotes one number and charges another. */\n source: 'provider' | 'fallback'\n degraded?: { reason: DegradeReason; detail?: string }\n}\n\nexport interface ShippingQuoterOptions {\n connector: RequestPlaneConnector\n /** The data-plane call. Receives the abort signal the budget fires. */\n quote(request: ShippingQuoteRequest, signal: AbortSignal): Promise<ShippingQuoteOption[]>\n /**\n * The configured zones. Must be LOCAL — a fallback that itself calls a network\n * is a second budget nobody declared.\n */\n zones(request: ShippingQuoteRequest): Promise<ShippingQuoteOption[]>\n /** False when there is no live connection: quote from zones without spending the budget. */\n connected?(): boolean\n}\n\n/**\n * A quoter that always answers.\n *\n * An EMPTY provider answer is a provider answer, not a failure: a carrier that\n * does not serve a postcode has told us something, and the storefront already\n * knows what to do with zero options (delivery.store.ts falls back to the\n * store-wide rate). Inventing a merge here would be policy nobody asked for.\n *\n * Writes nothing, on either path. A quote is not a run — see\n * packages/db/migrations/029_connections.sql.\n */\nexport function createShippingQuoter(options: ShippingQuoterOptions) {\n return async function quoteShipping(request: ShippingQuoteRequest): Promise<QuotedShipping> {\n const outcome = await runRequest<ShippingQuoteOption[]>(options.connector, {\n skip: options.connected ? !options.connected() : false,\n call: (signal) => options.quote(request, signal),\n fallback: () => options.zones(request),\n })\n return { options: outcome.value, source: outcome.source, degraded: outcome.degraded }\n }\n}\n","// ---------------------------------------------------------------------------\n// Checkout on the request plane — the gateway the buyer is waiting for\n// ---------------------------------------------------------------------------\n// The mirror image of shipping: freight degrades so the sale continues, payment\n// BLOCKS so an order that did not pay never reads as confirmed. Both are the\n// connector's `onFailure` field rather than a decision re-taken in each screen.\n//\n// TWO THINGS ARE ENFORCED BY SHAPE HERE, not by discipline:\n//\n// 1. The amount is not an argument. `startCheckout` takes an order id and a\n// `recompute` function; the payable total is that function's RETURN value,\n// so a number edited in devtools has nowhere to enter. Both FAY-1300 and\n// FAY-1301 say \"nothing the browser sends is trusted\" — this is the shape\n// that makes it true instead of asserted.\n// 2. The gateway's echo is compared to what we recomputed. A session opened\n// for a different amount than the order is worth is a blocked step, not a\n// warning in a log.\n//\n// The credential is never here: `open` and `capture` are data-plane calls\n// (an edge function). commerce-stack.md §4, twice.\nimport { runRequest, runEffect, buyerIdempotencyKey } from '@fayz-ai/core'\nimport type { EffectLedger, EffectOutcome, RequestOutcome } from '@fayz-ai/core'\nimport { RequestBlockedError } from '@fayz-ai/core'\nimport type { RequestPlaneConnector } from './shipping'\n\n/** What the buyer picked. Open on purpose — a gateway declares what it can do. */\nexport type CheckoutMethod = 'pix' | 'boleto' | 'credit_card' | (string & {})\n\n/**\n * The payable artifacts, one shape per method.\n *\n * TRANSPARENT VS REDIRECT (commerce-stack.md §8.1, open until now): Pix and\n * boleto are transparent because nothing sensitive passes through us — a QR\n * code and a barcode are public strings. CARD IS REDIRECT: taking a card number\n * on our page moves us into PCI-DSS SAQ A-EP, which is architecture (browser\n * tokenisation, scope, an audit) rather than a week of work, and this milestone\n * is the contract, not the compliance programme.\n *\n * The decision is reversible for exactly the reason it is expressed here as an\n * artifact and not as a checkout flow: transparent card becomes\n * `card.tokenizationKey` alongside `card.redirectUrl`, and the storefront that\n * renders whatever came back does not change.\n */\nexport interface CheckoutArtifacts {\n pix?: { qrCode: string; copyPaste: string; expiresAt?: string }\n boleto?: { url: string; barcode?: string; dueDate?: string }\n card?: { redirectUrl: string; installments?: number }\n}\n\nexport type CheckoutSessionStatus = 'pending' | 'authorized' | 'paid' | 'failed' | 'expired'\n\nexport interface CheckoutSession {\n sessionId: string\n method: CheckoutMethod\n status: CheckoutSessionStatus\n /** Minor units. The recomputed one — never a number that arrived from a client. */\n amountCents: number\n currency: string\n artifacts: CheckoutArtifacts\n /** When the total was recomputed server-side for THIS session. The proof, carried with the value. */\n amountRecomputedAt: string\n}\n\nexport interface CheckoutStartRequest {\n orderId: string\n method: CheckoutMethod\n installments?: number\n // Deliberately no `amount`. See the header.\n}\n\nexport interface CheckoutStarterOptions {\n connector: RequestPlaneConnector\n /** Recompute the payable total from the order, server-side. The ONLY source of the amount. */\n recompute(orderId: string): Promise<{ amountCents: number; currency: string }>\n /** The data-plane call that opens the session at the gateway. */\n open(\n input: CheckoutStartRequest & { amountCents: number; currency: string },\n signal: AbortSignal,\n ): Promise<Omit<CheckoutSession, 'amountRecomputedAt'>>\n now?(): Date\n}\n\n/**\n * Open a payment session under the gateway's declared budget.\n *\n * Returns the full {@link RequestOutcome} rather than the bare session so the\n * caller can report `elapsedMs`; `source` is always `'provider'` here, because\n * a `block` connector never degrades — it throws {@link RequestBlockedError},\n * which carries the reason the buyer is shown.\n */\nexport function createCheckoutStarter(options: CheckoutStarterOptions) {\n const now = options.now ?? (() => new Date())\n\n return async function startCheckout(request: CheckoutStartRequest): Promise<RequestOutcome<CheckoutSession>> {\n // Outside the budget on purpose: this is our own database, and charging the\n // gateway's allowance for our own arithmetic would shrink the window the\n // gateway actually gets.\n const { amountCents, currency } = await options.recompute(request.orderId)\n const recomputedAt = now().toISOString()\n\n return runRequest<CheckoutSession>(options.connector, {\n call: async (signal) => {\n const session = await options.open({ ...request, amountCents, currency }, signal)\n if (session.amountCents !== amountCents || session.currency !== currency) {\n // Not a warning. The session that would be shown to the buyer is for a\n // different sum than the order is worth.\n throw new Error(\n `gateway opened a session for ${session.amountCents} ${session.currency}, order is ${amountCents} ${currency}`,\n )\n }\n return { ...session, amountRecomputedAt: recomputedAt }\n },\n })\n }\n}\n\nexport { RequestBlockedError }\n\n// ---------------------------------------------------------------------------\n// Capture — the effect, and the only half of checkout that files a run\n// ---------------------------------------------------------------------------\n\nexport interface CapturePaymentRequest {\n /** The plg_connections row acting. The tenant is resolved from it server-side. */\n connectionId: string\n orderId: string\n amountCents: number\n /** A gateway webhook by default; 'manual' when an operator captures from the panel. */\n triggerKind?: 'webhook' | 'manual'\n}\n\nexport interface CapturePaymentOptions {\n ledger: EffectLedger\n /** The data-plane call that captures at the gateway. Reached at most once per key. */\n capture(request: CapturePaymentRequest, runId: string): Promise<{ status: CheckoutSessionStatus; providerRef?: string }>\n}\n\n/**\n * Capture at most once per (order, amount).\n *\n * The key is the BUYER's — `capture:<order>:<cents>` — and not the gateway's\n * transaction id, because that id only exists after the call that must not\n * happen twice. A re-delivered webhook and a double-clicked button both hash to\n * the same string and the second one never reaches the gateway.\n */\nexport function createPaymentCapturer(options: CapturePaymentOptions) {\n return async function capturePayment(\n request: CapturePaymentRequest,\n ): Promise<EffectOutcome<{ status: CheckoutSessionStatus; providerRef?: string }>> {\n return runEffect(\n options.ledger,\n {\n connectionId: request.connectionId,\n idempotencyKey: buyerIdempotencyKey({\n effect: 'capture',\n subject: request.orderId,\n amountCents: request.amountCents,\n }),\n direction: 'outbound',\n triggerKind: request.triggerKind ?? 'webhook',\n stream: 'payment',\n },\n (runId) => options.capture(request, runId),\n )\n }\n}\n\n/**\n * Buy the shipping label at most once per order.\n *\n * The second effect on the plane, and the reason the ledger lives in\n * `@fayz-ai/core` rather than inside the gateway: a label bought twice is money\n * spent twice, by the same mechanism.\n */\nexport function createLabelBuyer(options: {\n ledger: EffectLedger\n buy(request: { connectionId: string; orderId: string }, runId: string): Promise<{ trackingCode?: string; labelUrl?: string }>\n}) {\n return async function buyLabel(request: { connectionId: string; orderId: string }) {\n return runEffect(\n options.ledger,\n {\n connectionId: request.connectionId,\n idempotencyKey: buyerIdempotencyKey({ effect: 'label', subject: request.orderId }),\n direction: 'outbound',\n triggerKind: 'manual',\n stream: 'shipment',\n },\n (runId) => options.buy(request, runId),\n )\n }\n}\n"]}
@@ -0,0 +1,36 @@
1
+ import type { EvaluateOptions } from './evaluator';
2
+ import type { Discount } from '../types';
3
+ import type { Promotion, RuleCartSnapshot } from './rule-types';
4
+ /** The storefront config shape (`config.discounts` entries). */
5
+ export interface StorefrontPercentCode {
6
+ code: string;
7
+ percent: number;
8
+ title?: string;
9
+ }
10
+ /** `{code, percent}` — the simplest promotion there is. */
11
+ export declare function storefrontDiscountToPromotion(discount: StorefrontPercentCode): Promotion;
12
+ /**
13
+ * A provider Discount row as a Promotion, or null for the untranslatable
14
+ * legacy `buy_x_get_y` (see header). Pure; tested against the existing
15
+ * coupon fixtures in adapter.test.ts.
16
+ */
17
+ export declare function discountToPromotion(discount: Discount): Promotion | null;
18
+ export interface CouponPreview {
19
+ valid: boolean;
20
+ /** Percentage points, when the promotion is an order-wide percentage — the
21
+ * only shape the client-side percent cart model can preview. */
22
+ percent: number;
23
+ /** Machine-readable reason when invalid — the useDiscountValidator message
24
+ * vocabulary: not_found | not_started | expired | usage_limit | inactive |
25
+ * rules_not_matched | unsupported. */
26
+ reason?: string;
27
+ }
28
+ /**
29
+ * The client-side coupon preview, engine-backed: find the promotion carrying
30
+ * the code, gate it through the SAME eligibility path the evaluator runs, and
31
+ * report the percent when (and only when) it is an order-wide percentage —
32
+ * anything richer is previewed as compute-actions and always revalidated
33
+ * server-side at place_order.
34
+ */
35
+ export declare function previewCouponPercent(promotions: Promotion[], code: string, cart?: RuleCartSnapshot, options?: EvaluateOptions): CouponPreview;
36
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../src/rules/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,EAAqB,SAAS,EAAiB,gBAAgB,EAAE,MAAM,cAAc,CAAA;AA6BjG,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,2DAA2D;AAC3D,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,qBAAqB,GAAG,SAAS,CAUxF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,IAAI,CA8CxE;AAID,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,OAAO,CAAA;IACd;qEACiE;IACjE,OAAO,EAAE,MAAM,CAAA;IACf;;2CAEuC;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,SAAS,EAAE,EACvB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,gBAAgC,EACtC,OAAO,GAAE,eAAoB,GAC5B,aAAa,CAef"}