@vitrine-kit/core 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vitrine Kit (Max Davydov)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,7 @@
1
+ # @vitrine-kit/core
2
+
3
+ Критическая логика Vitrine: runtime слотов и адаптера, order pipeline, обработчик Stripe-вебхука.
4
+
5
+ Здесь — то, где «баг = инцидент у всех клиентов сразу» (§4 спеки). Поэтому это **версионируемый пакет**, а не copy-in: критфикс прилетает всем бампом версии.
6
+
7
+ Скелет (M0). Наполняется в M2 (runtime) и M8 (коммерция).
@@ -0,0 +1,36 @@
1
+ // src/slots/registry.ts
2
+ function createSlotRegistry() {
3
+ const items = /* @__PURE__ */ new Map();
4
+ let seq = 0;
5
+ function register(mount) {
6
+ const list = items.get(mount.slot) ?? [];
7
+ list.push({ mount, seq: seq++ });
8
+ items.set(mount.slot, list);
9
+ }
10
+ function registerMany(mounts) {
11
+ for (const m of mounts) register(m);
12
+ }
13
+ function get(slot) {
14
+ const list = items.get(slot) ?? [];
15
+ return [...list].sort((a, b) => (a.mount.order ?? 0) - (b.mount.order ?? 0) || a.seq - b.seq).map((x) => x.mount);
16
+ }
17
+ function clear() {
18
+ items.clear();
19
+ seq = 0;
20
+ }
21
+ return { register, registerMany, get, clear };
22
+ }
23
+ var slotRegistry = createSlotRegistry();
24
+ function registerSlot(mount) {
25
+ slotRegistry.register(mount);
26
+ }
27
+ function getSlotMounts(slot) {
28
+ return slotRegistry.get(slot);
29
+ }
30
+
31
+ export {
32
+ createSlotRegistry,
33
+ slotRegistry,
34
+ registerSlot,
35
+ getSlotMounts
36
+ };
@@ -0,0 +1,146 @@
1
+ export { S as SlotRegistry, c as createSlotRegistry, g as getSlotMounts, r as registerSlot, s as slotRegistry } from './registry-B4Ak1hIR.js';
2
+ import { Backend, SiteConfig, CatalogSource, CommerceBackend, Cart, Order, Money, CurrencyCode, OrderStatus } from '@vitrine-kit/contracts';
3
+
4
+ interface AdapterFactory {
5
+ backend: Backend;
6
+ createCatalog(config: SiteConfig): CatalogSource;
7
+ /** Отсутствует для чисто-каталожных бэкендов. */
8
+ createCommerce?(config: SiteConfig): CommerceBackend;
9
+ }
10
+ interface AdapterRegistry {
11
+ register(factory: AdapterFactory): void;
12
+ resolveCatalog(config: SiteConfig): CatalogSource;
13
+ resolveCommerce(config: SiteConfig): CommerceBackend;
14
+ clear(): void;
15
+ }
16
+ declare function createAdapterRegistry(): AdapterRegistry;
17
+ /** Глобальный реестр адаптеров по умолчанию. */
18
+ declare const adapters: AdapterRegistry;
19
+
20
+ interface OrderPipelineContext {
21
+ cart: Cart;
22
+ /** Заполняется шагами пайплайна (создание заказа). */
23
+ order?: Order;
24
+ /** Произвольные данные шагов (платёж, резерв склада, доставка). */
25
+ meta: Record<string, unknown>;
26
+ }
27
+ type OrderStage<T = OrderPipelineContext> = (ctx: T) => T | Promise<T>;
28
+ /**
29
+ * Последовательно прогоняет контекст через шаги. Любая ошибка шага прерывает
30
+ * пайплайн (откат/идемпотентность — ответственность конкретных шагов в M8).
31
+ */
32
+ declare function runPipeline<T>(ctx: T, stages: ReadonlyArray<OrderStage<T>>): Promise<T>;
33
+
34
+ interface OrderCreationGuard {
35
+ /** Статус корзины (карты Payload `carts`): 'converted' = уже оплачена. */
36
+ cartStatus?: string | null;
37
+ /** Референс платежа текущего события (session/transaction/payment id). */
38
+ providerRef?: string;
39
+ /** paymentRef уже существующих заказов (обычно результат точечного поиска). */
40
+ existingOrderRefs?: ReadonlyArray<string | undefined>;
41
+ }
42
+ /**
43
+ * Создавать ли заказ для этого события. false, если корзина уже converted ИЛИ
44
+ * для этого референса платежа заказ уже есть (повторная доставка/ретрай webhook).
45
+ */
46
+ declare function shouldCreateOrder(guard: OrderCreationGuard): boolean;
47
+
48
+ /** Совпадает с integrations.payments в site.config (контракт 4). */
49
+ type PaymentProviderName = 'stripe' | 'paddle' | 'yookassa';
50
+ interface CreateCheckoutArgs {
51
+ cart: Cart;
52
+ /** Базовый URL витрины; success/cancel строятся относительно него. */
53
+ baseUrl: string;
54
+ /** По умолчанию '/order/success'. */
55
+ successPath?: string;
56
+ /** По умолчанию '/cart'. */
57
+ cancelPath?: string;
58
+ }
59
+ interface PaymentWebhookRequest {
60
+ rawBody: string;
61
+ headers: Record<string, string | null>;
62
+ }
63
+ /** Нормализованное событие провайдера — общий язык для роутов вебхуков. */
64
+ interface NormalizedPaymentEvent {
65
+ kind: 'checkout_completed' | 'payment_failed' | 'unknown';
66
+ /** Из metadata/custom_data/label провайдера. */
67
+ cartId?: string;
68
+ /** Уникальный референс платежа провайдера — ключ идемпотентности. */
69
+ providerRef?: string;
70
+ email?: string;
71
+ /** Исходный объект провайдера (на случай, если роуту нужны доп. поля). */
72
+ raw: unknown;
73
+ }
74
+ interface PaymentProvider {
75
+ name: PaymentProviderName;
76
+ /** Создаёт hosted-checkout у провайдера → URL для редиректа. */
77
+ createCheckout(args: CreateCheckoutArgs): Promise<{
78
+ redirectUrl: string;
79
+ }>;
80
+ /** Верифицирует подпись/подлинность и нормализует событие. Бросает при невалидном. */
81
+ verifyWebhook(req: PaymentWebhookRequest): Promise<NormalizedPaymentEvent>;
82
+ }
83
+
84
+ interface PaymentRegistry {
85
+ register(provider: PaymentProvider): void;
86
+ /** Активный провайдер по site.config.integrations.payments. */
87
+ resolve(config: SiteConfig): PaymentProvider;
88
+ get(name: PaymentProviderName): PaymentProvider | undefined;
89
+ clear(): void;
90
+ }
91
+ declare function createPaymentRegistry(): PaymentRegistry;
92
+ /** Глобальный реестр провайдеров по умолчанию. */
93
+ declare const payments: PaymentRegistry;
94
+
95
+ interface PaymentWebhookHandlers {
96
+ onCheckoutCompleted?(event: NormalizedPaymentEvent): void | Promise<void>;
97
+ onPaymentFailed?(event: NormalizedPaymentEvent): void | Promise<void>;
98
+ }
99
+ interface PaymentWebhookResult {
100
+ received: true;
101
+ kind: NormalizedPaymentEvent['kind'];
102
+ /** Был ли зарегистрирован обработчик для этого вида события. */
103
+ handled: boolean;
104
+ }
105
+ declare function handlePaymentWebhook(args: {
106
+ provider: PaymentProvider;
107
+ req: PaymentWebhookRequest;
108
+ handlers?: PaymentWebhookHandlers;
109
+ }): Promise<PaymentWebhookResult>;
110
+
111
+ declare function computeLineTotal(unitPrice: Money, quantity: number): Money;
112
+ /** Пустая корзина. */
113
+ declare function emptyCart(id: string, currency: CurrencyCode): Cart;
114
+ /** Пересчитывает lineTotal каждой строки и итоги (subtotal/total с учётом скидки). */
115
+ declare function recalcCart(cart: Cart): Cart;
116
+ interface NewLineInput {
117
+ id: string;
118
+ variantId: string;
119
+ productId: string;
120
+ title: string;
121
+ unitPrice: Money;
122
+ quantity: number;
123
+ image?: string;
124
+ }
125
+ /** Добавляет строку; если вариант уже в корзине — увеличивает количество. */
126
+ declare function addCartLine(cart: Cart, input: NewLineInput): Cart;
127
+ /** Меняет количество строки; quantity ≤ 0 удаляет строку. */
128
+ declare function setCartLineQty(cart: Cart, lineId: string, quantity: number): Cart;
129
+ declare function removeCartLine(cart: Cart, lineId: string): Cart;
130
+ /** Суммарное число единиц в корзине (для индикатора в шапке). */
131
+ declare function cartItemCount(cart: Cart): number;
132
+
133
+ interface BuildOrderOptions {
134
+ id: string;
135
+ status?: OrderStatus;
136
+ email?: string;
137
+ number?: string;
138
+ /** ISO-8601; по умолчанию — сейчас. */
139
+ createdAt?: string;
140
+ }
141
+ /** Снимок корзины в заказ (после оплаты). Итоги берём из корзины — не пересчитываем. */
142
+ declare function buildOrderFromCart(cart: Cart, opts: BuildOrderOptions): Order;
143
+
144
+ declare const CORE_VERSION: "0.1.0";
145
+
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 };
package/dist/index.js ADDED
@@ -0,0 +1,210 @@
1
+ import {
2
+ createSlotRegistry,
3
+ getSlotMounts,
4
+ registerSlot,
5
+ slotRegistry
6
+ } from "./chunk-BQVNYHVU.js";
7
+
8
+ // src/adapter/resolver.ts
9
+ function createAdapterRegistry() {
10
+ const factories = /* @__PURE__ */ new Map();
11
+ function register(factory) {
12
+ factories.set(factory.backend, factory);
13
+ }
14
+ function factoryFor(config) {
15
+ const f = factories.get(config.backend);
16
+ if (!f) {
17
+ throw new Error(`[vitrine] \u043D\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D \u0430\u0434\u0430\u043F\u0442\u0435\u0440 \u0434\u043B\u044F backend "${config.backend}"`);
18
+ }
19
+ return f;
20
+ }
21
+ function resolveCatalog(config) {
22
+ return factoryFor(config).createCatalog(config);
23
+ }
24
+ function resolveCommerce(config) {
25
+ if (config.tier === "catalog") {
26
+ throw new Error('[vitrine] CommerceBackend \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D \u043D\u0430 \u0443\u0440\u043E\u0432\u043D\u0435 tier "catalog"');
27
+ }
28
+ const f = factoryFor(config);
29
+ if (!f.createCommerce) {
30
+ throw new Error(`[vitrine] backend "${config.backend}" \u043D\u0435 \u0440\u0435\u0430\u043B\u0438\u0437\u0443\u0435\u0442 CommerceBackend`);
31
+ }
32
+ return f.createCommerce(config);
33
+ }
34
+ function clear() {
35
+ factories.clear();
36
+ }
37
+ return { register, resolveCatalog, resolveCommerce, clear };
38
+ }
39
+ var adapters = createAdapterRegistry();
40
+
41
+ // src/order/pipeline.ts
42
+ async function runPipeline(ctx, stages) {
43
+ let current = ctx;
44
+ for (const stage of stages) {
45
+ current = await stage(current);
46
+ }
47
+ return current;
48
+ }
49
+
50
+ // src/order/idempotency.ts
51
+ function shouldCreateOrder(guard) {
52
+ if (guard.cartStatus === "converted") return false;
53
+ if (guard.providerRef && (guard.existingOrderRefs ?? []).includes(guard.providerRef)) {
54
+ return false;
55
+ }
56
+ return true;
57
+ }
58
+
59
+ // src/payment/registry.ts
60
+ function createPaymentRegistry() {
61
+ const providers = /* @__PURE__ */ new Map();
62
+ function register(provider) {
63
+ providers.set(provider.name, provider);
64
+ }
65
+ function resolve(config) {
66
+ const name = config.integrations.payments;
67
+ if (!name) {
68
+ throw new Error(
69
+ "[vitrine] integrations.payments \u043D\u0435 \u0437\u0430\u0434\u0430\u043D \u0432 site.config \u2014 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u0444\u0438\u0447\u0443 checkout-<provider>"
70
+ );
71
+ }
72
+ const provider = providers.get(name);
73
+ if (!provider) {
74
+ throw new Error(`[vitrine] \u043F\u043B\u0430\u0442\u0451\u0436\u043D\u044B\u0439 \u043F\u0440\u043E\u0432\u0430\u0439\u0434\u0435\u0440 "${name}" \u043D\u0435 \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043E\u0432\u0430\u043D`);
75
+ }
76
+ return provider;
77
+ }
78
+ function get(name) {
79
+ return providers.get(name);
80
+ }
81
+ function clear() {
82
+ providers.clear();
83
+ }
84
+ return { register, resolve, get, clear };
85
+ }
86
+ var payments = createPaymentRegistry();
87
+
88
+ // src/payment/webhook.ts
89
+ async function handlePaymentWebhook(args) {
90
+ const event = await args.provider.verifyWebhook(args.req);
91
+ const handlers = args.handlers ?? {};
92
+ let handled = false;
93
+ switch (event.kind) {
94
+ case "checkout_completed":
95
+ if (handlers.onCheckoutCompleted) {
96
+ await handlers.onCheckoutCompleted(event);
97
+ handled = true;
98
+ }
99
+ break;
100
+ case "payment_failed":
101
+ if (handlers.onPaymentFailed) {
102
+ await handlers.onPaymentFailed(event);
103
+ handled = true;
104
+ }
105
+ break;
106
+ }
107
+ return { received: true, kind: event.kind, handled };
108
+ }
109
+
110
+ // src/commerce/cart.ts
111
+ function computeLineTotal(unitPrice, quantity) {
112
+ return unitPrice * quantity;
113
+ }
114
+ function emptyCart(id, currency) {
115
+ return { id, lines: [], currency, subtotal: 0, total: 0 };
116
+ }
117
+ function recalcCart(cart) {
118
+ const lines = cart.lines.map((l) => ({ ...l, lineTotal: computeLineTotal(l.unitPrice, l.quantity) }));
119
+ const subtotal = lines.reduce((sum, l) => sum + l.lineTotal, 0);
120
+ const discountTotal = cart.discountTotal ?? 0;
121
+ const total = Math.max(0, subtotal - discountTotal);
122
+ return { ...cart, lines, subtotal, total };
123
+ }
124
+ function addCartLine(cart, input) {
125
+ if (!Number.isInteger(input.quantity) || input.quantity <= 0) {
126
+ throw new Error(`[vitrine] \u043D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0441\u0442\u0440\u043E\u043A\u0438: ${input.quantity} (\u043D\u0443\u0436\u043D\u043E \u0446\u0435\u043B\u043E\u0435 > 0)`);
127
+ }
128
+ const exists = cart.lines.some((l) => l.variantId === input.variantId);
129
+ const lines = exists ? cart.lines.map(
130
+ (l) => l.variantId === input.variantId ? { ...l, quantity: l.quantity + input.quantity } : l
131
+ ) : [
132
+ ...cart.lines,
133
+ {
134
+ id: input.id,
135
+ variantId: input.variantId,
136
+ productId: input.productId,
137
+ title: input.title,
138
+ quantity: input.quantity,
139
+ unitPrice: input.unitPrice,
140
+ lineTotal: computeLineTotal(input.unitPrice, input.quantity),
141
+ image: input.image
142
+ }
143
+ ];
144
+ return recalcCart({ ...cart, lines });
145
+ }
146
+ function setCartLineQty(cart, lineId, quantity) {
147
+ if (!Number.isInteger(quantity)) {
148
+ throw new Error(`[vitrine] \u043D\u0435\u0434\u043E\u043F\u0443\u0441\u0442\u0438\u043C\u043E\u0435 \u043A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E: ${quantity} (\u043D\u0443\u0436\u043D\u043E \u0446\u0435\u043B\u043E\u0435)`);
149
+ }
150
+ if (quantity <= 0) return removeCartLine(cart, lineId);
151
+ return recalcCart({
152
+ ...cart,
153
+ lines: cart.lines.map((l) => l.id === lineId ? { ...l, quantity } : l)
154
+ });
155
+ }
156
+ function removeCartLine(cart, lineId) {
157
+ return recalcCart({ ...cart, lines: cart.lines.filter((l) => l.id !== lineId) });
158
+ }
159
+ function cartItemCount(cart) {
160
+ return cart.lines.reduce((n, l) => n + l.quantity, 0);
161
+ }
162
+
163
+ // src/commerce/order.ts
164
+ function buildOrderFromCart(cart, opts) {
165
+ const lines = cart.lines.map((l) => ({
166
+ variantId: l.variantId,
167
+ productId: l.productId,
168
+ title: l.title,
169
+ quantity: l.quantity,
170
+ unitPrice: l.unitPrice,
171
+ lineTotal: l.lineTotal
172
+ }));
173
+ return {
174
+ id: opts.id,
175
+ number: opts.number,
176
+ status: opts.status ?? "paid",
177
+ lines,
178
+ currency: cart.currency,
179
+ subtotal: cart.subtotal,
180
+ discountTotal: cart.discountTotal,
181
+ total: cart.total,
182
+ email: opts.email,
183
+ createdAt: opts.createdAt ?? (/* @__PURE__ */ new Date()).toISOString()
184
+ };
185
+ }
186
+
187
+ // src/index.ts
188
+ var CORE_VERSION = "0.1.0";
189
+ export {
190
+ CORE_VERSION,
191
+ adapters,
192
+ addCartLine,
193
+ buildOrderFromCart,
194
+ cartItemCount,
195
+ computeLineTotal,
196
+ createAdapterRegistry,
197
+ createPaymentRegistry,
198
+ createSlotRegistry,
199
+ emptyCart,
200
+ getSlotMounts,
201
+ handlePaymentWebhook,
202
+ payments,
203
+ recalcCart,
204
+ registerSlot,
205
+ removeCartLine,
206
+ runPipeline,
207
+ setCartLineQty,
208
+ shouldCreateOrder,
209
+ slotRegistry
210
+ };
@@ -0,0 +1,17 @@
1
+ import { ComponentType, ReactNode } from 'react';
2
+ import { SlotId } from '@vitrine-kit/contracts';
3
+ import { S as SlotRegistry } from './registry-B4Ak1hIR.js';
4
+
5
+ type SlotComponentRegistry = SlotRegistry<ComponentType<Record<string, unknown>>>;
6
+ interface SlotProps {
7
+ name: SlotId;
8
+ /** Реестр (по умолчанию — глобальный). Для тестов/изоляции можно передать свой. */
9
+ registry?: SlotComponentRegistry;
10
+ /** Что рендерить, если слот пуст. */
11
+ fallback?: ReactNode;
12
+ /** Остальные пропсы прокидываются в каждый смонтированный компонент. */
13
+ [prop: string]: unknown;
14
+ }
15
+ declare function Slot(props: SlotProps): ReactNode;
16
+
17
+ export { Slot, type SlotProps };
package/dist/react.js ADDED
@@ -0,0 +1,23 @@
1
+ import {
2
+ slotRegistry
3
+ } from "./chunk-BQVNYHVU.js";
4
+
5
+ // src/react.ts
6
+ import {
7
+ createElement,
8
+ Fragment
9
+ } from "react";
10
+ function Slot(props) {
11
+ const { name, registry, fallback = null, ...rest } = props;
12
+ const reg = registry ?? slotRegistry;
13
+ const mounts = reg.get(name);
14
+ if (mounts.length === 0) return fallback;
15
+ return createElement(
16
+ Fragment,
17
+ null,
18
+ ...mounts.map((m, i) => createElement(m.component, { key: i, ...rest }))
19
+ );
20
+ }
21
+ export {
22
+ Slot
23
+ };
@@ -0,0 +1,16 @@
1
+ import { SlotMount, SlotId } from '@vitrine-kit/contracts';
2
+
3
+ interface SlotRegistry<C = unknown> {
4
+ register(mount: SlotMount<C>): void;
5
+ registerMany(mounts: ReadonlyArray<SlotMount<C>>): void;
6
+ /** Привязки слота, отсортированные по order (стабильно по порядку регистрации). */
7
+ get(slot: SlotId): SlotMount<C>[];
8
+ clear(): void;
9
+ }
10
+ declare function createSlotRegistry<C = unknown>(): SlotRegistry<C>;
11
+ /** Глобальный реестр по умолчанию (клиент регистрирует слоты в lib/slots.ts). */
12
+ declare const slotRegistry: SlotRegistry;
13
+ declare function registerSlot<C = unknown>(mount: SlotMount<C>): void;
14
+ declare function getSlotMounts<C = unknown>(slot: SlotId): SlotMount<C>[];
15
+
16
+ export { type SlotRegistry as S, createSlotRegistry as c, getSlotMounts as g, registerSlot as r, slotRegistry as s };
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@vitrine-kit/core",
3
+ "version": "0.2.0",
4
+ "description": "Vitrine core — runtime слотов/адаптера, order pipeline, Stripe webhook. Критическая логика (баг = инцидент у всех).",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "types": "./dist/index.d.ts",
8
+ "main": "./dist/index.js",
9
+ "module": "./dist/index.js",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js"
14
+ },
15
+ "./react": {
16
+ "types": "./dist/react.d.ts",
17
+ "import": "./dist/react.js"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "publishConfig": {
24
+ "access": "public"
25
+ },
26
+ "repository": {
27
+ "type": "git",
28
+ "url": "git+https://github.com/vitrine-kit/vitrine.git",
29
+ "directory": "packages/core"
30
+ },
31
+ "dependencies": {
32
+ "@vitrine-kit/contracts": "1.1.0"
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=18.0.0"
36
+ },
37
+ "peerDependenciesMeta": {
38
+ "react": {
39
+ "optional": true
40
+ }
41
+ },
42
+ "devDependencies": {
43
+ "@types/react": "^18.3.12",
44
+ "react": "^18.3.1"
45
+ },
46
+ "scripts": {
47
+ "build": "tsup src/index.ts src/react.ts --format esm --dts --clean",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run --passWithNoTests"
50
+ }
51
+ }