@sovecom/theme-sdk 1.0.0-rc.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/LICENSE +21 -0
- package/README.md +59 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +75 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.d.ts +67 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +174 -0
- package/dist/manifest.js.map +1 -0
- package/dist/settings.d.ts +84 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +23 -0
- package/dist/settings.js.map +1 -0
- package/dist/slots.d.ts +9 -0
- package/dist/slots.d.ts.map +1 -0
- package/dist/slots.js +40 -0
- package/dist/slots.js.map +1 -0
- package/dist/store-contract.d.ts +35 -0
- package/dist/store-contract.d.ts.map +1 -0
- package/dist/store-contract.js +3 -0
- package/dist/store-contract.js.map +1 -0
- package/dist/template.d.ts +119 -0
- package/dist/template.d.ts.map +1 -0
- package/dist/template.js +169 -0
- package/dist/template.js.map +1 -0
- package/dist/theme.d.ts +35 -0
- package/dist/theme.d.ts.map +1 -0
- package/dist/theme.js +31 -0
- package/dist/theme.js.map +1 -0
- package/dist/widget.d.ts +200 -0
- package/dist/widget.d.ts.map +1 -0
- package/dist/widget.js +232 -0
- package/dist/widget.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the STORE-facing contract types the storefront will
|
|
3
|
+
* consume. These describe the two tiny, already-shipped store endpoints a theme touches — there is
|
|
4
|
+
* no other runtime contract:
|
|
5
|
+
* - `GET /store/v1/theme` → `ActiveTheme` (`{ name, version, settings }`);
|
|
6
|
+
* - `GET /store/v1/slots` → `SlotMap` (`Record<slot, { module, component }>`, conflicts omitted).
|
|
7
|
+
*
|
|
8
|
+
* A CI type-conformance guard in apps/api asserts the in-tree view types (`ActiveThemeView` in
|
|
9
|
+
* `themes.service.ts`, the store slot map in `slots.controller.store.ts`) stay assignable to these
|
|
10
|
+
* exported types — so the seam is guarded at compile time, not by hope.
|
|
11
|
+
*/
|
|
12
|
+
import type { ThemeSettings } from './settings.js';
|
|
13
|
+
/**
|
|
14
|
+
* The public store view of the active theme — exactly what `GET /store/v1/theme` returns and what
|
|
15
|
+
* the storefront reads to render. Name + version + the (opaque) settings record only.
|
|
16
|
+
*/
|
|
17
|
+
export interface ActiveTheme {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly version: string;
|
|
20
|
+
readonly settings: ThemeSettings;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* A single cleanly-resolved slot binding: the module that fills the slot and the component id the
|
|
24
|
+
* storefront maps to that module's UI.
|
|
25
|
+
*/
|
|
26
|
+
export interface SlotBinding {
|
|
27
|
+
readonly module: string;
|
|
28
|
+
readonly component: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The public slot map — exactly what `GET /store/v1/slots` returns: `slot → { module, component }`
|
|
32
|
+
* for cleanly-resolved slots only (conflicts/unresolved slots are OMITTED, never silently picked).
|
|
33
|
+
*/
|
|
34
|
+
export type SlotMap = Record<string, SlotBinding>;
|
|
35
|
+
//# sourceMappingURL=store-contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-contract.d.ts","sourceRoot":"","sources":["../src/store-contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-contract.js","sourceRoot":"","sources":["../src/store-contract.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the section/JSON-template contract for theme COMPOSITION.
|
|
3
|
+
*
|
|
4
|
+
* A page template is a declarative JSON document: a `page` type plus an ordered list of `sections`,
|
|
5
|
+
* each a `{ type, settings? }` pair. The storefront runtime resolves each `type` against a section
|
|
6
|
+
* registry and renders the sections in order. Like the manifest (see `manifest.ts`), this is a
|
|
7
|
+
* declarative ASSET — there is NO code execution here: these are PURE validators + author-time typing
|
|
8
|
+
* helpers, exactly mirroring `parseAndVerifyThemeManifest` / `defineTheme` / `defineThemeSettings`.
|
|
9
|
+
*
|
|
10
|
+
* The manifest is NOT touched: the `templates[]` declaration on the manifest itself is deferred to a
|
|
11
|
+
* later stage. This file only ADDS the template contract; `apps/api` is unaffected.
|
|
12
|
+
*
|
|
13
|
+
* The byte cap is REUSED from `@sovecom/module-sdk` (via `manifest.ts`) so a template is gated by the
|
|
14
|
+
* same finite size bound as a manifest — one home for the cap, no duplication.
|
|
15
|
+
*/
|
|
16
|
+
import { z } from 'zod';
|
|
17
|
+
/**
|
|
18
|
+
* Max nesting depth of `regions`. Depth 0 = a top-level section; a layout section with
|
|
19
|
+
* `regions` is depth 1; a section INSIDE a region is depth 2. Two levels of layout nesting is the
|
|
20
|
+
* cap — enough for the `columns` sidebar/results split without unbounded recursion. Enforced in
|
|
21
|
+
* {@link parseTemplate} (the schema validates shape/bounds; this walk rejects over-depth).
|
|
22
|
+
*/
|
|
23
|
+
export declare const MAX_REGION_DEPTH = 2;
|
|
24
|
+
/** A region name is a non-empty lowercase slug (e.g. `left`/`right`) — same shape as a section type. */
|
|
25
|
+
export declare const REGION_NAME_RE: RegExp;
|
|
26
|
+
/**
|
|
27
|
+
* The page types a theme can supply a template for. Only `home` is consumed at this stage; the rest
|
|
28
|
+
* are DECLARED so later stages slot in (product/category/products/search/cart) without a contract
|
|
29
|
+
* change. The literal-tuple `as const` lets {@link pageTypeSchema} mirror it exactly.
|
|
30
|
+
*/
|
|
31
|
+
export declare const PAGE_TYPES: readonly ["home", "product", "category", "products", "search", "cart"];
|
|
32
|
+
/** A section `type`: the same lowercase-slug shape as a theme name / slot slug (`manifest.ts`). */
|
|
33
|
+
export declare const SECTION_TYPE_RE: RegExp;
|
|
34
|
+
/** Zod enum over {@link PAGE_TYPES}. Rejects any page type outside the declared set. */
|
|
35
|
+
export declare const pageTypeSchema: z.ZodEnum<{
|
|
36
|
+
home: "home";
|
|
37
|
+
product: "product";
|
|
38
|
+
category: "category";
|
|
39
|
+
products: "products";
|
|
40
|
+
search: "search";
|
|
41
|
+
cart: "cart";
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* A region-name key schema: a non-empty, bounded lowercase slug (e.g. `left`/`right`). Used as the
|
|
45
|
+
* KEY schema of the `regions` record so a bad region name is rejected at parse time.
|
|
46
|
+
*/
|
|
47
|
+
export declare const regionNameSchema: z.ZodString;
|
|
48
|
+
/**
|
|
49
|
+
* A single template section: a slug `type`, an optional opaque `settings` bag, and an
|
|
50
|
+
* OPTIONAL `regions` map for LAYOUT nesting — named region → an ordered list of nested sections,
|
|
51
|
+
* recursively the same shape. `.strict()` rejects unknown keys (supply-chain hygiene, mirrors the
|
|
52
|
+
* manifest). `settings` is `Record<string, unknown>` — opaque on the wire, validated against the
|
|
53
|
+
* section's own shape at render time (not here).
|
|
54
|
+
*
|
|
55
|
+
* The schema is SELF-REFERENTIAL: `regions` values are arrays of `templateSectionSchema`. The
|
|
56
|
+
* recursion uses the canonical Zod 4 pattern — a FIELD-LEVEL `z.lazy()` that defers the self-reference
|
|
57
|
+
* to validation time, so `templateSectionSchema` is fully initialised before the thunk dereferences
|
|
58
|
+
* it (no eager-`.strict()` ordering hazard). Structural bounds (region count, per-region section cap,
|
|
59
|
+
* region-name shape) are enforced HERE; the max NESTING DEPTH is enforced separately in
|
|
60
|
+
* {@link parseTemplate} (a post-parse walk), so an over-deep template is rejected with a clear error.
|
|
61
|
+
*/
|
|
62
|
+
export declare const templateSectionSchema: z.ZodType<TemplateSection>;
|
|
63
|
+
/**
|
|
64
|
+
* A page template: a known `page` type plus an ordered, bounded list of sections. `.strict()` rejects
|
|
65
|
+
* unknown top-level keys. The section count is capped at {@link MAX_SECTIONS} to reject runaway docs.
|
|
66
|
+
*/
|
|
67
|
+
export declare const templateSchema: z.ZodObject<{
|
|
68
|
+
page: z.ZodEnum<{
|
|
69
|
+
home: "home";
|
|
70
|
+
product: "product";
|
|
71
|
+
category: "category";
|
|
72
|
+
products: "products";
|
|
73
|
+
search: "search";
|
|
74
|
+
cart: "cart";
|
|
75
|
+
}>;
|
|
76
|
+
sections: z.ZodArray<z.ZodType<TemplateSection, unknown, z.core.$ZodTypeInternals<TemplateSection, unknown>>>;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
/** A validated page type (one of {@link PAGE_TYPES}). */
|
|
79
|
+
export type PageType = z.infer<typeof pageTypeSchema>;
|
|
80
|
+
/**
|
|
81
|
+
* A validated template section (`{ type, settings?, regions? }`). SELF-REFERENTIAL:
|
|
82
|
+
* `regions` maps a region name → a list of nested `TemplateSection`s. Hand-written rather than
|
|
83
|
+
* `z.infer`'d so the recursion is explicit + stable across the getter-based schema.
|
|
84
|
+
*/
|
|
85
|
+
export interface TemplateSection {
|
|
86
|
+
type: string;
|
|
87
|
+
settings?: Record<string, unknown>;
|
|
88
|
+
regions?: Record<string, TemplateSection[]>;
|
|
89
|
+
}
|
|
90
|
+
/** A validated page template (`{ page, sections }`). */
|
|
91
|
+
export type ThemeTemplate = z.infer<typeof templateSchema>;
|
|
92
|
+
/**
|
|
93
|
+
* Parse + verify a raw template JSON string. Enforces the byte cap (reused from the manifest), parses
|
|
94
|
+
* JSON (mapping a parse failure to a clear error), then runs the Zod schema, aggregating issues into a
|
|
95
|
+
* descriptive message. Returns the typed template or throws a descriptive `Error`. PURE — no I/O, no
|
|
96
|
+
* code execution. Mirrors {@link parseAndVerifyThemeManifest}.
|
|
97
|
+
*/
|
|
98
|
+
export declare function parseTemplate(raw: string): ThemeTemplate;
|
|
99
|
+
/**
|
|
100
|
+
* Validate an author's template config and return the validated, typed {@link ThemeTemplate}. Runs the
|
|
101
|
+
* SAME `parseTemplate` pipeline (round-trips through JSON), so what passes here is exactly what the
|
|
102
|
+
* runtime accepts. Throws a clear `Error` on invalid input. NO code execution. Mirrors {@link defineTheme}.
|
|
103
|
+
*/
|
|
104
|
+
export declare function defineTemplate(config: ThemeTemplate): ThemeTemplate;
|
|
105
|
+
/**
|
|
106
|
+
* An author-time section DEFINITION: the section `type` plus an OPTIONAL phantom `settings` default
|
|
107
|
+
* carrying the settings type `T`. Purely a typing vehicle — see {@link defineSection}.
|
|
108
|
+
*/
|
|
109
|
+
export interface SectionDef<T> {
|
|
110
|
+
readonly type: string;
|
|
111
|
+
readonly settings?: T;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Identity helper that types-and-returns a section definition, pinning the settings type `T` for
|
|
115
|
+
* author editor autocomplete. A pure no-op at runtime (returns its argument unchanged) — it does NOT
|
|
116
|
+
* validate or read anything. Mirrors {@link defineThemeSettings}'s pure-typing style.
|
|
117
|
+
*/
|
|
118
|
+
export declare function defineSection<T>(def: SectionDef<T>): SectionDef<T>;
|
|
119
|
+
//# sourceMappingURL=template.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.d.ts","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AASxB;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAClC,wGAAwG;AACxG,eAAO,MAAM,cAAc,QAAsB,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,UAAU,wEAAyE,CAAC;AAEjG,mGAAmG;AACnG,eAAO,MAAM,eAAe,QAAsB,CAAC;AAEnD,wFAAwF;AACxF,eAAO,MAAM,cAAc;;;;;;;EAAqB,CAAC;AAEjD;;;GAGG;AACH,eAAO,MAAM,gBAAgB,aAI+C,CAAC;AAE7E;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,CAAC,OAAO,CAAC,eAAe,CAqBpB,CAAC;AAE1C;;;GAGG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;kBAKhB,CAAC;AAEZ,yDAAyD;AACzD,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AACtD;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,EAAE,CAAC,CAAC;CAC7C;AACD,wDAAwD;AACxD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAE3D;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CA6BxD;AAkBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAKnE;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,GAAG,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAElE"}
|
package/dist/template.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.templateSchema = exports.templateSectionSchema = exports.regionNameSchema = exports.pageTypeSchema = exports.SECTION_TYPE_RE = exports.PAGE_TYPES = exports.REGION_NAME_RE = exports.MAX_REGION_DEPTH = void 0;
|
|
4
|
+
exports.parseTemplate = parseTemplate;
|
|
5
|
+
exports.defineTemplate = defineTemplate;
|
|
6
|
+
exports.defineSection = defineSection;
|
|
7
|
+
/**
|
|
8
|
+
* the section/JSON-template contract for theme COMPOSITION.
|
|
9
|
+
*
|
|
10
|
+
* A page template is a declarative JSON document: a `page` type plus an ordered list of `sections`,
|
|
11
|
+
* each a `{ type, settings? }` pair. The storefront runtime resolves each `type` against a section
|
|
12
|
+
* registry and renders the sections in order. Like the manifest (see `manifest.ts`), this is a
|
|
13
|
+
* declarative ASSET — there is NO code execution here: these are PURE validators + author-time typing
|
|
14
|
+
* helpers, exactly mirroring `parseAndVerifyThemeManifest` / `defineTheme` / `defineThemeSettings`.
|
|
15
|
+
*
|
|
16
|
+
* The manifest is NOT touched: the `templates[]` declaration on the manifest itself is deferred to a
|
|
17
|
+
* later stage. This file only ADDS the template contract; `apps/api` is unaffected.
|
|
18
|
+
*
|
|
19
|
+
* The byte cap is REUSED from `@sovecom/module-sdk` (via `manifest.ts`) so a template is gated by the
|
|
20
|
+
* same finite size bound as a manifest — one home for the cap, no duplication.
|
|
21
|
+
*/
|
|
22
|
+
const zod_1 = require("zod");
|
|
23
|
+
const manifest_js_1 = require("./manifest.js");
|
|
24
|
+
/** Generous-but-finite bound — reject pathological templates up front. */
|
|
25
|
+
const MAX_SECTIONS = 64;
|
|
26
|
+
/** A section type is a non-empty lowercase slug — same length bound the manifest uses for slots. */
|
|
27
|
+
const MAX_SECTION_TYPE_LEN = 128;
|
|
28
|
+
/** A layout section nests sub-sections in named `regions` — bound the region-name count. */
|
|
29
|
+
const MAX_REGIONS = 8;
|
|
30
|
+
/**
|
|
31
|
+
* Max nesting depth of `regions`. Depth 0 = a top-level section; a layout section with
|
|
32
|
+
* `regions` is depth 1; a section INSIDE a region is depth 2. Two levels of layout nesting is the
|
|
33
|
+
* cap — enough for the `columns` sidebar/results split without unbounded recursion. Enforced in
|
|
34
|
+
* {@link parseTemplate} (the schema validates shape/bounds; this walk rejects over-depth).
|
|
35
|
+
*/
|
|
36
|
+
exports.MAX_REGION_DEPTH = 2;
|
|
37
|
+
/** A region name is a non-empty lowercase slug (e.g. `left`/`right`) — same shape as a section type. */
|
|
38
|
+
exports.REGION_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
39
|
+
/**
|
|
40
|
+
* The page types a theme can supply a template for. Only `home` is consumed at this stage; the rest
|
|
41
|
+
* are DECLARED so later stages slot in (product/category/products/search/cart) without a contract
|
|
42
|
+
* change. The literal-tuple `as const` lets {@link pageTypeSchema} mirror it exactly.
|
|
43
|
+
*/
|
|
44
|
+
exports.PAGE_TYPES = ['home', 'product', 'category', 'products', 'search', 'cart'];
|
|
45
|
+
/** A section `type`: the same lowercase-slug shape as a theme name / slot slug (`manifest.ts`). */
|
|
46
|
+
exports.SECTION_TYPE_RE = /^[a-z][a-z0-9-]*$/;
|
|
47
|
+
/** Zod enum over {@link PAGE_TYPES}. Rejects any page type outside the declared set. */
|
|
48
|
+
exports.pageTypeSchema = zod_1.z.enum(exports.PAGE_TYPES);
|
|
49
|
+
/**
|
|
50
|
+
* A region-name key schema: a non-empty, bounded lowercase slug (e.g. `left`/`right`). Used as the
|
|
51
|
+
* KEY schema of the `regions` record so a bad region name is rejected at parse time.
|
|
52
|
+
*/
|
|
53
|
+
exports.regionNameSchema = zod_1.z
|
|
54
|
+
.string()
|
|
55
|
+
.min(1)
|
|
56
|
+
.max(MAX_SECTION_TYPE_LEN)
|
|
57
|
+
.regex(exports.REGION_NAME_RE, 'region name must be a lowercase slug like "left"');
|
|
58
|
+
/**
|
|
59
|
+
* A single template section: a slug `type`, an optional opaque `settings` bag, and an
|
|
60
|
+
* OPTIONAL `regions` map for LAYOUT nesting — named region → an ordered list of nested sections,
|
|
61
|
+
* recursively the same shape. `.strict()` rejects unknown keys (supply-chain hygiene, mirrors the
|
|
62
|
+
* manifest). `settings` is `Record<string, unknown>` — opaque on the wire, validated against the
|
|
63
|
+
* section's own shape at render time (not here).
|
|
64
|
+
*
|
|
65
|
+
* The schema is SELF-REFERENTIAL: `regions` values are arrays of `templateSectionSchema`. The
|
|
66
|
+
* recursion uses the canonical Zod 4 pattern — a FIELD-LEVEL `z.lazy()` that defers the self-reference
|
|
67
|
+
* to validation time, so `templateSectionSchema` is fully initialised before the thunk dereferences
|
|
68
|
+
* it (no eager-`.strict()` ordering hazard). Structural bounds (region count, per-region section cap,
|
|
69
|
+
* region-name shape) are enforced HERE; the max NESTING DEPTH is enforced separately in
|
|
70
|
+
* {@link parseTemplate} (a post-parse walk), so an over-deep template is rejected with a clear error.
|
|
71
|
+
*/
|
|
72
|
+
exports.templateSectionSchema = zod_1.z
|
|
73
|
+
.object({
|
|
74
|
+
type: zod_1.z
|
|
75
|
+
.string()
|
|
76
|
+
.min(1)
|
|
77
|
+
.max(MAX_SECTION_TYPE_LEN)
|
|
78
|
+
.regex(exports.SECTION_TYPE_RE, 'section type must be a lowercase slug like "featured-products"'),
|
|
79
|
+
settings: zod_1.z.record(zod_1.z.string(), zod_1.z.unknown()).optional(),
|
|
80
|
+
// Canonical Zod 4 recursion: a field-level `z.lazy()` thunk yields a `regions` record of
|
|
81
|
+
// region-name → bounded array of nested sections; the region-COUNT cap is a `.refine` on the
|
|
82
|
+
// record. Nesting DEPTH (≤ MAX_REGION_DEPTH) is enforced separately in `parseTemplate`.
|
|
83
|
+
regions: zod_1.z
|
|
84
|
+
.lazy(() => zod_1.z
|
|
85
|
+
.record(exports.regionNameSchema, zod_1.z.array(exports.templateSectionSchema).max(MAX_SECTIONS))
|
|
86
|
+
.refine((r) => Object.keys(r).length <= MAX_REGIONS, {
|
|
87
|
+
message: `a section may declare at most ${MAX_REGIONS} regions`,
|
|
88
|
+
}))
|
|
89
|
+
.optional(),
|
|
90
|
+
})
|
|
91
|
+
.strict();
|
|
92
|
+
/**
|
|
93
|
+
* A page template: a known `page` type plus an ordered, bounded list of sections. `.strict()` rejects
|
|
94
|
+
* unknown top-level keys. The section count is capped at {@link MAX_SECTIONS} to reject runaway docs.
|
|
95
|
+
*/
|
|
96
|
+
exports.templateSchema = zod_1.z
|
|
97
|
+
.object({
|
|
98
|
+
page: exports.pageTypeSchema,
|
|
99
|
+
sections: zod_1.z.array(exports.templateSectionSchema).max(MAX_SECTIONS),
|
|
100
|
+
})
|
|
101
|
+
.strict();
|
|
102
|
+
/**
|
|
103
|
+
* Parse + verify a raw template JSON string. Enforces the byte cap (reused from the manifest), parses
|
|
104
|
+
* JSON (mapping a parse failure to a clear error), then runs the Zod schema, aggregating issues into a
|
|
105
|
+
* descriptive message. Returns the typed template or throws a descriptive `Error`. PURE — no I/O, no
|
|
106
|
+
* code execution. Mirrors {@link parseAndVerifyThemeManifest}.
|
|
107
|
+
*/
|
|
108
|
+
function parseTemplate(raw) {
|
|
109
|
+
const byteLen = Buffer.byteLength(raw, 'utf8');
|
|
110
|
+
if (byteLen > manifest_js_1.MANIFEST_MAX_BYTES) {
|
|
111
|
+
throw new Error(`theme template too large: ${byteLen} bytes exceeds the ${manifest_js_1.MANIFEST_MAX_BYTES}-byte cap`);
|
|
112
|
+
}
|
|
113
|
+
let parsed;
|
|
114
|
+
try {
|
|
115
|
+
parsed = JSON.parse(raw);
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
throw new Error('theme template is not valid JSON');
|
|
119
|
+
}
|
|
120
|
+
const result = exports.templateSchema.safeParse(parsed);
|
|
121
|
+
if (!result.success) {
|
|
122
|
+
const detail = result.error.issues
|
|
123
|
+
.map((issue) => `${issue.path.join('.') || '(root)'}: ${issue.message}`)
|
|
124
|
+
.join('; ');
|
|
125
|
+
throw new Error(`invalid theme template: ${detail}`);
|
|
126
|
+
}
|
|
127
|
+
// Enforce the max nesting depth. The Zod schema validates the shape + structural
|
|
128
|
+
// bounds at every level (region count, per-region cap, region-name slug, .strict), but a getter-
|
|
129
|
+
// based recursive schema can't bound DEPTH — so reject an over-deep `regions` tree here. Depth 0 =
|
|
130
|
+
// a top-level section; a section inside a region is depth 1; nested again is depth 2; deeper throws.
|
|
131
|
+
for (const section of result.data.sections)
|
|
132
|
+
assertRegionDepth(section, 0);
|
|
133
|
+
return result.data;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Recursively assert no `regions` nesting exceeds {@link MAX_REGION_DEPTH}. `depth` is the current
|
|
137
|
+
* section's depth (0 at the top level). Throws a descriptive `Error` on the first over-deep section.
|
|
138
|
+
*/
|
|
139
|
+
function assertRegionDepth(section, depth) {
|
|
140
|
+
if (!section.regions)
|
|
141
|
+
return;
|
|
142
|
+
if (depth >= exports.MAX_REGION_DEPTH) {
|
|
143
|
+
throw new Error(`invalid theme template: region nesting exceeds the max depth of ${exports.MAX_REGION_DEPTH}`);
|
|
144
|
+
}
|
|
145
|
+
for (const nested of Object.values(section.regions)) {
|
|
146
|
+
for (const child of nested)
|
|
147
|
+
assertRegionDepth(child, depth + 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Validate an author's template config and return the validated, typed {@link ThemeTemplate}. Runs the
|
|
152
|
+
* SAME `parseTemplate` pipeline (round-trips through JSON), so what passes here is exactly what the
|
|
153
|
+
* runtime accepts. Throws a clear `Error` on invalid input. NO code execution. Mirrors {@link defineTheme}.
|
|
154
|
+
*/
|
|
155
|
+
function defineTemplate(config) {
|
|
156
|
+
if (config === null || typeof config !== 'object' || Array.isArray(config)) {
|
|
157
|
+
throw new TypeError('defineTemplate(config): config must be an object');
|
|
158
|
+
}
|
|
159
|
+
return parseTemplate(JSON.stringify(config));
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Identity helper that types-and-returns a section definition, pinning the settings type `T` for
|
|
163
|
+
* author editor autocomplete. A pure no-op at runtime (returns its argument unchanged) — it does NOT
|
|
164
|
+
* validate or read anything. Mirrors {@link defineThemeSettings}'s pure-typing style.
|
|
165
|
+
*/
|
|
166
|
+
function defineSection(def) {
|
|
167
|
+
return def;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=template.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"template.js","sourceRoot":"","sources":["../src/template.ts"],"names":[],"mappings":"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"}
|
package/dist/theme.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the author-facing `defineTheme` helper.
|
|
3
|
+
*
|
|
4
|
+
* `defineTheme(config)` is the theme twin of `@sovecom/module-sdk`'s `defineModule`, with one
|
|
5
|
+
* LOAD-BEARING difference: a theme is a declarative ASSET — there is NO
|
|
6
|
+
* `activate`, NO worker, NO runtime entrypoint. So `defineTheme` does not wrap an executable
|
|
7
|
+
* body; it VALIDATES the author's config against the canonical manifest schema and returns the
|
|
8
|
+
* typed, validated `ThemeManifest` OBJECT. It is a build-/author-time helper, never a runtime
|
|
9
|
+
* entry. A misconfigured theme therefore fails fast with a clear message at author build time,
|
|
10
|
+
* not as an opaque install rejection.
|
|
11
|
+
*/
|
|
12
|
+
import { type ThemeManifest } from './manifest.js';
|
|
13
|
+
/**
|
|
14
|
+
* Author-supplied config for {@link defineTheme}. Structurally the manifest shape itself — the
|
|
15
|
+
* author writes their `sovecom.theme.json` content as a typed object and `defineTheme` validates
|
|
16
|
+
* it. Kept as a distinct alias so the authoring intent (input) reads differently from the
|
|
17
|
+
* validated output ({@link ThemeManifest}).
|
|
18
|
+
*
|
|
19
|
+
* The `slots` INPUT is widened to `readonly string[]` so the documented compose pattern
|
|
20
|
+
* `defineTheme({ slots: defineThemeSlots([...]) })` typechecks — `defineThemeSlots` returns a
|
|
21
|
+
* FROZEN (`readonly`) array and that must be assignable here. The runtime validation is unchanged:
|
|
22
|
+
* `defineTheme` round-trips the config through `parseAndVerifyThemeManifest`, so the returned
|
|
23
|
+
* {@link ThemeManifest} still carries the schema-validated mutable `string[]`.
|
|
24
|
+
*/
|
|
25
|
+
export type DefineThemeConfig = Omit<ThemeManifest, 'slots'> & {
|
|
26
|
+
readonly slots?: readonly string[];
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Validate an author's theme config and return the validated, typed {@link ThemeManifest}.
|
|
30
|
+
* Runs the SAME `parseAndVerifyThemeManifest` the core runs at install time (single source of
|
|
31
|
+
* truth), so what passes here is exactly what the core will accept. Throws a clear `Error` on
|
|
32
|
+
* invalid input (bad slug, invalid semver, unknown key, oversized, …). NO code execution.
|
|
33
|
+
*/
|
|
34
|
+
export declare function defineTheme(config: DefineThemeConfig): ThemeManifest;
|
|
35
|
+
//# sourceMappingURL=theme.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAA+B,KAAK,aAAa,EAAE,MAAM,eAAe,CAAC;AAEhF;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,iBAAiB,GAAG,IAAI,CAAC,aAAa,EAAE,OAAO,CAAC,GAAG;IAC7D,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa,CAQpE"}
|
package/dist/theme.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineTheme = defineTheme;
|
|
4
|
+
/**
|
|
5
|
+
* the author-facing `defineTheme` helper.
|
|
6
|
+
*
|
|
7
|
+
* `defineTheme(config)` is the theme twin of `@sovecom/module-sdk`'s `defineModule`, with one
|
|
8
|
+
* LOAD-BEARING difference: a theme is a declarative ASSET — there is NO
|
|
9
|
+
* `activate`, NO worker, NO runtime entrypoint. So `defineTheme` does not wrap an executable
|
|
10
|
+
* body; it VALIDATES the author's config against the canonical manifest schema and returns the
|
|
11
|
+
* typed, validated `ThemeManifest` OBJECT. It is a build-/author-time helper, never a runtime
|
|
12
|
+
* entry. A misconfigured theme therefore fails fast with a clear message at author build time,
|
|
13
|
+
* not as an opaque install rejection.
|
|
14
|
+
*/
|
|
15
|
+
const manifest_js_1 = require("./manifest.js");
|
|
16
|
+
/**
|
|
17
|
+
* Validate an author's theme config and return the validated, typed {@link ThemeManifest}.
|
|
18
|
+
* Runs the SAME `parseAndVerifyThemeManifest` the core runs at install time (single source of
|
|
19
|
+
* truth), so what passes here is exactly what the core will accept. Throws a clear `Error` on
|
|
20
|
+
* invalid input (bad slug, invalid semver, unknown key, oversized, …). NO code execution.
|
|
21
|
+
*/
|
|
22
|
+
function defineTheme(config) {
|
|
23
|
+
if (config === null || typeof config !== 'object') {
|
|
24
|
+
throw new TypeError('defineTheme(config): config must be an object');
|
|
25
|
+
}
|
|
26
|
+
// Round-trip through the canonical validator: serialise, then parse+verify. This reuses the
|
|
27
|
+
// one byte-cap + `.strict()` + slug + semver pipeline the core enforces, so `defineTheme`
|
|
28
|
+
// cannot drift from install-time validation.
|
|
29
|
+
return (0, manifest_js_1.parseAndVerifyThemeManifest)(JSON.stringify(config));
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=theme.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["../src/theme.ts"],"names":[],"mappings":";;AAmCA,kCAQC;AA3CD;;;;;;;;;;GAUG;AACH,+CAAgF;AAkBhF;;;;;GAKG;AACH,SAAgB,WAAW,CAAC,MAAyB;IACnD,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAClD,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,4FAA4F;IAC5F,0FAA0F;IAC1F,6CAA6C;IAC7C,OAAO,IAAA,yCAA2B,EAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAC7D,CAAC"}
|
package/dist/widget.d.ts
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* the module slot-widget CONTRACT + vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* A sandboxed module contributes storefront UI by returning a typed **widget descriptor**
|
|
5
|
+
* `{ type, props }` — a widget `type` from a CLOSED, core-owned MIT vocabulary plus validated props.
|
|
6
|
+
* The storefront renders its OWN known MIT components with that data; NO code, NO HTML, NO SVG
|
|
7
|
+
* crosses the boundary — only DATA.
|
|
8
|
+
*
|
|
9
|
+
* Like `manifest.ts` / `template.ts`, this file is a declarative ASSET contract: PURE validators +
|
|
10
|
+
* types only. There is NO React, NO fetch, NO Node runtime API, NO render here. `parseWidget` mirrors
|
|
11
|
+
* `parseTemplate`'s byte-cap + JSON.parse + Zod pipeline, with ONE deliberate difference: it returns
|
|
12
|
+
* `null` on ANY failure (never throws) — the defensive fail-closed contract the storefront RSC relies
|
|
13
|
+
* on to "render nothing, never 500 the page".
|
|
14
|
+
*
|
|
15
|
+
* The byte cap is its own constant ({@link WIDGET_MAX_BYTES}) but reuses the manifest cap value so a
|
|
16
|
+
* widget descriptor is gated by the same finite bound — one numeric home, no divergence.
|
|
17
|
+
*/
|
|
18
|
+
import { z } from 'zod';
|
|
19
|
+
/**
|
|
20
|
+
* The CLOSED, core-owned widget vocabulary. A module author CANNOT register a new type;
|
|
21
|
+
* adding one is an MIT storefront contribution reviewed like adding a section. The
|
|
22
|
+
* `component` slug in a module manifest's `slots[]` IS one of these `type`s. The literal-tuple
|
|
23
|
+
* `as const` lets the discriminated union below mirror it exactly.
|
|
24
|
+
*/
|
|
25
|
+
export declare const WIDGET_TYPES: readonly ["star-rating-summary", "review-list", "product-carousel", "toggle-button", "submit-form"];
|
|
26
|
+
/** A validated widget type (one of {@link WIDGET_TYPES}). */
|
|
27
|
+
export type WidgetType = (typeof WIDGET_TYPES)[number];
|
|
28
|
+
/**
|
|
29
|
+
* Byte cap for a raw widget descriptor. Reuses the manifest cap VALUE (one source of truth) — a
|
|
30
|
+
* descriptor is small data, so this generous bound only rejects pathological payloads up front, before
|
|
31
|
+
* any JSON.parse or schema work.
|
|
32
|
+
*/
|
|
33
|
+
export declare const WIDGET_MAX_BYTES: number;
|
|
34
|
+
export declare const actionPathSchema: z.ZodString;
|
|
35
|
+
/** An interactive widget's action target: just a validated relative `path` (C1 shape-only). */
|
|
36
|
+
export declare const actionSchema: z.ZodObject<{
|
|
37
|
+
path: z.ZodString;
|
|
38
|
+
}, z.core.$strict>;
|
|
39
|
+
/** `star-rating-summary` props: an average in [0,5] and a non-negative integer count. */
|
|
40
|
+
export declare const starRatingSummaryPropsSchema: z.ZodObject<{
|
|
41
|
+
average: z.ZodNumber;
|
|
42
|
+
count: z.ZodNumber;
|
|
43
|
+
}, z.core.$strict>;
|
|
44
|
+
/** `review-list` props: a bounded list of bounded review items (id/rating/body/author/createdAt). */
|
|
45
|
+
export declare const reviewListPropsSchema: z.ZodObject<{
|
|
46
|
+
items: z.ZodArray<z.ZodObject<{
|
|
47
|
+
id: z.ZodString;
|
|
48
|
+
rating: z.ZodNumber;
|
|
49
|
+
body: z.ZodString;
|
|
50
|
+
author: z.ZodOptional<z.ZodString>;
|
|
51
|
+
createdAt: z.ZodISODateTime;
|
|
52
|
+
}, z.core.$strict>>;
|
|
53
|
+
}, z.core.$strict>;
|
|
54
|
+
/** `product-carousel` props: an optional heading + a bounded list of bounded product cards. */
|
|
55
|
+
export declare const productCarouselPropsSchema: z.ZodObject<{
|
|
56
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
57
|
+
items: z.ZodArray<z.ZodObject<{
|
|
58
|
+
productId: z.ZodString;
|
|
59
|
+
slug: z.ZodString;
|
|
60
|
+
title: z.ZodString;
|
|
61
|
+
imageUrl: z.ZodOptional<z.ZodString>;
|
|
62
|
+
}, z.core.$strict>>;
|
|
63
|
+
}, z.core.$strict>;
|
|
64
|
+
/** `toggle-button` props: on/off actions (relative paths), bounded labels, enum-only icon. */
|
|
65
|
+
export declare const toggleButtonPropsSchema: z.ZodObject<{
|
|
66
|
+
initialOn: z.ZodBoolean;
|
|
67
|
+
onAction: z.ZodObject<{
|
|
68
|
+
path: z.ZodString;
|
|
69
|
+
}, z.core.$strict>;
|
|
70
|
+
offAction: z.ZodObject<{
|
|
71
|
+
path: z.ZodString;
|
|
72
|
+
}, z.core.$strict>;
|
|
73
|
+
labels: z.ZodObject<{
|
|
74
|
+
on: z.ZodString;
|
|
75
|
+
off: z.ZodString;
|
|
76
|
+
}, z.core.$strict>;
|
|
77
|
+
icon: z.ZodEnum<{
|
|
78
|
+
heart: "heart";
|
|
79
|
+
bell: "bell";
|
|
80
|
+
star: "star";
|
|
81
|
+
}>;
|
|
82
|
+
}, z.core.$strict>;
|
|
83
|
+
/** `submit-form` props: a relative action path, a bounded field list with enum-only kinds. */
|
|
84
|
+
export declare const submitFormPropsSchema: z.ZodObject<{
|
|
85
|
+
action: z.ZodObject<{
|
|
86
|
+
path: z.ZodString;
|
|
87
|
+
}, z.core.$strict>;
|
|
88
|
+
submitLabel: z.ZodString;
|
|
89
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
90
|
+
name: z.ZodString;
|
|
91
|
+
label: z.ZodString;
|
|
92
|
+
kind: z.ZodEnum<{
|
|
93
|
+
rating: "rating";
|
|
94
|
+
text: "text";
|
|
95
|
+
textarea: "textarea";
|
|
96
|
+
email: "email";
|
|
97
|
+
select: "select";
|
|
98
|
+
}>;
|
|
99
|
+
required: z.ZodBoolean;
|
|
100
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
101
|
+
}, z.core.$strict>>;
|
|
102
|
+
successMessage: z.ZodOptional<z.ZodString>;
|
|
103
|
+
}, z.core.$strict>;
|
|
104
|
+
/**
|
|
105
|
+
* The widget descriptor: `{ type, props }` discriminated on `type`, so each widget's props are
|
|
106
|
+
* validated against ITS OWN schema (wrong props for a type ⇒ fail). `.strict()` on each member rejects
|
|
107
|
+
* unknown keys at the descriptor level; the per-widget props schemas are `.strict()` too, so unknown
|
|
108
|
+
* keys are rejected at BOTH levels.
|
|
109
|
+
*/
|
|
110
|
+
export declare const widgetDescriptorSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
111
|
+
type: z.ZodLiteral<"star-rating-summary">;
|
|
112
|
+
props: z.ZodObject<{
|
|
113
|
+
average: z.ZodNumber;
|
|
114
|
+
count: z.ZodNumber;
|
|
115
|
+
}, z.core.$strict>;
|
|
116
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
117
|
+
type: z.ZodLiteral<"review-list">;
|
|
118
|
+
props: z.ZodObject<{
|
|
119
|
+
items: z.ZodArray<z.ZodObject<{
|
|
120
|
+
id: z.ZodString;
|
|
121
|
+
rating: z.ZodNumber;
|
|
122
|
+
body: z.ZodString;
|
|
123
|
+
author: z.ZodOptional<z.ZodString>;
|
|
124
|
+
createdAt: z.ZodISODateTime;
|
|
125
|
+
}, z.core.$strict>>;
|
|
126
|
+
}, z.core.$strict>;
|
|
127
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
128
|
+
type: z.ZodLiteral<"product-carousel">;
|
|
129
|
+
props: z.ZodObject<{
|
|
130
|
+
heading: z.ZodOptional<z.ZodString>;
|
|
131
|
+
items: z.ZodArray<z.ZodObject<{
|
|
132
|
+
productId: z.ZodString;
|
|
133
|
+
slug: z.ZodString;
|
|
134
|
+
title: z.ZodString;
|
|
135
|
+
imageUrl: z.ZodOptional<z.ZodString>;
|
|
136
|
+
}, z.core.$strict>>;
|
|
137
|
+
}, z.core.$strict>;
|
|
138
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
139
|
+
type: z.ZodLiteral<"toggle-button">;
|
|
140
|
+
props: z.ZodObject<{
|
|
141
|
+
initialOn: z.ZodBoolean;
|
|
142
|
+
onAction: z.ZodObject<{
|
|
143
|
+
path: z.ZodString;
|
|
144
|
+
}, z.core.$strict>;
|
|
145
|
+
offAction: z.ZodObject<{
|
|
146
|
+
path: z.ZodString;
|
|
147
|
+
}, z.core.$strict>;
|
|
148
|
+
labels: z.ZodObject<{
|
|
149
|
+
on: z.ZodString;
|
|
150
|
+
off: z.ZodString;
|
|
151
|
+
}, z.core.$strict>;
|
|
152
|
+
icon: z.ZodEnum<{
|
|
153
|
+
heart: "heart";
|
|
154
|
+
bell: "bell";
|
|
155
|
+
star: "star";
|
|
156
|
+
}>;
|
|
157
|
+
}, z.core.$strict>;
|
|
158
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
159
|
+
type: z.ZodLiteral<"submit-form">;
|
|
160
|
+
props: z.ZodObject<{
|
|
161
|
+
action: z.ZodObject<{
|
|
162
|
+
path: z.ZodString;
|
|
163
|
+
}, z.core.$strict>;
|
|
164
|
+
submitLabel: z.ZodString;
|
|
165
|
+
fields: z.ZodArray<z.ZodObject<{
|
|
166
|
+
name: z.ZodString;
|
|
167
|
+
label: z.ZodString;
|
|
168
|
+
kind: z.ZodEnum<{
|
|
169
|
+
rating: "rating";
|
|
170
|
+
text: "text";
|
|
171
|
+
textarea: "textarea";
|
|
172
|
+
email: "email";
|
|
173
|
+
select: "select";
|
|
174
|
+
}>;
|
|
175
|
+
required: z.ZodBoolean;
|
|
176
|
+
options: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
177
|
+
}, z.core.$strict>>;
|
|
178
|
+
successMessage: z.ZodOptional<z.ZodString>;
|
|
179
|
+
}, z.core.$strict>;
|
|
180
|
+
}, z.core.$strict>], "type">;
|
|
181
|
+
export type StarRatingSummaryProps = z.infer<typeof starRatingSummaryPropsSchema>;
|
|
182
|
+
export type ReviewListProps = z.infer<typeof reviewListPropsSchema>;
|
|
183
|
+
export type ProductCarouselProps = z.infer<typeof productCarouselPropsSchema>;
|
|
184
|
+
export type ToggleButtonProps = z.infer<typeof toggleButtonPropsSchema>;
|
|
185
|
+
export type SubmitFormProps = z.infer<typeof submitFormPropsSchema>;
|
|
186
|
+
/** A validated widget descriptor (the discriminated `{ type, props }`). */
|
|
187
|
+
export type WidgetDescriptor = z.infer<typeof widgetDescriptorSchema>;
|
|
188
|
+
/**
|
|
189
|
+
* Parse + verify a raw widget descriptor. PURE — no I/O, no code execution, no render.
|
|
190
|
+
*
|
|
191
|
+
* Accepts either a JSON string (the wire form — byte-capped, then `JSON.parse`d, mirroring
|
|
192
|
+
* {@link parseTemplate}) or an already-parsed value (so the storefront can hand it the parsed JSON
|
|
193
|
+
* directly). Enforces the byte cap on the string form, validates against {@link widgetDescriptorSchema}
|
|
194
|
+
* (the discriminated union + per-widget `.strict()` props), and returns the typed descriptor — or
|
|
195
|
+
* `null` on ANY failure: oversized, non-JSON, non-object, unknown type, discriminator mismatch, an
|
|
196
|
+
* out-of-bounds / bad-enum / bad-path prop, or an unknown key at either level. It NEVER throws: the
|
|
197
|
+
* fail-closed contract the storefront RSC relies on to "render nothing, never block or 500 the page".
|
|
198
|
+
*/
|
|
199
|
+
export declare function parseWidget(raw: unknown): WidgetDescriptor | null;
|
|
200
|
+
//# sourceMappingURL=widget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget.d.ts","sourceRoot":"","sources":["../src/widget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;;;;GAKG;AACH,eAAO,MAAM,YAAY,qGAMf,CAAC;AAEX,6DAA6D;AAC7D,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,QAAqB,CAAC;AAmDnD,eAAO,MAAM,gBAAgB,aAOzB,CAAC;AAEL,+FAA+F;AAC/F,eAAO,MAAM,YAAY;;kBAAgD,CAAC;AAI1E,yFAAyF;AACzF,eAAO,MAAM,4BAA4B;;;kBAK9B,CAAC;AAEZ,qGAAqG;AACrG,eAAO,MAAM,qBAAqB;;;;;;;;kBAkBvB,CAAC;AAmBZ,+FAA+F;AAC/F,eAAO,MAAM,0BAA0B;;;;;;;;kBAgB5B,CAAC;AAEZ,8FAA8F;AAC9F,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;kBAazB,CAAC;AAEZ,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;kBAmBvB,CAAC;AAEZ;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4BAQjC,CAAC;AAGH,MAAM,MAAM,sBAAsB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,4BAA4B,CAAC,CAAC;AAClF,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAC9E,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AACxE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,OAAO,GAAG,gBAAgB,GAAG,IAAI,CAcjE"}
|