@wocart/theme 0.0.0-stage → 1.1.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.
@@ -0,0 +1,479 @@
1
+ /**
2
+ * The `@wocart/theme` contract — every type in one place.
3
+ *
4
+ * Two sides, one seam:
5
+ * - code-owned: `ThemeDefinition` / `ComponentDefinition` (never serialized);
6
+ * - data-owned: `ComponentInstance` / `PageData` (always JSON).
7
+ */
8
+ import type { StoreClient } from "@wocart/storefront/server";
9
+ import type { StoreConfig } from "@wocart/storefront";
10
+ /** Every value page data may hold. */
11
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
12
+ [key: string]: JsonValue;
13
+ };
14
+ /** A JSON object, as stored in `ComponentInstance.props`. */
15
+ export type JsonObject = {
16
+ [key: string]: JsonValue;
17
+ };
18
+ /**
19
+ * A BCP 47 language tag in canonical case: `ar`, `fr`, `en`, `ar-DZ`, `zgh`.
20
+ * A plain string type on purpose: the set of languages is platform data, never
21
+ * a closed enum baked into the contract.
22
+ */
23
+ export type LocaleCode = string;
24
+ /**
25
+ * Text that is the same in every language (a plain string), or one value per
26
+ * language. Used for translatable field values, translatable text defaults, and
27
+ * editor-facing labels.
28
+ */
29
+ export type LocalizedText = string | Readonly<Record<LocaleCode, string>>;
30
+ /**
31
+ * What resolution needs besides the instance: the theme's content pack for
32
+ * the language being rendered, and the language preference — the requested
33
+ * language first, then the store's fallback chain.
34
+ */
35
+ export interface ResolutionScope {
36
+ content?: Record<string, string>;
37
+ locales?: readonly LocaleCode[];
38
+ }
39
+ /** The field kinds. `color` and `menu` were added after V1, additively. */
40
+ export type FieldKind = "text" | "textarea" | "number" | "boolean" | "select" | "url" | "color" | "menu";
41
+ interface FieldDefinitionBase {
42
+ /** The exact prop name the component expects. */
43
+ key: string;
44
+ /** Editor-facing name, the same in every language or translated. */
45
+ label: LocalizedText;
46
+ /** Editor-facing help text. */
47
+ description?: LocalizedText;
48
+ required?: boolean;
49
+ }
50
+ /**
51
+ * `text` / `textarea`: a string that defaults from the content pack
52
+ * (`contentKey`) or from a literal (`default`) — mutually exclusive.
53
+ */
54
+ export interface TextFieldDefinition extends FieldDefinitionBase {
55
+ kind: "text" | "textarea";
56
+ /**
57
+ * The key in the **theme's own** content pack that supplies this field's
58
+ * creation-time value. The SDK never models the pack: it reads
59
+ * `ThemeRenderContext.content` — an opaque flat string map — and falls
60
+ * through to `default` → omit when the key is absent. Pass the pack's key set
61
+ * to `validateThemeDefinition` to have typos reported.
62
+ */
63
+ contentKey?: string;
64
+ /**
65
+ * Literal creation-time default (mutually exclusive with `contentKey`), the
66
+ * same in every language or translated. Prefer `contentKey` for theme copy:
67
+ * the pack is already per language.
68
+ */
69
+ default?: LocalizedText;
70
+ }
71
+ /** `number`: a finite number, optionally bounded. */
72
+ export interface NumberFieldDefinition extends FieldDefinitionBase {
73
+ kind: "number";
74
+ default?: number;
75
+ min?: number;
76
+ max?: number;
77
+ }
78
+ /** `boolean`. */
79
+ export interface BooleanFieldDefinition extends FieldDefinitionBase {
80
+ kind: "boolean";
81
+ default?: boolean;
82
+ }
83
+ /** `select`: one of a closed option list. */
84
+ export interface SelectFieldDefinition extends FieldDefinitionBase {
85
+ kind: "select";
86
+ options: readonly string[];
87
+ default?: string;
88
+ }
89
+ /**
90
+ * `url`: a link target an editor may set, safe to render as an `href`. Only a
91
+ * site path (`/products`), a fragment (`#best-sellers`), a query (`?page=2`),
92
+ * `http(s)://`, `mailto:` or `tel:` is accepted. Anything else, including
93
+ * `javascript:`, `data:`, protocol-relative `//host` and backslash tricks, is
94
+ * rejected at assembly and reported as `invalid-prop` on read.
95
+ */
96
+ export interface UrlFieldDefinition extends FieldDefinitionBase {
97
+ kind: "url";
98
+ default?: string;
99
+ }
100
+ /** `color`: a hex color, `#rgb` or `#rrggbb`. Not translatable. */
101
+ export interface ColorFieldDefinition extends FieldDefinitionBase {
102
+ kind: "color";
103
+ default?: string;
104
+ }
105
+ /**
106
+ * `menu`: which of the store's menus to show here — Shopify's `link_list`.
107
+ * The value is a menu handle (`main-menu`); the theme reads the menu with
108
+ * the storefront client's `menus.get(handle)`. A theme never assumes a menu
109
+ * exists: an unknown handle reads as no menu.
110
+ */
111
+ export interface MenuFieldDefinition extends FieldDefinitionBase {
112
+ kind: "menu";
113
+ default?: string;
114
+ }
115
+ export type FieldDefinition = TextFieldDefinition | NumberFieldDefinition | BooleanFieldDefinition | SelectFieldDefinition | UrlFieldDefinition | ColorFieldDefinition | MenuFieldDefinition;
116
+ /**
117
+ * One composition point a component declares (`"default"` = children).
118
+ *
119
+ * **Renderer ceiling (V1):** the reference renderer (`@wocart/theme/astro`)
120
+ * fills **at most one named slot** per instance. The Astro compiler lifts the
121
+ * first slot-attribute expression of a dynamic child expression into the
122
+ * slots-object key, evaluated at component scope, so per-item slot names inside
123
+ * a `.map()` are not expressible. Any further named slot **with content** is
124
+ * reported as `multiple-slots-unsupported` — never silently dropped.
125
+ *
126
+ * Declaring several named slots is legal; it is a capability declaration, not a
127
+ * promise that all of them can be filled at once. A component that needs more
128
+ * than one filled can transfer Astro's own slots inside itself
129
+ * (`<slot name="x" slot="x" />`) — a component-level decision, deliberately not
130
+ * part of this contract.
131
+ */
132
+ export interface SlotDefinition {
133
+ /** `"default"` or a lowercase kebab-case name. */
134
+ name: string;
135
+ /** False = at most one child instance. Defaults to true. */
136
+ multiple?: boolean;
137
+ /**
138
+ * The component types this slot accepts (catalog keys). Absent = any type.
139
+ * Assembly rejects a type that is not in the catalog; the reader drops a
140
+ * child of any other type with a `disallowed-child` issue. Narrowing a list
141
+ * after documents are saved hides their children, so treat it like removing
142
+ * a slot.
143
+ */
144
+ allow?: readonly string[];
145
+ }
146
+ /**
147
+ * What a component permits.
148
+ *
149
+ * V1 carries one flag, and it is **advisory**: `insertable` is metadata for an
150
+ * editor/CLI's insertion palette. Neither `parsePageData` nor `resolveInstance`
151
+ * enforces it — a saved document containing a non-insertable component still
152
+ * renders, and the renderer reports only what the document tells it. The render
153
+ * path never second-guesses a document that was already saved. Enforcement, when
154
+ * it exists, belongs at *insertion* time in the writer, not at render.
155
+ */
156
+ export interface ComponentCapabilities {
157
+ insertable?: boolean;
158
+ }
159
+ /** The request facts available while rendering (formalises `Astro.locals.store` + URL). */
160
+ export interface ThemeRenderContext {
161
+ store: StoreConfig;
162
+ /**
163
+ * The theme's resolved content pack: an opaque, flat string map with store
164
+ * overrides already merged. The SDK reads it (through a field's `contentKey`)
165
+ * and never shapes it — the taxonomy belongs to the theme, and the theme owns
166
+ * how its components consume the pack.
167
+ */
168
+ content: Record<string, string>;
169
+ /**
170
+ * The language being rendered (BCP 47). Translatable field values resolve
171
+ * to it first; `content` is the pack for it.
172
+ */
173
+ locale: LocaleCode;
174
+ /**
175
+ * The store's fallback chain after `locale`, in order (e.g. the store's
176
+ * primary language, then the platform default). A translatable value with
177
+ * no entry for `locale` tries these before the pack and the default.
178
+ */
179
+ fallbackLocales?: readonly LocaleCode[];
180
+ isRTL: boolean;
181
+ /** Request pathname. */
182
+ url: string;
183
+ /**
184
+ * The route's dynamic segments (`Astro.params`), e.g. `{ slug: "chair" }` on
185
+ * `/products/[slug]`. A section on a templated surface reads its route
186
+ * resource through these, with a `reads` key that includes them, instead of
187
+ * parsing `url`.
188
+ */
189
+ params?: Readonly<Record<string, string | undefined>>;
190
+ }
191
+ /** Per-render read sharing; the renderer provides the implementation. */
192
+ export interface ReadCache {
193
+ get<T>(key: string, load: () => T | Promise<T>): Promise<T>;
194
+ }
195
+ /** What a `load` hook receives. Server-only; reads go through the storefront SDK. */
196
+ export interface LoadContext {
197
+ storefront: StoreClient;
198
+ context: ThemeRenderContext;
199
+ /**
200
+ * The resolved field values for this instance.
201
+ *
202
+ * The renderer merges `load`'s return value **over** these
203
+ * (`{ ...props, ...loaded }`), so a load-returned key wins over a field of
204
+ * the same name. A definition whose component reads a prop from `load` should
205
+ * keep that name out of `fields`.
206
+ */
207
+ props: Record<string, unknown>;
208
+ reads: ReadCache;
209
+ }
210
+ /** Server-side data fetch for one component definition. */
211
+ export type LoadFn = (context: LoadContext) => Promise<Record<string, unknown>>;
212
+ /**
213
+ * Code-owned description of one reusable component. `component` is the real
214
+ * component reference — opaque here so the core stays framework-neutral; the
215
+ * `./astro` renderer narrows it.
216
+ */
217
+ export interface ComponentDefinition {
218
+ /** Stable type identity: lowercase kebab-case, unique in the catalog. */
219
+ type: string;
220
+ component: unknown;
221
+ label: LocalizedText;
222
+ /** The editor palette heading this component is listed under. */
223
+ group?: LocalizedText;
224
+ fields?: readonly FieldDefinition[];
225
+ slots?: readonly SlotDefinition[];
226
+ load?: LoadFn;
227
+ capabilities?: ComponentCapabilities;
228
+ }
229
+ /** The value type one field resolves to. A select narrows to its option literals. */
230
+ export type FieldValue<F extends FieldDefinition> = F extends {
231
+ kind: "number";
232
+ } ? number : F extends {
233
+ kind: "boolean";
234
+ } ? boolean : F extends {
235
+ kind: "select";
236
+ options: readonly (infer Option)[];
237
+ } ? Option : string;
238
+ type FieldsOf<D> = D extends {
239
+ fields: readonly (infer F extends FieldDefinition)[];
240
+ } ? F : never;
241
+ type FieldProps<D> = {
242
+ [F in FieldsOf<D> as F extends {
243
+ default: unknown;
244
+ } ? F["key"] : never]: FieldValue<F>;
245
+ } & {
246
+ [F in FieldsOf<D> as F extends {
247
+ default: unknown;
248
+ } ? never : F["key"]]?: FieldValue<F>;
249
+ };
250
+ type LoadedProps<D> = D extends {
251
+ load: (context: LoadContext) => Promise<infer L>;
252
+ } ? L : {};
253
+ /**
254
+ * The props `InstanceView` passes to a definition's component, derived from
255
+ * the definition so the component and its fields cannot drift apart:
256
+ *
257
+ * ```ts
258
+ * type Props = InferProps<typeof heroDefinition>;
259
+ * ```
260
+ *
261
+ * A field with a `default` is always present; any other field may be absent
262
+ * (no value, no content, or reported as invalid). Whatever `load` returns is
263
+ * merged over the fields, as the renderer does at runtime. Use `defineComponent`
264
+ * so field keys, kinds and select options keep their literal types.
265
+ */
266
+ export type InferProps<D extends ComponentDefinition> = Omit<FieldProps<D>, keyof LoadedProps<D>> & LoadedProps<D>;
267
+ /** The catalog a theme ships — the unit the renderer and validator consume. */
268
+ export interface ThemeDefinition {
269
+ key: string;
270
+ version: string;
271
+ /**
272
+ * The surfaces this theme can render, declared as data. Optional and
273
+ * additive: a theme that declares none is treated as declaring none, and no
274
+ * existing page-document behavior changes — `parsePageData` /
275
+ * `validatePageData` never consult this list. Surface *enforcement* (rejecting
276
+ * a document whose surface the theme does not serve) is a later, separately
277
+ * tested concern; the manifest carries the declaration now so an editor can
278
+ * offer only valid surfaces.
279
+ */
280
+ surfaces?: readonly SurfaceSpec[];
281
+ /**
282
+ * The languages this theme's content packs cover (BCP 47), e.g.
283
+ * `["ar", "fr", "en"]`. Declared, the validator requires every translated
284
+ * label to cover all of them, and the manifest tells an editor which
285
+ * languages the theme can serve. Absent means "not declared".
286
+ */
287
+ locales?: readonly LocaleCode[];
288
+ /**
289
+ * The theme's global settings: what a merchant may change about the whole
290
+ * theme (its font, its colors, which menu the header shows), declared with
291
+ * the same fields sections use, in named groups. Optional and additive. A
292
+ * store's values are read with `resolveThemeSettings`; a writer checks them
293
+ * with `checkThemeSettings`. Setting keys are unique across all groups.
294
+ */
295
+ settings?: readonly SettingsGroup[];
296
+ components: Record<string, ComponentDefinition>;
297
+ }
298
+ /** One group of theme settings, as an editor lists them ("Typography", "Header"). */
299
+ export interface SettingsGroup {
300
+ /** Lowercase kebab-case, unique in the theme. */
301
+ key: string;
302
+ label: LocalizedText;
303
+ fields: readonly FieldDefinition[];
304
+ }
305
+ /**
306
+ * One surface a theme declares it can render.
307
+ *
308
+ * `key` uses the same surface-key pattern as the page-document envelope
309
+ * (`SURFACE_KEY_PATTERN` in `schemas.ts`); `label` is the editor-facing name.
310
+ */
311
+ export interface SurfaceSpec {
312
+ key: string;
313
+ label: LocalizedText;
314
+ }
315
+ /**
316
+ * One component as data: a `ComponentDefinition` minus its runtime-only
317
+ * implementation. `component` (the live component reference) and `load` (the
318
+ * server-only data hook) are intentionally absent — neither is
319
+ * JSON-serializable and neither belongs in a tooling contract.
320
+ *
321
+ * `fields` / `slots` are always present (normalized from absent to `[]` by the
322
+ * generator) so consumers never branch on "missing vs empty".
323
+ */
324
+ export interface ComponentSpec {
325
+ /** Stable type identity: lowercase kebab-case, equal to the catalog key. */
326
+ type: string;
327
+ /** Editor-facing name. */
328
+ label: LocalizedText;
329
+ /** The editor palette heading this component is listed under. */
330
+ group?: LocalizedText;
331
+ fields: readonly FieldDefinition[];
332
+ slots: readonly SlotDefinition[];
333
+ capabilities?: ComponentCapabilities;
334
+ }
335
+ /**
336
+ * The serializable theme contract.
337
+ *
338
+ * - `contractVersion` identifies this manifest's schema and interpretation
339
+ * contract (currently `1`). It versions the *format*, not the theme.
340
+ * - `key` / `version` identify the *theme* (same values as `ThemeDefinition`).
341
+ * - `surfaces` declares what the theme can render (possibly empty).
342
+ * - `components` is the data-only catalogue, keyed by component type.
343
+ *
344
+ * Three versioned contracts coexist and must not be confused:
345
+ * `contractVersion` (this manifest), `ThemeDefinition.version` (the theme's
346
+ * semver), and `PageData.version` (the page-document envelope, currently `1`).
347
+ */
348
+ export interface ThemeManifest {
349
+ contractVersion: 1;
350
+ key: string;
351
+ version: string;
352
+ surfaces: readonly SurfaceSpec[];
353
+ /** The languages the theme's packs cover (possibly none declared). */
354
+ locales: readonly LocaleCode[];
355
+ /** The theme's global settings (possibly none). */
356
+ settings: readonly SettingsGroup[];
357
+ components: Record<string, ComponentSpec>;
358
+ }
359
+ export type ThemeManifestIssueCode = "invalid-manifest" | "unsupported-contract" | "invalid-identity" | "invalid-version" | "invalid-surface" | "invalid-settings" | "invalid-component" | "type-mismatch" | "non-serializable";
360
+ /** One reported manifest problem; `path` points into the manifest (e.g. `components.hero.fields[0].kind`). */
361
+ export interface ThemeManifestIssue {
362
+ path: string;
363
+ code: ThemeManifestIssueCode;
364
+ message: string;
365
+ }
366
+ /**
367
+ * Result of the manifest boundary. `manifest` is non-null only when the input
368
+ * is structurally sound: structural failures (wrong shape, unsupported
369
+ * contract, malformed components, non-serializable values) yield `null` plus
370
+ * issues. The one advisory check — a non-semver `version` — returns the
371
+ * manifest *with* an `invalid-version` issue, so a version-format concern is
372
+ * never confused with a fatal structural failure.
373
+ */
374
+ export interface ThemeManifestResult {
375
+ manifest: ThemeManifest | null;
376
+ issues: ThemeManifestIssue[];
377
+ }
378
+ /** One occurrence of a component on one page (JSON only). */
379
+ export interface ComponentInstance {
380
+ id: string;
381
+ type: string;
382
+ props?: JsonObject;
383
+ children?: ComponentInstance[];
384
+ slots?: Record<string, ComponentInstance[]>;
385
+ }
386
+ /**
387
+ * One composed surface's persisted document.
388
+ *
389
+ * At runtime the envelope is **forward-compatible**: `parsePageData` and
390
+ * `PageDataSchema` carry unknown *top-level* keys through verbatim, so a later
391
+ * writer can record metadata (catalog provenance is the intended first use)
392
+ * without a `PageData.version` bump. Those keys are never read by the renderer,
393
+ * which only walks `sections`, and they are deliberately **not declared here**:
394
+ * an index signature would turn a `page.sectons` typo into `unknown` instead of
395
+ * a compile error. The field is added when a real reader exists.
396
+ *
397
+ * Unknown keys *inside* an instance are still stripped — the envelope is
398
+ * extensible, the instance is not.
399
+ */
400
+ export interface PageData {
401
+ version: 1;
402
+ surface: string;
403
+ sections: ComponentInstance[];
404
+ }
405
+ export type PageDataIssueCode = "envelope-invalid" | "instance-dropped" | "duplicate-id" | "unknown-type" | "unknown-prop" | "invalid-prop" | "missing-required-prop" | "undeclared-children" | "unknown-slot" | "slot-not-multiple" | "disallowed-child";
406
+ /** One reported degradation; `path` points into the document (e.g. `sections[0].props.limit`). */
407
+ export interface PageDataIssue {
408
+ path: string;
409
+ code: PageDataIssueCode;
410
+ message: string;
411
+ }
412
+ /** Result of a read boundary: `page` is null only when the envelope itself failed. */
413
+ export interface PageDataResult {
414
+ page: PageData | null;
415
+ issues: PageDataIssue[];
416
+ }
417
+ /** What the renderer passes to the real component, after gating. */
418
+ export interface ResolvedInstance {
419
+ props: Record<string, unknown>;
420
+ children: ComponentInstance[];
421
+ slots: Record<string, ComponentInstance[]>;
422
+ issues: PageDataIssue[];
423
+ }
424
+ export type ThemeDefinitionIssueCode = "invalid-theme" | "invalid-settings" | "invalid-definition" | "invalid-type" | "type-mismatch" | "invalid-field" | "duplicate-field-key" | "unknown-content-key" | "invalid-slot" | "duplicate-slot" | "invalid-capability" | "invalid-load";
425
+ export interface ThemeDefinitionIssue {
426
+ path: string;
427
+ code: ThemeDefinitionIssueCode;
428
+ message: string;
429
+ }
430
+ /**
431
+ * Optional inputs to `validateThemeDefinition`. Every field is additive: with
432
+ * no options, the validator behaves exactly as it always has.
433
+ *
434
+ * `contentKeys` is the one cross-boundary check the Theme SDK can make and a
435
+ * theme cannot — it defines `contentKey` *and* reads the resolved content
436
+ * record, so a typo is invisible everywhere else. The SDK deliberately does not
437
+ * model content packs; it only compares a field's `contentKey` against the key
438
+ * set the theme supplies. Passing an **empty** array is meaningful: it enables
439
+ * the check and reports every declared key.
440
+ */
441
+ export interface ThemeValidationOptions {
442
+ contentKeys?: readonly string[];
443
+ }
444
+ export type ThemeSettingsIssueCode = "invalid-settings" | "unknown-setting" | "invalid-setting" | "missing-required-setting";
445
+ /** One reported problem with a store's theme settings; `path` is `settings.<key>`. */
446
+ export interface ThemeSettingsIssue {
447
+ path: string;
448
+ code: ThemeSettingsIssueCode;
449
+ message: string;
450
+ }
451
+ /** The effective settings: every value checked, the rest from the theme's defaults. */
452
+ export interface ThemeSettingsResult {
453
+ values: Record<string, unknown>;
454
+ issues: ThemeSettingsIssue[];
455
+ }
456
+ type SettingsFieldsOf<T> = T extends {
457
+ settings: readonly (infer G)[];
458
+ } ? G extends {
459
+ fields: readonly (infer F extends FieldDefinition)[];
460
+ } ? F : never : never;
461
+ /**
462
+ * The effective settings of a theme, typed from its declaration: a setting
463
+ * with a `default` is always present, any other may be absent.
464
+ *
465
+ * ```ts
466
+ * const { values } = resolveThemeSettings(theme, stored);
467
+ * const settings = values as InferSettings<typeof theme>;
468
+ * ```
469
+ */
470
+ export type InferSettings<T extends ThemeDefinition> = {
471
+ [F in SettingsFieldsOf<T> as F extends {
472
+ default: unknown;
473
+ } ? F["key"] : never]: FieldValue<F>;
474
+ } & {
475
+ [F in SettingsFieldsOf<T> as F extends {
476
+ default: unknown;
477
+ } ? never : F["key"]]?: FieldValue<F>;
478
+ };
479
+ export {};
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json CHANGED
@@ -1,6 +1,82 @@
1
1
  {
2
2
  "name": "@wocart/theme",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.0",
4
+ "description": "The Wocart theme contract: code-owned component definitions, JSON page documents, validation and resolution, and a reference Astro renderer.",
5
+ "keywords": [
6
+ "wocart",
7
+ "theme",
8
+ "storefront",
9
+ "ecommerce",
10
+ "page-builder",
11
+ "astro",
12
+ "astro-component",
13
+ "withastro",
14
+ "typescript"
15
+ ],
16
+ "license": "Apache-2.0",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/bighadj22/codflow.git",
20
+ "directory": "wocart-sdk/theme"
21
+ },
22
+ "homepage": "https://github.com/bighadj22/codflow/tree/main/wocart-sdk/theme#readme",
23
+ "bugs": "https://github.com/bighadj22/codflow/issues",
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "type": "module",
28
+ "sideEffects": false,
29
+ "main": "./dist/index.js",
30
+ "types": "./dist/index.d.ts",
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "default": "./dist/index.js"
35
+ },
36
+ "./astro": {
37
+ "types": "./dist/astro/index.d.ts",
38
+ "default": "./dist/astro/index.js"
39
+ },
40
+ "./astro/InstanceView.astro": "./dist/astro/InstanceView.astro",
41
+ "./package.json": "./package.json"
42
+ },
43
+ "files": [
44
+ "dist",
45
+ "README.md",
46
+ "CHANGELOG.md",
47
+ "LICENSE",
48
+ "NOTICE"
49
+ ],
50
+ "scripts": {
51
+ "build": "node --eval \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc && node scripts/copy-astro.mjs",
52
+ "typecheck": "tsc --noEmit && tsc --project tsconfig.test.json && astro check",
53
+ "test": "npm run build && vitest run",
54
+ "test:watch": "vitest",
55
+ "verify:package": "npm run build && node scripts/check-package.mjs && node scripts/verify-package.mjs",
56
+ "prepack": "npm run build",
57
+ "prepublishOnly": "node scripts/check-package.mjs"
58
+ },
59
+ "engines": {
60
+ "node": ">=22.12.0"
61
+ },
62
+ "dependencies": {
63
+ "zod": "^4.4.3"
64
+ },
65
+ "peerDependencies": {
66
+ "@wocart/storefront": "^1.0.0",
67
+ "astro": "^7.0.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "astro": {
71
+ "optional": true
72
+ }
73
+ },
74
+ "devDependencies": {
75
+ "@wocart/storefront": "^1.0.0",
76
+ "@types/node": "^26.2.0",
77
+ "astro": "^7.3.2",
78
+ "fast-check": "^4.6.0",
79
+ "typescript": "^7.0.2",
80
+ "vitest": "^5.0.0"
81
+ }
82
+ }