@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.
- package/README.md +18 -1
- package/dist/admin.d.ts +2 -0
- package/dist/admin.d.ts.map +1 -0
- package/dist/catalog.d.ts +1 -3
- package/dist/catalog.d.ts.map +1 -1
- package/dist/catalog.js +2 -2
- package/dist/{chunk-IYQHKMLB.js → chunk-2WACO2TG.js} +4 -4
- package/dist/chunk-2WACO2TG.js.map +1 -0
- package/dist/chunk-3GERSY5F.js +526 -0
- package/dist/chunk-3GERSY5F.js.map +1 -0
- package/dist/chunk-6BO2TMZ5.js +93 -0
- package/dist/chunk-6BO2TMZ5.js.map +1 -0
- package/dist/chunk-FQPGE3YN.js +3 -0
- package/dist/{chunk-JR25TISO.js.map → chunk-FQPGE3YN.js.map} +1 -1
- package/dist/{chunk-CTKZ6O2J.js → chunk-IKNGCRUM.js} +41 -15
- package/dist/chunk-IKNGCRUM.js.map +1 -0
- package/dist/chunk-KLDS7IPU.js +17 -0
- package/dist/chunk-KLDS7IPU.js.map +1 -0
- package/dist/chunk-NWW3WJ7Y.js +136 -0
- package/dist/chunk-NWW3WJ7Y.js.map +1 -0
- package/dist/chunk-STRNMFC4.js +1438 -0
- package/dist/chunk-STRNMFC4.js.map +1 -0
- package/dist/chunk-U6HV3LVP.js +1170 -0
- package/dist/chunk-U6HV3LVP.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/data/customer-search.d.ts +3 -0
- package/dist/data/customer-search.d.ts.map +1 -0
- package/dist/data/functions.d.ts +9 -0
- package/dist/data/functions.d.ts.map +1 -0
- package/dist/data/media.d.ts +37 -0
- package/dist/data/media.d.ts.map +1 -0
- package/dist/{mock-catalog.d.ts → data/mock-catalog.d.ts} +21 -1
- package/dist/data/mock-catalog.d.ts.map +1 -0
- package/dist/data/mock-provider.d.ts +165 -0
- package/dist/data/mock-provider.d.ts.map +1 -0
- package/dist/data/mock.d.ts +3 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/provider.d.ts +335 -0
- package/dist/data/provider.d.ts.map +1 -0
- package/dist/data/supabase-provider.d.ts +208 -0
- package/dist/data/supabase-provider.d.ts.map +1 -0
- package/dist/data/tables.d.ts +38 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/data/tenant.d.ts.map +1 -0
- package/dist/entities/entity-reconcile.d.ts +32 -0
- package/dist/entities/entity-reconcile.d.ts.map +1 -0
- package/dist/entities/entity-sql.d.ts +50 -0
- package/dist/entities/entity-sql.d.ts.map +1 -0
- package/dist/entities/entity-store.d.ts +58 -0
- package/dist/entities/entity-store.d.ts.map +1 -0
- package/dist/entities/index.d.ts +10 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/mock-entity-store.d.ts +17 -0
- package/dist/entities/mock-entity-store.d.ts.map +1 -0
- package/dist/entities/supabase-entity-store.d.ts +8 -0
- package/dist/entities/supabase-entity-store.d.ts.map +1 -0
- package/dist/entities.js +159 -0
- package/dist/entities.js.map +1 -0
- package/dist/index.d.ts +28 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1558 -40
- package/dist/index.js.map +1 -1
- package/dist/inventory/allocation.d.ts +98 -0
- package/dist/inventory/allocation.d.ts.map +1 -0
- package/dist/inventory/index.d.ts +13 -0
- package/dist/inventory/index.d.ts.map +1 -0
- package/dist/inventory/mock-stock-store.d.ts +89 -0
- package/dist/inventory/mock-stock-store.d.ts.map +1 -0
- package/dist/inventory/postgrest-double.d.ts +65 -0
- package/dist/inventory/postgrest-double.d.ts.map +1 -0
- package/dist/inventory/shopify-mapping.d.ts +67 -0
- package/dist/inventory/shopify-mapping.d.ts.map +1 -0
- package/dist/inventory/stock-contract.d.ts +46 -0
- package/dist/inventory/stock-contract.d.ts.map +1 -0
- package/dist/inventory/stock-store.d.ts +96 -0
- package/dist/inventory/stock-store.d.ts.map +1 -0
- package/dist/inventory/stock-types.d.ts +351 -0
- package/dist/inventory/stock-types.d.ts.map +1 -0
- package/dist/inventory/supabase-stock-store.d.ts +60 -0
- package/dist/inventory/supabase-stock-store.d.ts.map +1 -0
- package/dist/inventory/vocabulary.d.ts +86 -0
- package/dist/inventory/vocabulary.d.ts.map +1 -0
- package/dist/inventory.js +4 -0
- package/dist/inventory.js.map +1 -0
- package/dist/lib/catalog.d.ts +4 -0
- package/dist/lib/catalog.d.ts.map +1 -0
- package/dist/{commerce.d.ts → lib/commerce.d.ts} +2 -2
- package/dist/lib/commerce.d.ts.map +1 -0
- package/dist/lib/document.d.ts +13 -0
- package/dist/lib/document.d.ts.map +1 -0
- package/dist/lib/emails.d.ts +50 -0
- package/dist/lib/emails.d.ts.map +1 -0
- package/dist/lib/message-events.d.ts +4 -0
- package/dist/lib/message-events.d.ts.map +1 -0
- package/dist/lib/runtime.d.ts +5 -0
- package/dist/lib/runtime.d.ts.map +1 -0
- package/dist/lib/variant-matrix.d.ts +120 -0
- package/dist/lib/variant-matrix.d.ts.map +1 -0
- package/dist/mock.d.ts +1 -2
- package/dist/mock.d.ts.map +1 -1
- package/dist/mock.js +4 -2
- package/dist/provider.d.ts +1 -88
- package/dist/provider.d.ts.map +1 -1
- package/dist/providers/checkout.d.ts +129 -0
- package/dist/providers/checkout.d.ts.map +1 -0
- package/dist/providers/index.d.ts +5 -0
- package/dist/providers/index.d.ts.map +1 -0
- package/dist/providers/shipping.d.ts +43 -0
- package/dist/providers/shipping.d.ts.map +1 -0
- package/dist/providers.js +70 -0
- package/dist/providers.js.map +1 -0
- package/dist/rules/adapter.d.ts +36 -0
- package/dist/rules/adapter.d.ts.map +1 -0
- package/dist/rules/attributes.d.ts +64 -0
- package/dist/rules/attributes.d.ts.map +1 -0
- package/dist/rules/context.d.ts +43 -0
- package/dist/rules/context.d.ts.map +1 -0
- package/dist/rules/evaluator.d.ts +37 -0
- package/dist/rules/evaluator.d.ts.map +1 -0
- package/dist/rules/index.d.ts +13 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/prices.d.ts +25 -0
- package/dist/rules/prices.d.ts.map +1 -0
- package/dist/rules/rule-types.d.ts +160 -0
- package/dist/rules/rule-types.d.ts.map +1 -0
- package/dist/rules.js +22 -0
- package/dist/rules.js.map +1 -0
- package/dist/runtime.d.ts +1 -4
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +5 -3
- package/dist/shipping/label-provider.d.ts +43 -0
- package/dist/shipping/label-provider.d.ts.map +1 -0
- package/dist/types.d.ts +688 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +30 -20
- package/dist/chunk-CTKZ6O2J.js.map +0 -1
- package/dist/chunk-IYQHKMLB.js.map +0 -1
- package/dist/chunk-JR25TISO.js +0 -3
- package/dist/chunk-SVTCJ6HP.js +0 -440
- package/dist/chunk-SVTCJ6HP.js.map +0 -1
- package/dist/commerce.d.ts.map +0 -1
- package/dist/mock-catalog.d.ts.map +0 -1
- package/dist/mock-provider.d.ts +0 -60
- package/dist/mock-provider.d.ts.map +0 -1
- package/dist/supabase-provider.d.ts +0 -56
- package/dist/supabase-provider.d.ts.map +0 -1
- package/dist/tables.d.ts +0 -12
- package/dist/tables.d.ts.map +0 -1
- package/dist/tenant.d.ts.map +0 -1
- package/src/catalog.ts +0 -3
- package/src/commerce.ts +0 -120
- package/src/index.ts +0 -40
- package/src/mock-catalog.ts +0 -266
- package/src/mock-provider.ts +0 -480
- package/src/mock.ts +0 -2
- package/src/provider.ts +0 -117
- package/src/runtime.ts +0 -20
- package/src/supabase-provider.ts +0 -754
- package/src/tables.ts +0 -17
- package/src/tenant.ts +0 -21
- package/src/types.ts +0 -623
- /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
package/dist/mock.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../src/mock.ts"],"names":[],"mappings":"
|
|
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-
|
|
2
|
-
import './chunk-
|
|
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
|
package/dist/provider.d.ts
CHANGED
|
@@ -1,89 +1,2 @@
|
|
|
1
|
-
|
|
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
|
package/dist/provider.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"
|
|
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"}
|