@vitrine-kit/core 0.2.0 → 0.2.1
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 +2 -4
- package/dist/index.d.ts +32 -32
- package/dist/index.js +7 -7
- package/dist/react.d.ts +4 -4
- package/dist/{registry-B4Ak1hIR.d.ts → registry-CfORxf9H.d.ts} +2 -2
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
# @vitrine-kit/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Vitrine's critical logic: the slot and adapter runtime, the order pipeline, and the Stripe webhook handler.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
Скелет (M0). Наполняется в M2 (runtime) и M8 (коммерция).
|
|
5
|
+
This is where "a bug = an incident for every client at once" (spec §4). That's why it's a **versioned package** rather than copy-in: a critical fix reaches everyone via a version bump.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export { S as SlotRegistry, c as createSlotRegistry, g as getSlotMounts, r as registerSlot, s as slotRegistry } from './registry-
|
|
1
|
+
export { S as SlotRegistry, c as createSlotRegistry, g as getSlotMounts, r as registerSlot, s as slotRegistry } from './registry-CfORxf9H.js';
|
|
2
2
|
import { Backend, SiteConfig, CatalogSource, CommerceBackend, Cart, Order, Money, CurrencyCode, OrderStatus } from '@vitrine-kit/contracts';
|
|
3
3
|
|
|
4
4
|
interface AdapterFactory {
|
|
5
5
|
backend: Backend;
|
|
6
6
|
createCatalog(config: SiteConfig): CatalogSource;
|
|
7
|
-
/**
|
|
7
|
+
/** Absent for catalog-only backends. */
|
|
8
8
|
createCommerce?(config: SiteConfig): CommerceBackend;
|
|
9
9
|
}
|
|
10
10
|
interface AdapterRegistry {
|
|
@@ -14,82 +14,82 @@ interface AdapterRegistry {
|
|
|
14
14
|
clear(): void;
|
|
15
15
|
}
|
|
16
16
|
declare function createAdapterRegistry(): AdapterRegistry;
|
|
17
|
-
/**
|
|
17
|
+
/** The default global adapter registry. */
|
|
18
18
|
declare const adapters: AdapterRegistry;
|
|
19
19
|
|
|
20
20
|
interface OrderPipelineContext {
|
|
21
21
|
cart: Cart;
|
|
22
|
-
/**
|
|
22
|
+
/** Filled by pipeline stages (order creation). */
|
|
23
23
|
order?: Order;
|
|
24
|
-
/**
|
|
24
|
+
/** Arbitrary stage data (payment, stock reservation, shipping). */
|
|
25
25
|
meta: Record<string, unknown>;
|
|
26
26
|
}
|
|
27
27
|
type OrderStage<T = OrderPipelineContext> = (ctx: T) => T | Promise<T>;
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
29
|
+
* Runs the context through the stages sequentially. Any stage error aborts the
|
|
30
|
+
* pipeline (rollback/idempotency are the responsibility of specific stages in M8).
|
|
31
31
|
*/
|
|
32
32
|
declare function runPipeline<T>(ctx: T, stages: ReadonlyArray<OrderStage<T>>): Promise<T>;
|
|
33
33
|
|
|
34
34
|
interface OrderCreationGuard {
|
|
35
|
-
/**
|
|
35
|
+
/** Cart status (the Payload `carts` map): 'converted' = already paid. */
|
|
36
36
|
cartStatus?: string | null;
|
|
37
|
-
/**
|
|
37
|
+
/** Payment reference of the current event (session/transaction/payment id). */
|
|
38
38
|
providerRef?: string;
|
|
39
|
-
/** paymentRef
|
|
39
|
+
/** paymentRef of existing orders (usually the result of a targeted lookup). */
|
|
40
40
|
existingOrderRefs?: ReadonlyArray<string | undefined>;
|
|
41
41
|
}
|
|
42
42
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
43
|
+
* Whether to create an order for this event. false if the cart is already converted OR
|
|
44
|
+
* an order already exists for this payment reference (redelivery/retry of the webhook).
|
|
45
45
|
*/
|
|
46
46
|
declare function shouldCreateOrder(guard: OrderCreationGuard): boolean;
|
|
47
47
|
|
|
48
|
-
/**
|
|
48
|
+
/** Matches integrations.payments in site.config (contract 4). */
|
|
49
49
|
type PaymentProviderName = 'stripe' | 'paddle' | 'yookassa';
|
|
50
50
|
interface CreateCheckoutArgs {
|
|
51
51
|
cart: Cart;
|
|
52
|
-
/**
|
|
52
|
+
/** The storefront base URL; success/cancel are built relative to it. */
|
|
53
53
|
baseUrl: string;
|
|
54
|
-
/**
|
|
54
|
+
/** Defaults to '/order/success'. */
|
|
55
55
|
successPath?: string;
|
|
56
|
-
/**
|
|
56
|
+
/** Defaults to '/cart'. */
|
|
57
57
|
cancelPath?: string;
|
|
58
58
|
}
|
|
59
59
|
interface PaymentWebhookRequest {
|
|
60
60
|
rawBody: string;
|
|
61
61
|
headers: Record<string, string | null>;
|
|
62
62
|
}
|
|
63
|
-
/**
|
|
63
|
+
/** A normalized provider event — the common language for webhook routes. */
|
|
64
64
|
interface NormalizedPaymentEvent {
|
|
65
65
|
kind: 'checkout_completed' | 'payment_failed' | 'unknown';
|
|
66
|
-
/**
|
|
66
|
+
/** From the provider's metadata/custom_data/label. */
|
|
67
67
|
cartId?: string;
|
|
68
|
-
/**
|
|
68
|
+
/** The provider's unique payment reference — the idempotency key. */
|
|
69
69
|
providerRef?: string;
|
|
70
70
|
email?: string;
|
|
71
|
-
/**
|
|
71
|
+
/** The raw provider object (in case the route needs extra fields). */
|
|
72
72
|
raw: unknown;
|
|
73
73
|
}
|
|
74
74
|
interface PaymentProvider {
|
|
75
75
|
name: PaymentProviderName;
|
|
76
|
-
/**
|
|
76
|
+
/** Creates a hosted checkout at the provider → redirect URL. */
|
|
77
77
|
createCheckout(args: CreateCheckoutArgs): Promise<{
|
|
78
78
|
redirectUrl: string;
|
|
79
79
|
}>;
|
|
80
|
-
/**
|
|
80
|
+
/** Verifies the signature/authenticity and normalizes the event. Throws if invalid. */
|
|
81
81
|
verifyWebhook(req: PaymentWebhookRequest): Promise<NormalizedPaymentEvent>;
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
interface PaymentRegistry {
|
|
85
85
|
register(provider: PaymentProvider): void;
|
|
86
|
-
/**
|
|
86
|
+
/** The active provider by site.config.integrations.payments. */
|
|
87
87
|
resolve(config: SiteConfig): PaymentProvider;
|
|
88
88
|
get(name: PaymentProviderName): PaymentProvider | undefined;
|
|
89
89
|
clear(): void;
|
|
90
90
|
}
|
|
91
91
|
declare function createPaymentRegistry(): PaymentRegistry;
|
|
92
|
-
/**
|
|
92
|
+
/** The default global provider registry. */
|
|
93
93
|
declare const payments: PaymentRegistry;
|
|
94
94
|
|
|
95
95
|
interface PaymentWebhookHandlers {
|
|
@@ -99,7 +99,7 @@ interface PaymentWebhookHandlers {
|
|
|
99
99
|
interface PaymentWebhookResult {
|
|
100
100
|
received: true;
|
|
101
101
|
kind: NormalizedPaymentEvent['kind'];
|
|
102
|
-
/**
|
|
102
|
+
/** Whether a handler was registered for this event kind. */
|
|
103
103
|
handled: boolean;
|
|
104
104
|
}
|
|
105
105
|
declare function handlePaymentWebhook(args: {
|
|
@@ -109,9 +109,9 @@ declare function handlePaymentWebhook(args: {
|
|
|
109
109
|
}): Promise<PaymentWebhookResult>;
|
|
110
110
|
|
|
111
111
|
declare function computeLineTotal(unitPrice: Money, quantity: number): Money;
|
|
112
|
-
/**
|
|
112
|
+
/** An empty cart. */
|
|
113
113
|
declare function emptyCart(id: string, currency: CurrencyCode): Cart;
|
|
114
|
-
/**
|
|
114
|
+
/** Recomputes each line's lineTotal and the totals (subtotal/total incl. discount). */
|
|
115
115
|
declare function recalcCart(cart: Cart): Cart;
|
|
116
116
|
interface NewLineInput {
|
|
117
117
|
id: string;
|
|
@@ -122,12 +122,12 @@ interface NewLineInput {
|
|
|
122
122
|
quantity: number;
|
|
123
123
|
image?: string;
|
|
124
124
|
}
|
|
125
|
-
/**
|
|
125
|
+
/** Adds a line; if the variant is already in the cart, increases its quantity. */
|
|
126
126
|
declare function addCartLine(cart: Cart, input: NewLineInput): Cart;
|
|
127
|
-
/**
|
|
127
|
+
/** Changes a line's quantity; quantity ≤ 0 removes the line. */
|
|
128
128
|
declare function setCartLineQty(cart: Cart, lineId: string, quantity: number): Cart;
|
|
129
129
|
declare function removeCartLine(cart: Cart, lineId: string): Cart;
|
|
130
|
-
/**
|
|
130
|
+
/** Total number of units in the cart (for the header indicator). */
|
|
131
131
|
declare function cartItemCount(cart: Cart): number;
|
|
132
132
|
|
|
133
133
|
interface BuildOrderOptions {
|
|
@@ -135,10 +135,10 @@ interface BuildOrderOptions {
|
|
|
135
135
|
status?: OrderStatus;
|
|
136
136
|
email?: string;
|
|
137
137
|
number?: string;
|
|
138
|
-
/** ISO-8601;
|
|
138
|
+
/** ISO-8601; defaults to now. */
|
|
139
139
|
createdAt?: string;
|
|
140
140
|
}
|
|
141
|
-
/**
|
|
141
|
+
/** Snapshots the cart into an order (after payment). Totals come from the cart — not recomputed. */
|
|
142
142
|
declare function buildOrderFromCart(cart: Cart, opts: BuildOrderOptions): Order;
|
|
143
143
|
|
|
144
144
|
declare const CORE_VERSION: "0.1.0";
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ function createAdapterRegistry() {
|
|
|
14
14
|
function factoryFor(config) {
|
|
15
15
|
const f = factories.get(config.backend);
|
|
16
16
|
if (!f) {
|
|
17
|
-
throw new Error(`[vitrine]
|
|
17
|
+
throw new Error(`[vitrine] no adapter registered for backend "${config.backend}"`);
|
|
18
18
|
}
|
|
19
19
|
return f;
|
|
20
20
|
}
|
|
@@ -23,11 +23,11 @@ function createAdapterRegistry() {
|
|
|
23
23
|
}
|
|
24
24
|
function resolveCommerce(config) {
|
|
25
25
|
if (config.tier === "catalog") {
|
|
26
|
-
throw new Error('[vitrine] CommerceBackend
|
|
26
|
+
throw new Error('[vitrine] CommerceBackend is unavailable at tier "catalog"');
|
|
27
27
|
}
|
|
28
28
|
const f = factoryFor(config);
|
|
29
29
|
if (!f.createCommerce) {
|
|
30
|
-
throw new Error(`[vitrine] backend "${config.backend}"
|
|
30
|
+
throw new Error(`[vitrine] backend "${config.backend}" does not implement CommerceBackend`);
|
|
31
31
|
}
|
|
32
32
|
return f.createCommerce(config);
|
|
33
33
|
}
|
|
@@ -66,12 +66,12 @@ function createPaymentRegistry() {
|
|
|
66
66
|
const name = config.integrations.payments;
|
|
67
67
|
if (!name) {
|
|
68
68
|
throw new Error(
|
|
69
|
-
"[vitrine] integrations.payments
|
|
69
|
+
"[vitrine] integrations.payments is not set in site.config \u2014 install a checkout-<provider> feature"
|
|
70
70
|
);
|
|
71
71
|
}
|
|
72
72
|
const provider = providers.get(name);
|
|
73
73
|
if (!provider) {
|
|
74
|
-
throw new Error(`[vitrine]
|
|
74
|
+
throw new Error(`[vitrine] payment provider "${name}" is not registered`);
|
|
75
75
|
}
|
|
76
76
|
return provider;
|
|
77
77
|
}
|
|
@@ -123,7 +123,7 @@ function recalcCart(cart) {
|
|
|
123
123
|
}
|
|
124
124
|
function addCartLine(cart, input) {
|
|
125
125
|
if (!Number.isInteger(input.quantity) || input.quantity <= 0) {
|
|
126
|
-
throw new Error(`[vitrine]
|
|
126
|
+
throw new Error(`[vitrine] invalid line quantity: ${input.quantity} (must be an integer > 0)`);
|
|
127
127
|
}
|
|
128
128
|
const exists = cart.lines.some((l) => l.variantId === input.variantId);
|
|
129
129
|
const lines = exists ? cart.lines.map(
|
|
@@ -145,7 +145,7 @@ function addCartLine(cart, input) {
|
|
|
145
145
|
}
|
|
146
146
|
function setCartLineQty(cart, lineId, quantity) {
|
|
147
147
|
if (!Number.isInteger(quantity)) {
|
|
148
|
-
throw new Error(`[vitrine]
|
|
148
|
+
throw new Error(`[vitrine] invalid quantity: ${quantity} (must be an integer)`);
|
|
149
149
|
}
|
|
150
150
|
if (quantity <= 0) return removeCartLine(cart, lineId);
|
|
151
151
|
return recalcCart({
|
package/dist/react.d.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { ComponentType, ReactNode } from 'react';
|
|
2
2
|
import { SlotId } from '@vitrine-kit/contracts';
|
|
3
|
-
import { S as SlotRegistry } from './registry-
|
|
3
|
+
import { S as SlotRegistry } from './registry-CfORxf9H.js';
|
|
4
4
|
|
|
5
5
|
type SlotComponentRegistry = SlotRegistry<ComponentType<Record<string, unknown>>>;
|
|
6
6
|
interface SlotProps {
|
|
7
7
|
name: SlotId;
|
|
8
|
-
/**
|
|
8
|
+
/** The registry (global by default). Pass your own for tests/isolation. */
|
|
9
9
|
registry?: SlotComponentRegistry;
|
|
10
|
-
/**
|
|
10
|
+
/** What to render if the slot is empty. */
|
|
11
11
|
fallback?: ReactNode;
|
|
12
|
-
/**
|
|
12
|
+
/** The remaining props are forwarded to each mounted component. */
|
|
13
13
|
[prop: string]: unknown;
|
|
14
14
|
}
|
|
15
15
|
declare function Slot(props: SlotProps): ReactNode;
|
|
@@ -3,12 +3,12 @@ import { SlotMount, SlotId } from '@vitrine-kit/contracts';
|
|
|
3
3
|
interface SlotRegistry<C = unknown> {
|
|
4
4
|
register(mount: SlotMount<C>): void;
|
|
5
5
|
registerMany(mounts: ReadonlyArray<SlotMount<C>>): void;
|
|
6
|
-
/**
|
|
6
|
+
/** A slot's bindings, sorted by order (stable by registration order). */
|
|
7
7
|
get(slot: SlotId): SlotMount<C>[];
|
|
8
8
|
clear(): void;
|
|
9
9
|
}
|
|
10
10
|
declare function createSlotRegistry<C = unknown>(): SlotRegistry<C>;
|
|
11
|
-
/**
|
|
11
|
+
/** The default global registry (the client registers slots in lib/slots.ts). */
|
|
12
12
|
declare const slotRegistry: SlotRegistry;
|
|
13
13
|
declare function registerSlot<C = unknown>(mount: SlotMount<C>): void;
|
|
14
14
|
declare function getSlotMounts<C = unknown>(slot: SlotId): SlotMount<C>[];
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitrine-kit/core",
|
|
3
|
-
"version": "0.2.
|
|
4
|
-
"description": "Vitrine core — runtime
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Vitrine core — slot/adapter runtime, order pipeline, Stripe webhook. Critical logic (a bug = an incident for everyone).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"directory": "packages/core"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@vitrine-kit/contracts": "1.
|
|
32
|
+
"@vitrine-kit/contracts": "1.2.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": ">=18.0.0"
|