@vitrine-kit/core 0.2.1 → 0.3.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 +20 -1
- package/dist/index.d.ts +18 -3
- package/dist/index.js +25 -2
- package/dist/react.js +8 -1
- package/package.json +9 -5
package/README.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @vitrine-kit/core
|
|
2
2
|
|
|
3
|
-
Vitrine's critical logic: the slot and adapter runtime, the order pipeline, and
|
|
3
|
+
Vitrine's critical logic: the slot and adapter runtime, the order pipeline, and provider-neutral payment webhook dispatch.
|
|
4
4
|
|
|
5
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.
|
|
6
|
+
|
|
7
|
+
## Global registries and isolation
|
|
8
|
+
|
|
9
|
+
`slotRegistry`, `adapters`, and `payments` are **module-level singletons** — state is shared by
|
|
10
|
+
everything importing this package in one process. That is the right default for a Vitrine client
|
|
11
|
+
(1 client = 1 repository = 1 store; `lib/slots.ts` / `lib/payments.ts` register once at module
|
|
12
|
+
load, which is idempotent-safe because generated registration modules only run once per process).
|
|
13
|
+
|
|
14
|
+
Do NOT reach for the globals when you need isolation:
|
|
15
|
+
|
|
16
|
+
- **Tests** — create your own: `createSlotRegistry()`, `createAdapterRegistry()`,
|
|
17
|
+
`createPaymentRegistry()`; `<Slot registry={...}>` accepts one. Or `clear()` the global in a
|
|
18
|
+
`finally` block.
|
|
19
|
+
- **Multi-tenant / multi-store servers** — one process serving several stores must NOT share the
|
|
20
|
+
globals (registrations from one tenant would leak into another). Keep a registry instance per
|
|
21
|
+
tenant and pass it explicitly.
|
|
22
|
+
|
|
23
|
+
Registering the same mount twice (e.g. calling a `register<Feature>Slots()` again after HMR)
|
|
24
|
+
duplicates it — re-registration should go through `clear()` first.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
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
|
+
declare const CORE_VERSION: "0.3.0";
|
|
5
|
+
|
|
4
6
|
interface AdapterFactory {
|
|
5
7
|
backend: Backend;
|
|
6
8
|
createCatalog(config: SiteConfig): CatalogSource;
|
|
@@ -27,7 +29,7 @@ interface OrderPipelineContext {
|
|
|
27
29
|
type OrderStage<T = OrderPipelineContext> = (ctx: T) => T | Promise<T>;
|
|
28
30
|
/**
|
|
29
31
|
* Runs the context through the stages sequentially. Any stage error aborts the
|
|
30
|
-
* pipeline (rollback/idempotency are the responsibility of specific stages
|
|
32
|
+
* pipeline (rollback/idempotency are the responsibility of specific stages).
|
|
31
33
|
*/
|
|
32
34
|
declare function runPipeline<T>(ctx: T, stages: ReadonlyArray<OrderStage<T>>): Promise<T>;
|
|
33
35
|
|
|
@@ -141,6 +143,19 @@ interface BuildOrderOptions {
|
|
|
141
143
|
/** Snapshots the cart into an order (after payment). Totals come from the cart — not recomputed. */
|
|
142
144
|
declare function buildOrderFromCart(cart: Cart, opts: BuildOrderOptions): Order;
|
|
143
145
|
|
|
144
|
-
|
|
146
|
+
interface RateLimitOptions {
|
|
147
|
+
/** Max requests allowed within the window. */
|
|
148
|
+
limit: number;
|
|
149
|
+
/** Window size in milliseconds. */
|
|
150
|
+
windowMs: number;
|
|
151
|
+
}
|
|
152
|
+
interface RateLimitResult {
|
|
153
|
+
allowed: boolean;
|
|
154
|
+
/** Milliseconds until the caller may retry — present only when !allowed. */
|
|
155
|
+
retryAfterMs?: number;
|
|
156
|
+
}
|
|
157
|
+
declare function checkRateLimit(key: string, { limit, windowMs }: RateLimitOptions): RateLimitResult;
|
|
158
|
+
/** Best-effort client IP from standard proxy headers (Web Fetch API `Headers`). */
|
|
159
|
+
declare function clientIpFromHeaders(headers: Headers): string;
|
|
145
160
|
|
|
146
|
-
export { type AdapterFactory, type AdapterRegistry, type BuildOrderOptions, CORE_VERSION, type CreateCheckoutArgs, type NewLineInput, type NormalizedPaymentEvent, type OrderCreationGuard, type OrderPipelineContext, type OrderStage, type PaymentProvider, type PaymentProviderName, type PaymentRegistry, type PaymentWebhookHandlers, type PaymentWebhookRequest, type PaymentWebhookResult, adapters, addCartLine, buildOrderFromCart, cartItemCount, computeLineTotal, createAdapterRegistry, createPaymentRegistry, emptyCart, handlePaymentWebhook, payments, recalcCart, removeCartLine, runPipeline, setCartLineQty, shouldCreateOrder };
|
|
161
|
+
export { type AdapterFactory, type AdapterRegistry, type BuildOrderOptions, CORE_VERSION, type CreateCheckoutArgs, type NewLineInput, type NormalizedPaymentEvent, type OrderCreationGuard, type OrderPipelineContext, type OrderStage, type PaymentProvider, type PaymentProviderName, type PaymentRegistry, type PaymentWebhookHandlers, type PaymentWebhookRequest, type PaymentWebhookResult, type RateLimitOptions, type RateLimitResult, adapters, addCartLine, buildOrderFromCart, cartItemCount, checkRateLimit, clientIpFromHeaders, computeLineTotal, createAdapterRegistry, createPaymentRegistry, emptyCart, handlePaymentWebhook, payments, recalcCart, removeCartLine, runPipeline, setCartLineQty, shouldCreateOrder };
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
slotRegistry
|
|
6
6
|
} from "./chunk-BQVNYHVU.js";
|
|
7
7
|
|
|
8
|
+
// src/version.generated.ts
|
|
9
|
+
var CORE_VERSION = "0.3.0";
|
|
10
|
+
|
|
8
11
|
// src/adapter/resolver.ts
|
|
9
12
|
function createAdapterRegistry() {
|
|
10
13
|
const factories = /* @__PURE__ */ new Map();
|
|
@@ -184,14 +187,34 @@ function buildOrderFromCart(cart, opts) {
|
|
|
184
187
|
};
|
|
185
188
|
}
|
|
186
189
|
|
|
187
|
-
// src/
|
|
188
|
-
var
|
|
190
|
+
// src/security/rate-limit.ts
|
|
191
|
+
var buckets = /* @__PURE__ */ new Map();
|
|
192
|
+
function checkRateLimit(key, { limit, windowMs }) {
|
|
193
|
+
const now = Date.now();
|
|
194
|
+
const bucket = buckets.get(key);
|
|
195
|
+
if (!bucket || bucket.resetAt <= now) {
|
|
196
|
+
buckets.set(key, { count: 1, resetAt: now + windowMs });
|
|
197
|
+
return { allowed: true };
|
|
198
|
+
}
|
|
199
|
+
if (bucket.count >= limit) {
|
|
200
|
+
return { allowed: false, retryAfterMs: bucket.resetAt - now };
|
|
201
|
+
}
|
|
202
|
+
bucket.count += 1;
|
|
203
|
+
return { allowed: true };
|
|
204
|
+
}
|
|
205
|
+
function clientIpFromHeaders(headers) {
|
|
206
|
+
const forwarded = headers.get("x-forwarded-for");
|
|
207
|
+
const first = forwarded?.split(",")[0]?.trim();
|
|
208
|
+
return first || headers.get("x-real-ip") || "unknown";
|
|
209
|
+
}
|
|
189
210
|
export {
|
|
190
211
|
CORE_VERSION,
|
|
191
212
|
adapters,
|
|
192
213
|
addCartLine,
|
|
193
214
|
buildOrderFromCart,
|
|
194
215
|
cartItemCount,
|
|
216
|
+
checkRateLimit,
|
|
217
|
+
clientIpFromHeaders,
|
|
195
218
|
computeLineTotal,
|
|
196
219
|
createAdapterRegistry,
|
|
197
220
|
createPaymentRegistry,
|
package/dist/react.js
CHANGED
|
@@ -12,10 +12,17 @@ function Slot(props) {
|
|
|
12
12
|
const reg = registry ?? slotRegistry;
|
|
13
13
|
const mounts = reg.get(name);
|
|
14
14
|
if (mounts.length === 0) return fallback;
|
|
15
|
+
const seen = /* @__PURE__ */ new Map();
|
|
15
16
|
return createElement(
|
|
16
17
|
Fragment,
|
|
17
18
|
null,
|
|
18
|
-
...mounts.map((m
|
|
19
|
+
...mounts.map((m) => {
|
|
20
|
+
const c = m.component;
|
|
21
|
+
const base = c.displayName ?? (c.name || "mount");
|
|
22
|
+
const n = seen.get(base) ?? 0;
|
|
23
|
+
seen.set(base, n + 1);
|
|
24
|
+
return createElement(m.component, { key: n === 0 ? base : `${base}:${n}`, ...rest });
|
|
25
|
+
})
|
|
19
26
|
);
|
|
20
27
|
}
|
|
21
28
|
export {
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vitrine-kit/core",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Vitrine core — slot/adapter runtime, order pipeline,
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"description": "Vitrine core — slot/adapter runtime, order pipeline, provider-neutral payment webhook dispatch. Critical logic (a bug = an incident for everyone).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=20"
|
|
10
|
+
},
|
|
7
11
|
"types": "./dist/index.d.ts",
|
|
8
12
|
"main": "./dist/index.js",
|
|
9
13
|
"module": "./dist/index.js",
|
|
@@ -29,7 +33,7 @@
|
|
|
29
33
|
"directory": "packages/core"
|
|
30
34
|
},
|
|
31
35
|
"dependencies": {
|
|
32
|
-
"@vitrine-kit/contracts": "1.2.
|
|
36
|
+
"@vitrine-kit/contracts": "1.2.1"
|
|
33
37
|
},
|
|
34
38
|
"peerDependencies": {
|
|
35
39
|
"react": ">=18.0.0"
|
|
@@ -44,8 +48,8 @@
|
|
|
44
48
|
"react": "^18.3.1"
|
|
45
49
|
},
|
|
46
50
|
"scripts": {
|
|
47
|
-
"build": "tsup src/index.ts src/react.ts --format esm --dts --clean",
|
|
48
|
-
"typecheck": "tsc --noEmit",
|
|
51
|
+
"build": "node scripts/generate-version.mjs && tsup src/index.ts src/react.ts --format esm --dts --clean",
|
|
52
|
+
"typecheck": "node scripts/generate-version.mjs && tsc --noEmit",
|
|
49
53
|
"test": "vitest run --passWithNoTests"
|
|
50
54
|
}
|
|
51
55
|
}
|