@wocart/theme 1.4.0 → 1.5.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.
@@ -9,9 +9,15 @@
9
9
  * so the manifest and assembly validators cannot disagree.
10
10
  */
11
11
  import { z } from "zod";
12
- import type { ThemeDefinition, ThemeManifest, ThemeManifestResult } from "./types.js";
12
+ import type { PageData, PresetInstance, SurfacePageType, RichTextFormat, RichTextValue, ThemeDefinition, ThemeManifest, ThemeManifestResult } from "./types.js";
13
13
  /** Version of the manifest schema and its interpretation (not the theme's). */
14
14
  export declare const THEME_MANIFEST_CONTRACT_VERSION: 1;
15
+ /**
16
+ * Where a built theme serves its manifest: a prerendered page at this path
17
+ * (`src/pages/wocart-theme.json.ts`), so the file is part of every build and
18
+ * a release can read it without running the theme.
19
+ */
20
+ export declare const THEME_MANIFEST_PATH = "/wocart-theme.json";
15
21
  /** Derive the data-only manifest from a theme. `component` and `load` are dropped. */
16
22
  export declare function themeManifest(theme: ThemeDefinition): ThemeManifest;
17
23
  /**
@@ -23,9 +29,12 @@ export declare const ThemeManifestSchema: z.ZodObject<{
23
29
  contractVersion: z.ZodLiteral<1>;
24
30
  key: z.ZodString;
25
31
  version: z.ZodString;
32
+ sdkVersion: z.ZodExactOptional<z.ZodString>;
26
33
  surfaces: z.ZodDefault<z.ZodArray<z.ZodObject<{
27
34
  key: z.ZodString;
28
35
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
36
+ pageType: z.ZodExactOptional<z.ZodCustom<SurfacePageType, SurfacePageType>>;
37
+ document: z.ZodExactOptional<z.ZodCustom<PageData, PageData>>;
29
38
  }, z.core.$strip>>>;
30
39
  locales: z.ZodDefault<z.ZodArray<z.ZodString>>;
31
40
  settings: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -56,6 +65,7 @@ export declare const ThemeManifestSchema: z.ZodObject<{
56
65
  default: z.ZodExactOptional<z.ZodNumber>;
57
66
  min: z.ZodExactOptional<z.ZodNumber>;
58
67
  max: z.ZodExactOptional<z.ZodNumber>;
68
+ step: z.ZodExactOptional<z.ZodNumber>;
59
69
  }, z.core.$strip>, z.ZodObject<{
60
70
  key: z.ZodString;
61
71
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
@@ -70,37 +80,110 @@ export declare const ThemeManifestSchema: z.ZodObject<{
70
80
  required: z.ZodExactOptional<z.ZodBoolean>;
71
81
  kind: z.ZodLiteral<"select">;
72
82
  options: z.ZodArray<z.ZodString>;
83
+ optionLabels: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
73
84
  default: z.ZodExactOptional<z.ZodString>;
74
- }, z.core.$strip>, z.ZodObject<{
85
+ }, z.core.$strip>, ...z.ZodObject<{
75
86
  key: z.ZodString;
76
87
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
77
88
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
78
89
  required: z.ZodExactOptional<z.ZodBoolean>;
79
- kind: z.ZodLiteral<"url">;
90
+ kind: z.ZodLiteral<"category" | "collection" | "color" | "menu" | "product" | "url">;
80
91
  default: z.ZodExactOptional<z.ZodString>;
81
- }, z.core.$strip>, z.ZodObject<{
92
+ }, z.core.$strip>[], z.ZodObject<{
82
93
  key: z.ZodString;
83
94
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
84
95
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
85
96
  required: z.ZodExactOptional<z.ZodBoolean>;
86
- kind: z.ZodLiteral<"color">;
87
- default: z.ZodExactOptional<z.ZodString>;
97
+ kind: z.ZodLiteral<"image">;
88
98
  }, z.core.$strip>, z.ZodObject<{
89
99
  key: z.ZodString;
90
100
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
91
101
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
92
102
  required: z.ZodExactOptional<z.ZodBoolean>;
93
- kind: z.ZodLiteral<"menu">;
94
- default: z.ZodExactOptional<z.ZodString>;
103
+ kind: z.ZodLiteral<"richtext">;
104
+ formats: z.ZodExactOptional<z.ZodArray<z.ZodCustom<RichTextFormat, RichTextFormat>>>;
105
+ default: z.ZodExactOptional<z.ZodCustom<RichTextValue, RichTextValue>>;
95
106
  }, z.core.$strip>, z.ZodObject<{
96
107
  key: z.ZodString;
97
108
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
98
109
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
99
110
  required: z.ZodExactOptional<z.ZodBoolean>;
100
- kind: z.ZodLiteral<"collection">;
101
- default: z.ZodExactOptional<z.ZodString>;
111
+ kind: z.ZodLiteral<"list">;
112
+ fields: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
113
+ key: z.ZodString;
114
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
115
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
116
+ required: z.ZodExactOptional<z.ZodBoolean>;
117
+ contentKey: z.ZodExactOptional<z.ZodString>;
118
+ default: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
119
+ kind: z.ZodLiteral<"text">;
120
+ }, z.core.$strip>, z.ZodObject<{
121
+ key: z.ZodString;
122
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
123
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
124
+ required: z.ZodExactOptional<z.ZodBoolean>;
125
+ contentKey: z.ZodExactOptional<z.ZodString>;
126
+ default: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
127
+ kind: z.ZodLiteral<"textarea">;
128
+ }, z.core.$strip>, z.ZodObject<{
129
+ key: z.ZodString;
130
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
131
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
132
+ required: z.ZodExactOptional<z.ZodBoolean>;
133
+ kind: z.ZodLiteral<"number">;
134
+ default: z.ZodExactOptional<z.ZodNumber>;
135
+ min: z.ZodExactOptional<z.ZodNumber>;
136
+ max: z.ZodExactOptional<z.ZodNumber>;
137
+ step: z.ZodExactOptional<z.ZodNumber>;
138
+ }, z.core.$strip>, z.ZodObject<{
139
+ key: z.ZodString;
140
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
141
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
142
+ required: z.ZodExactOptional<z.ZodBoolean>;
143
+ kind: z.ZodLiteral<"boolean">;
144
+ default: z.ZodExactOptional<z.ZodBoolean>;
145
+ }, z.core.$strip>, z.ZodObject<{
146
+ key: z.ZodString;
147
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
148
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
149
+ required: z.ZodExactOptional<z.ZodBoolean>;
150
+ kind: z.ZodLiteral<"select">;
151
+ options: z.ZodArray<z.ZodString>;
152
+ optionLabels: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
153
+ default: z.ZodExactOptional<z.ZodString>;
154
+ }, z.core.$strip>, ...z.ZodObject<{
155
+ key: z.ZodString;
156
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
157
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
158
+ required: z.ZodExactOptional<z.ZodBoolean>;
159
+ kind: z.ZodLiteral<"category" | "collection" | "color" | "menu" | "product" | "url">;
160
+ default: z.ZodExactOptional<z.ZodString>;
161
+ }, z.core.$strip>[], z.ZodObject<{
162
+ key: z.ZodString;
163
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
164
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
165
+ required: z.ZodExactOptional<z.ZodBoolean>;
166
+ kind: z.ZodLiteral<"image">;
167
+ }, z.core.$strip>, z.ZodObject<{
168
+ key: z.ZodString;
169
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
170
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
171
+ required: z.ZodExactOptional<z.ZodBoolean>;
172
+ kind: z.ZodLiteral<"richtext">;
173
+ formats: z.ZodExactOptional<z.ZodArray<z.ZodCustom<RichTextFormat, RichTextFormat>>>;
174
+ default: z.ZodExactOptional<z.ZodCustom<RichTextValue, RichTextValue>>;
175
+ }, z.core.$strip>], "kind">>;
176
+ min: z.ZodExactOptional<z.ZodNumber>;
177
+ max: z.ZodExactOptional<z.ZodNumber>;
178
+ default: z.ZodExactOptional<z.ZodArray<z.ZodObject<{
179
+ id: z.ZodString;
180
+ props: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./types.js").JsonValue, unknown, z.core.$ZodTypeInternals<import("./types.js").JsonValue, unknown>>>>;
181
+ }, z.core.$strip>>>;
182
+ itemLabel: z.ZodExactOptional<z.ZodString>;
102
183
  }, z.core.$strip>], "kind">>;
103
184
  }, z.core.$strip>>>;
185
+ content: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>;
186
+ preview: z.ZodDefault<z.ZodBoolean>;
104
187
  components: z.ZodRecord<z.ZodString, z.ZodObject<{
105
188
  type: z.ZodString;
106
189
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
@@ -130,6 +213,7 @@ export declare const ThemeManifestSchema: z.ZodObject<{
130
213
  default: z.ZodExactOptional<z.ZodNumber>;
131
214
  min: z.ZodExactOptional<z.ZodNumber>;
132
215
  max: z.ZodExactOptional<z.ZodNumber>;
216
+ step: z.ZodExactOptional<z.ZodNumber>;
133
217
  }, z.core.$strip>, z.ZodObject<{
134
218
  key: z.ZodString;
135
219
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
@@ -144,35 +228,106 @@ export declare const ThemeManifestSchema: z.ZodObject<{
144
228
  required: z.ZodExactOptional<z.ZodBoolean>;
145
229
  kind: z.ZodLiteral<"select">;
146
230
  options: z.ZodArray<z.ZodString>;
231
+ optionLabels: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
147
232
  default: z.ZodExactOptional<z.ZodString>;
148
- }, z.core.$strip>, z.ZodObject<{
233
+ }, z.core.$strip>, ...z.ZodObject<{
149
234
  key: z.ZodString;
150
235
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
151
236
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
152
237
  required: z.ZodExactOptional<z.ZodBoolean>;
153
- kind: z.ZodLiteral<"url">;
238
+ kind: z.ZodLiteral<"category" | "collection" | "color" | "menu" | "product" | "url">;
154
239
  default: z.ZodExactOptional<z.ZodString>;
155
- }, z.core.$strip>, z.ZodObject<{
240
+ }, z.core.$strip>[], z.ZodObject<{
156
241
  key: z.ZodString;
157
242
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
158
243
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
159
244
  required: z.ZodExactOptional<z.ZodBoolean>;
160
- kind: z.ZodLiteral<"color">;
161
- default: z.ZodExactOptional<z.ZodString>;
245
+ kind: z.ZodLiteral<"image">;
162
246
  }, z.core.$strip>, z.ZodObject<{
163
247
  key: z.ZodString;
164
248
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
165
249
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
166
250
  required: z.ZodExactOptional<z.ZodBoolean>;
167
- kind: z.ZodLiteral<"menu">;
168
- default: z.ZodExactOptional<z.ZodString>;
251
+ kind: z.ZodLiteral<"richtext">;
252
+ formats: z.ZodExactOptional<z.ZodArray<z.ZodCustom<RichTextFormat, RichTextFormat>>>;
253
+ default: z.ZodExactOptional<z.ZodCustom<RichTextValue, RichTextValue>>;
169
254
  }, z.core.$strip>, z.ZodObject<{
170
255
  key: z.ZodString;
171
256
  label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
172
257
  description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
173
258
  required: z.ZodExactOptional<z.ZodBoolean>;
174
- kind: z.ZodLiteral<"collection">;
175
- default: z.ZodExactOptional<z.ZodString>;
259
+ kind: z.ZodLiteral<"list">;
260
+ fields: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
261
+ key: z.ZodString;
262
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
263
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
264
+ required: z.ZodExactOptional<z.ZodBoolean>;
265
+ contentKey: z.ZodExactOptional<z.ZodString>;
266
+ default: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
267
+ kind: z.ZodLiteral<"text">;
268
+ }, z.core.$strip>, z.ZodObject<{
269
+ key: z.ZodString;
270
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
271
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
272
+ required: z.ZodExactOptional<z.ZodBoolean>;
273
+ contentKey: z.ZodExactOptional<z.ZodString>;
274
+ default: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
275
+ kind: z.ZodLiteral<"textarea">;
276
+ }, z.core.$strip>, z.ZodObject<{
277
+ key: z.ZodString;
278
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
279
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
280
+ required: z.ZodExactOptional<z.ZodBoolean>;
281
+ kind: z.ZodLiteral<"number">;
282
+ default: z.ZodExactOptional<z.ZodNumber>;
283
+ min: z.ZodExactOptional<z.ZodNumber>;
284
+ max: z.ZodExactOptional<z.ZodNumber>;
285
+ step: z.ZodExactOptional<z.ZodNumber>;
286
+ }, z.core.$strip>, z.ZodObject<{
287
+ key: z.ZodString;
288
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
289
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
290
+ required: z.ZodExactOptional<z.ZodBoolean>;
291
+ kind: z.ZodLiteral<"boolean">;
292
+ default: z.ZodExactOptional<z.ZodBoolean>;
293
+ }, z.core.$strip>, z.ZodObject<{
294
+ key: z.ZodString;
295
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
296
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
297
+ required: z.ZodExactOptional<z.ZodBoolean>;
298
+ kind: z.ZodLiteral<"select">;
299
+ options: z.ZodArray<z.ZodString>;
300
+ optionLabels: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>>;
301
+ default: z.ZodExactOptional<z.ZodString>;
302
+ }, z.core.$strip>, ...z.ZodObject<{
303
+ key: z.ZodString;
304
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
305
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
306
+ required: z.ZodExactOptional<z.ZodBoolean>;
307
+ kind: z.ZodLiteral<"category" | "collection" | "color" | "menu" | "product" | "url">;
308
+ default: z.ZodExactOptional<z.ZodString>;
309
+ }, z.core.$strip>[], z.ZodObject<{
310
+ key: z.ZodString;
311
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
312
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
313
+ required: z.ZodExactOptional<z.ZodBoolean>;
314
+ kind: z.ZodLiteral<"image">;
315
+ }, z.core.$strip>, z.ZodObject<{
316
+ key: z.ZodString;
317
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
318
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
319
+ required: z.ZodExactOptional<z.ZodBoolean>;
320
+ kind: z.ZodLiteral<"richtext">;
321
+ formats: z.ZodExactOptional<z.ZodArray<z.ZodCustom<RichTextFormat, RichTextFormat>>>;
322
+ default: z.ZodExactOptional<z.ZodCustom<RichTextValue, RichTextValue>>;
323
+ }, z.core.$strip>], "kind">>;
324
+ min: z.ZodExactOptional<z.ZodNumber>;
325
+ max: z.ZodExactOptional<z.ZodNumber>;
326
+ default: z.ZodExactOptional<z.ZodArray<z.ZodObject<{
327
+ id: z.ZodString;
328
+ props: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./types.js").JsonValue, unknown, z.core.$ZodTypeInternals<import("./types.js").JsonValue, unknown>>>>;
329
+ }, z.core.$strip>>>;
330
+ itemLabel: z.ZodExactOptional<z.ZodString>;
176
331
  }, z.core.$strip>], "kind">>>;
177
332
  slots: z.ZodDefault<z.ZodArray<z.ZodObject<{
178
333
  name: z.ZodString;
@@ -181,7 +336,16 @@ export declare const ThemeManifestSchema: z.ZodObject<{
181
336
  }, z.core.$strip>>>;
182
337
  capabilities: z.ZodExactOptional<z.ZodObject<{
183
338
  insertable: z.ZodExactOptional<z.ZodBoolean>;
339
+ surfaces: z.ZodExactOptional<z.ZodArray<z.ZodString>>;
184
340
  }, z.core.$strip>>;
341
+ presets: z.ZodDefault<z.ZodArray<z.ZodObject<{
342
+ key: z.ZodString;
343
+ label: z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>;
344
+ description: z.ZodExactOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
345
+ props: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodType<import("./types.js").JsonValue, unknown, z.core.$ZodTypeInternals<import("./types.js").JsonValue, unknown>>>>;
346
+ children: z.ZodExactOptional<z.ZodArray<z.ZodType<PresetInstance, unknown, z.core.$ZodTypeInternals<PresetInstance, unknown>>>>;
347
+ slots: z.ZodExactOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodType<PresetInstance, unknown, z.core.$ZodTypeInternals<PresetInstance, unknown>>>>>;
348
+ }, z.core.$strip>>>;
185
349
  }, z.core.$strip>>;
186
350
  }, z.core.$strip>;
187
351
  /**
@@ -190,3 +354,12 @@ export declare const ThemeManifestSchema: z.ZodObject<{
190
354
  * returns the manifest with an `invalid-version` issue.
191
355
  */
192
356
  export declare function validateThemeManifest(input: unknown): ThemeManifestResult;
357
+ /**
358
+ * A manifest as a catalog every data function takes — `createInstance`,
359
+ * `instantiatePreset`, the document operations, `checkPageData`,
360
+ * `validatePageData`, `resolveThemeSettings`, `checkThemeSettings` — so an
361
+ * editor or a tool works from a theme's manifest alone, never its code.
362
+ * Components carry no implementation (`component: null`): the catalog
363
+ * describes and checks; it does not render.
364
+ */
365
+ export declare function manifestCatalog(manifest: ThemeManifest): ThemeDefinition;
package/dist/manifest.js CHANGED
@@ -10,8 +10,16 @@
10
10
  */
11
11
  import { z } from "zod";
12
12
  import { validateThemeDefinition } from "./definitions.js";
13
+ import { JsonValueSchema } from "./schemas.js";
14
+ import { THEME_SDK_VERSION } from "./version.js";
13
15
  /** Version of the manifest schema and its interpretation (not the theme's). */
14
16
  export const THEME_MANIFEST_CONTRACT_VERSION = 1;
17
+ /**
18
+ * Where a built theme serves its manifest: a prerendered page at this path
19
+ * (`src/pages/wocart-theme.json.ts`), so the file is part of every build and
20
+ * a release can read it without running the theme.
21
+ */
22
+ export const THEME_MANIFEST_PATH = "/wocart-theme.json";
15
23
  const SEMVER_PATTERN = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/;
16
24
  const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
17
25
  // ── Derivation ───────────────────────────────────────────────────────────────
@@ -21,12 +29,17 @@ export function themeManifest(theme) {
21
29
  contractVersion: THEME_MANIFEST_CONTRACT_VERSION,
22
30
  key: theme.key,
23
31
  version: theme.version,
32
+ sdkVersion: THEME_SDK_VERSION,
24
33
  surfaces: (theme.surfaces ?? []).map((surface) => ({
25
34
  key: surface.key,
26
35
  label: copyData(surface.label),
36
+ ...(surface.pageType === undefined ? {} : { pageType: surface.pageType }),
37
+ ...(surface.document === undefined ? {} : { document: copyData(surface.document) }),
27
38
  })),
28
39
  locales: [...(theme.locales ?? [])],
29
40
  settings: (theme.settings ?? []).map((group) => copyData(group)),
41
+ content: copyData(theme.content ?? {}),
42
+ preview: theme.preview === true,
30
43
  components: Object.fromEntries(Object.entries(theme.components).map(([key, definition]) => [
31
44
  key,
32
45
  componentSpec(definition),
@@ -43,6 +56,7 @@ function componentSpec(definition) {
43
56
  ...(definition.capabilities === undefined
44
57
  ? {}
45
58
  : { capabilities: copyData(definition.capabilities) }),
59
+ presets: (definition.presets ?? []).map((preset) => copyData(preset)),
46
60
  };
47
61
  }
48
62
  /**
@@ -67,7 +81,7 @@ const TextFieldSchema = z.object({
67
81
  contentKey: z.exactOptional(z.string()),
68
82
  default: z.exactOptional(LocalizedTextSchema),
69
83
  });
70
- const FieldSchema = z.discriminatedUnion("kind", [
84
+ const ScalarFieldSchemas = [
71
85
  TextFieldSchema.extend({ kind: z.literal("text") }),
72
86
  TextFieldSchema.extend({ kind: z.literal("textarea") }),
73
87
  z.object({
@@ -76,6 +90,7 @@ const FieldSchema = z.discriminatedUnion("kind", [
76
90
  default: z.exactOptional(z.number()),
77
91
  min: z.exactOptional(z.number()),
78
92
  max: z.exactOptional(z.number()),
93
+ step: z.exactOptional(z.number()),
79
94
  }),
80
95
  z.object({
81
96
  ...FieldCommonSchema,
@@ -86,29 +101,45 @@ const FieldSchema = z.discriminatedUnion("kind", [
86
101
  ...FieldCommonSchema,
87
102
  kind: z.literal("select"),
88
103
  options: z.array(z.string()),
104
+ optionLabels: z.exactOptional(z.record(z.string(), LocalizedTextSchema)),
89
105
  default: z.exactOptional(z.string()),
90
106
  }),
91
- z.object({
107
+ ...["url", "color", "menu", "collection", "product", "category"].map((kind) => z.object({
92
108
  ...FieldCommonSchema,
93
- kind: z.literal("url"),
109
+ kind: z.literal(kind),
94
110
  default: z.exactOptional(z.string()),
95
- }),
111
+ })),
96
112
  z.object({
97
113
  ...FieldCommonSchema,
98
- kind: z.literal("color"),
99
- default: z.exactOptional(z.string()),
114
+ kind: z.literal("image"),
100
115
  }),
101
116
  z.object({
102
117
  ...FieldCommonSchema,
103
- kind: z.literal("menu"),
104
- default: z.exactOptional(z.string()),
118
+ kind: z.literal("richtext"),
119
+ formats: z.exactOptional(z.array(z.custom((value) => typeof value === "string"))),
120
+ // The document's own rules are relational; `validateThemeDefinition` checks them.
121
+ default: z.exactOptional(z.custom((value) => typeof value === "object" && value !== null)),
105
122
  }),
123
+ ];
124
+ const ItemFieldSchema = z.discriminatedUnion("kind", [...ScalarFieldSchemas]);
125
+ const FieldSchema = z.discriminatedUnion("kind", [
126
+ ...ScalarFieldSchemas,
106
127
  z.object({
107
128
  ...FieldCommonSchema,
108
- kind: z.literal("collection"),
109
- default: z.exactOptional(z.string()),
129
+ kind: z.literal("list"),
130
+ fields: z.array(ItemFieldSchema),
131
+ min: z.exactOptional(z.number()),
132
+ max: z.exactOptional(z.number()),
133
+ default: z.exactOptional(z.array(z.object({ id: z.string(), props: z.exactOptional(z.record(z.string(), JsonValueSchema)) }))),
134
+ itemLabel: z.exactOptional(z.string()),
110
135
  }),
111
136
  ]);
137
+ const PresetInstanceSchema = z.lazy(() => z.object({
138
+ type: z.string(),
139
+ props: z.exactOptional(z.record(z.string(), JsonValueSchema)),
140
+ children: z.exactOptional(z.array(PresetInstanceSchema)),
141
+ slots: z.exactOptional(z.record(z.string(), z.array(PresetInstanceSchema))),
142
+ }));
112
143
  const ComponentSpecSchema = z.object({
113
144
  type: z.string(),
114
145
  label: LocalizedTextSchema,
@@ -121,7 +152,20 @@ const ComponentSpecSchema = z.object({
121
152
  allow: z.exactOptional(z.array(z.string())),
122
153
  }))
123
154
  .default([]),
124
- capabilities: z.exactOptional(z.object({ insertable: z.exactOptional(z.boolean()) })),
155
+ capabilities: z.exactOptional(z.object({
156
+ insertable: z.exactOptional(z.boolean()),
157
+ surfaces: z.exactOptional(z.array(z.string())),
158
+ })),
159
+ presets: z
160
+ .array(z.object({
161
+ key: z.string(),
162
+ label: LocalizedTextSchema,
163
+ description: z.exactOptional(LocalizedTextSchema),
164
+ props: z.exactOptional(z.record(z.string(), JsonValueSchema)),
165
+ children: z.exactOptional(z.array(PresetInstanceSchema)),
166
+ slots: z.exactOptional(z.record(z.string(), z.array(PresetInstanceSchema))),
167
+ }))
168
+ .default([]),
125
169
  });
126
170
  /**
127
171
  * The manifest's structural shape. Unknown keys are stripped (the forward-compat
@@ -132,13 +176,22 @@ export const ThemeManifestSchema = z.object({
132
176
  contractVersion: z.literal(THEME_MANIFEST_CONTRACT_VERSION),
133
177
  key: z.string(),
134
178
  version: z.string(),
179
+ sdkVersion: z.exactOptional(z.string()),
135
180
  surfaces: z
136
- .array(z.object({ key: z.string(), label: LocalizedTextSchema }))
181
+ .array(z.object({
182
+ key: z.string(),
183
+ label: LocalizedTextSchema,
184
+ pageType: z.exactOptional(z.custom((value) => typeof value === "string")),
185
+ // A page document's own rules are checked by `validateThemeDefinition` on the probe.
186
+ document: z.exactOptional(z.custom((value) => isRecord(value))),
187
+ }))
137
188
  .default([]),
138
189
  locales: z.array(z.string()).default([]),
139
190
  settings: z
140
191
  .array(z.object({ key: z.string(), label: LocalizedTextSchema, fields: z.array(FieldSchema) }))
141
192
  .default([]),
193
+ content: z.record(z.string(), z.record(z.string(), z.string())).default({}),
194
+ preview: z.boolean().default(false),
142
195
  components: z.record(z.string(), ComponentSpecSchema),
143
196
  });
144
197
  // ── Validation ───────────────────────────────────────────────────────────────
@@ -168,7 +221,7 @@ export function validateThemeManifest(input) {
168
221
  return { manifest: null, issues };
169
222
  }
170
223
  const manifest = parsed.data;
171
- const issues = validateThemeDefinition(probeOf(manifest)).map((issue) => ({
224
+ const issues = validateThemeDefinition(manifestCatalog(manifest)).map((issue) => ({
172
225
  path: issue.path,
173
226
  code: issue.code === "type-mismatch" ? "type-mismatch" : codeAt(issue.path),
174
227
  message: issue.message,
@@ -198,6 +251,8 @@ function codeAt(path) {
198
251
  return "invalid-surface";
199
252
  if (path === "settings" || path.startsWith("settings["))
200
253
  return "invalid-settings";
254
+ if (path === "content" || path.startsWith("content."))
255
+ return "invalid-content";
201
256
  if (path.startsWith("components."))
202
257
  return "invalid-component";
203
258
  return "invalid-manifest";
@@ -211,16 +266,22 @@ function toPath(segments) {
211
266
  }, "");
212
267
  }
213
268
  /**
214
- * The manifest as a `ThemeDefinition` with a placeholder component, so the
215
- * assembly validator checks the relational rules without a real component.
269
+ * A manifest as a catalog every data function takes — `createInstance`,
270
+ * `instantiatePreset`, the document operations, `checkPageData`,
271
+ * `validatePageData`, `resolveThemeSettings`, `checkThemeSettings` — so an
272
+ * editor or a tool works from a theme's manifest alone, never its code.
273
+ * Components carry no implementation (`component: null`): the catalog
274
+ * describes and checks; it does not render.
216
275
  */
217
- function probeOf(manifest) {
276
+ export function manifestCatalog(manifest) {
218
277
  return {
219
278
  key: manifest.key,
220
279
  version: manifest.version,
221
280
  surfaces: manifest.surfaces,
222
281
  locales: manifest.locales,
223
282
  settings: manifest.settings,
283
+ content: manifest.content,
284
+ preview: manifest.preview,
224
285
  components: Object.fromEntries(Object.entries(manifest.components).map(([key, spec]) => [
225
286
  key,
226
287
  { ...spec, component: null },
@@ -0,0 +1,71 @@
1
+ import type { ComponentInstance, JsonValue, PageData, ThemeDefinition } from "./types.js";
2
+ export type DocumentIssueCode = "invalid-document" | "invalid-instance" | "invalid-index" | "invalid-props" | "unknown-instance" | "unknown-type" | "unknown-slot" | "duplicate-id" | "not-insertable" | "not-available" | "not-allowed" | "slot-full" | "cycle";
3
+ /** Why a document or a change is refused; `path` points into the document. */
4
+ export interface DocumentIssue {
5
+ path: string;
6
+ code: DocumentIssueCode;
7
+ message: string;
8
+ }
9
+ export type DocumentEdit = {
10
+ ok: true;
11
+ page: PageData;
12
+ } | {
13
+ ok: false;
14
+ issue: DocumentIssue;
15
+ };
16
+ /** Where an instance goes: a parent's slot (absent: the top-level sections) at an index (absent: the end). */
17
+ export interface InstancePlacement {
18
+ parent?: string;
19
+ /** Absent or `"default"`: the parent's children. */
20
+ slot?: string;
21
+ index?: number;
22
+ }
23
+ /** Where an instance is: its parent (`null` at the top level), its slot and its index there. */
24
+ export interface InstanceLocation {
25
+ instance: ComponentInstance;
26
+ parent: string | null;
27
+ slot: string | null;
28
+ index: number;
29
+ }
30
+ /**
31
+ * A new instance id: `cmp_` and 16 random hex digits. Operations refuse an id
32
+ * the document already holds, so a collision is reported, never written.
33
+ */
34
+ export declare function newInstanceId(): string;
35
+ /** Where `id` is in the document, or `null`. */
36
+ export declare function findInstance(page: PageData, id: string): InstanceLocation | null;
37
+ /**
38
+ * Insert a new instance — from `createInstance` or `instantiatePreset` — at a
39
+ * placement. Refused when the component is not insertable or not available on
40
+ * the document's surface, when the slot does not take it, or when any id is
41
+ * already in the document.
42
+ */
43
+ export declare function insertInstance(theme: ThemeDefinition, page: PageData, instance: ComponentInstance, placement?: InstancePlacement): DocumentEdit;
44
+ /** Remove an instance and everything inside it. */
45
+ export declare function removeInstance(page: PageData, id: string): DocumentEdit;
46
+ /**
47
+ * Move an instance (with everything inside it) to a placement. `index` counts
48
+ * positions in the destination after the instance has left its old place.
49
+ */
50
+ export declare function moveInstance(theme: ThemeDefinition, page: PageData, id: string, placement?: InstancePlacement): DocumentEdit;
51
+ /**
52
+ * Copy an instance and everything inside it right after the original, every
53
+ * copied instance with an id from `newId`. List items keep their ids: they
54
+ * are unique within their list, not the document.
55
+ */
56
+ export declare function duplicateInstance(theme: ThemeDefinition, page: PageData, id: string, newId: () => string): DocumentEdit;
57
+ /**
58
+ * Change an instance's props: each value is checked by its field;
59
+ * `undefined` removes the stored value, so the field falls back to its
60
+ * content-pack text or default.
61
+ */
62
+ export declare function setInstanceProps(theme: ThemeDefinition, page: PageData, id: string, changes: Readonly<Record<string, JsonValue | undefined>>): DocumentEdit;
63
+ /** Switch an instance off (kept, not rendered) or back on. */
64
+ export declare function setInstanceHidden(page: PageData, id: string, hidden: boolean): DocumentEdit;
65
+ /**
66
+ * Everything a writer must fix before saving a document for this theme —
67
+ * the strict counterpart of `validatePageData`: an envelope for one of the
68
+ * theme's surfaces when it declares any, and every section a tree the
69
+ * operations would have built. An empty list means the document may be saved.
70
+ */
71
+ export declare function checkPageData(theme: ThemeDefinition, input: unknown): DocumentIssue[];