@avocadostudio-ai/shared 0.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.
Files changed (76) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +80 -0
  3. package/contract/operation.schema.json +752 -0
  4. package/dist/api-responses.d.ts +62 -0
  5. package/dist/api-responses.js +68 -0
  6. package/dist/block-manifest.d.ts +28 -0
  7. package/dist/block-manifest.js +247 -0
  8. package/dist/block-names.d.ts +19 -0
  9. package/dist/block-names.js +52 -0
  10. package/dist/blocks/_helpers.d.ts +14 -0
  11. package/dist/blocks/_helpers.js +26 -0
  12. package/dist/blocks/_registry.d.ts +116 -0
  13. package/dist/blocks/_registry.js +271 -0
  14. package/dist/blocks/banner.d.ts +1 -0
  15. package/dist/blocks/banner.js +34 -0
  16. package/dist/blocks/card-grid.d.ts +1 -0
  17. package/dist/blocks/card-grid.js +73 -0
  18. package/dist/blocks/card.d.ts +1 -0
  19. package/dist/blocks/card.js +37 -0
  20. package/dist/blocks/carousel.d.ts +1 -0
  21. package/dist/blocks/carousel.js +51 -0
  22. package/dist/blocks/cta.d.ts +1 -0
  23. package/dist/blocks/cta.js +35 -0
  24. package/dist/blocks/embed.d.ts +1 -0
  25. package/dist/blocks/embed.js +30 -0
  26. package/dist/blocks/faq-accordion.d.ts +1 -0
  27. package/dist/blocks/faq-accordion.js +30 -0
  28. package/dist/blocks/feature-grid.d.ts +1 -0
  29. package/dist/blocks/feature-grid.js +46 -0
  30. package/dist/blocks/footer.d.ts +1 -0
  31. package/dist/blocks/footer.js +31 -0
  32. package/dist/blocks/gallery.d.ts +1 -0
  33. package/dist/blocks/gallery.js +47 -0
  34. package/dist/blocks/hero.d.ts +1 -0
  35. package/dist/blocks/hero.js +48 -0
  36. package/dist/blocks/index.d.ts +3 -0
  37. package/dist/blocks/index.js +53 -0
  38. package/dist/blocks/quote.d.ts +1 -0
  39. package/dist/blocks/quote.js +32 -0
  40. package/dist/blocks/rich-text.d.ts +1 -0
  41. package/dist/blocks/rich-text.js +41 -0
  42. package/dist/blocks/site-header.d.ts +1 -0
  43. package/dist/blocks/site-header.js +48 -0
  44. package/dist/blocks/stats.d.ts +1 -0
  45. package/dist/blocks/stats.js +42 -0
  46. package/dist/blocks/table.d.ts +1 -0
  47. package/dist/blocks/table.js +37 -0
  48. package/dist/blocks/tabs.d.ts +1 -0
  49. package/dist/blocks/tabs.js +39 -0
  50. package/dist/blocks/testimonials.d.ts +1 -0
  51. package/dist/blocks/testimonials.js +45 -0
  52. package/dist/blocks/two-column.d.ts +1 -0
  53. package/dist/blocks/two-column.js +61 -0
  54. package/dist/blocks/video.d.ts +1 -0
  55. package/dist/blocks/video.js +33 -0
  56. package/dist/chat-events.d.ts +475 -0
  57. package/dist/chat-events.js +137 -0
  58. package/dist/demo-seed-content.d.ts +3 -0
  59. package/dist/demo-seed-content.js +1128 -0
  60. package/dist/draft-mode.d.ts +10 -0
  61. package/dist/draft-mode.js +29 -0
  62. package/dist/editable-path.d.ts +20 -0
  63. package/dist/editable-path.js +101 -0
  64. package/dist/index.d.ts +13 -0
  65. package/dist/index.js +26 -0
  66. package/dist/ops/builders.d.ts +62 -0
  67. package/dist/ops/builders.js +111 -0
  68. package/dist/ops/theme-tokens.d.ts +50 -0
  69. package/dist/ops/theme-tokens.js +73 -0
  70. package/dist/protocol.d.ts +7 -0
  71. package/dist/protocol.js +7 -0
  72. package/dist/publish-diff.d.ts +67 -0
  73. package/dist/publish-diff.js +9 -0
  74. package/dist/schemas.d.ts +321 -0
  75. package/dist/schemas.js +238 -0
  76. package/package.json +48 -0
@@ -0,0 +1,321 @@
1
+ import { z } from "zod";
2
+ import { type BlockInstance } from "./blocks/_registry.ts";
3
+ export declare const siteConfigSchema: z.ZodObject<{
4
+ name: z.ZodOptional<z.ZodString>;
5
+ logo: z.ZodOptional<z.ZodString>;
6
+ purpose: z.ZodOptional<z.ZodString>;
7
+ tone: z.ZodOptional<z.ZodString>;
8
+ constraints: z.ZodOptional<z.ZodArray<z.ZodString>>;
9
+ navLabels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
10
+ navGroups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
11
+ themeOverrides: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
12
+ }, z.core.$strip>;
13
+ export type SiteConfig = z.infer<typeof siteConfigSchema>;
14
+ export type PageMeta = {
15
+ title?: string;
16
+ description?: string;
17
+ ogImage?: string;
18
+ };
19
+ export type PageDoc = {
20
+ id: string;
21
+ slug: string;
22
+ title: string;
23
+ updatedAt: string;
24
+ blocks: BlockInstance[];
25
+ meta?: PageMeta;
26
+ };
27
+ export declare const pageMetaSchema: z.ZodObject<{
28
+ title: z.ZodOptional<z.ZodString>;
29
+ description: z.ZodOptional<z.ZodString>;
30
+ ogImage: z.ZodOptional<z.ZodString>;
31
+ }, z.core.$strip>;
32
+ /** Lenient — accepts any block type (for ingesting content from sites with custom blocks). */
33
+ export declare const pageDocSchemaLenient: z.ZodType<PageDoc>;
34
+ /** Strict — rejects block types not in the shared registry. */
35
+ export declare const pageDocSchema: z.ZodType<PageDoc>;
36
+ export declare const operationSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
37
+ op: z.ZodLiteral<"create_page">;
38
+ page: z.ZodType<PageDoc, unknown, z.core.$ZodTypeInternals<PageDoc, unknown>>;
39
+ }, z.core.$strip>, z.ZodObject<{
40
+ op: z.ZodLiteral<"add_block">;
41
+ pageSlug: z.ZodString;
42
+ afterBlockId: z.ZodOptional<z.ZodString>;
43
+ block: z.ZodObject<{
44
+ id: z.ZodString;
45
+ props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
46
+ type: z.ZodString;
47
+ }, z.core.$strip>;
48
+ }, z.core.$strip>, z.ZodObject<{
49
+ op: z.ZodLiteral<"update_props">;
50
+ pageSlug: z.ZodString;
51
+ blockId: z.ZodString;
52
+ patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
53
+ }, z.core.$strip>, z.ZodObject<{
54
+ op: z.ZodLiteral<"remove_block">;
55
+ pageSlug: z.ZodString;
56
+ blockId: z.ZodString;
57
+ }, z.core.$strip>, z.ZodObject<{
58
+ op: z.ZodLiteral<"move_block">;
59
+ pageSlug: z.ZodString;
60
+ blockId: z.ZodString;
61
+ afterBlockId: z.ZodOptional<z.ZodString>;
62
+ }, z.core.$strip>, z.ZodObject<{
63
+ op: z.ZodLiteral<"duplicate_block">;
64
+ pageSlug: z.ZodString;
65
+ blockId: z.ZodString;
66
+ toPageSlug: z.ZodOptional<z.ZodString>;
67
+ newBlockId: z.ZodOptional<z.ZodString>;
68
+ afterBlockId: z.ZodOptional<z.ZodString>;
69
+ }, z.core.$strip>, z.ZodObject<{
70
+ op: z.ZodLiteral<"add_item">;
71
+ pageSlug: z.ZodString;
72
+ blockId: z.ZodString;
73
+ listKey: z.ZodString;
74
+ item: z.ZodRecord<z.ZodString, z.ZodUnknown>;
75
+ afterItemId: z.ZodOptional<z.ZodString>;
76
+ afterIndex: z.ZodOptional<z.ZodNumber>;
77
+ }, z.core.$strip>, z.ZodObject<{
78
+ op: z.ZodLiteral<"update_item">;
79
+ pageSlug: z.ZodString;
80
+ blockId: z.ZodString;
81
+ listKey: z.ZodString;
82
+ itemId: z.ZodOptional<z.ZodString>;
83
+ index: z.ZodOptional<z.ZodNumber>;
84
+ patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
85
+ }, z.core.$strip>, z.ZodObject<{
86
+ op: z.ZodLiteral<"remove_item">;
87
+ pageSlug: z.ZodString;
88
+ blockId: z.ZodString;
89
+ listKey: z.ZodString;
90
+ itemId: z.ZodOptional<z.ZodString>;
91
+ index: z.ZodOptional<z.ZodNumber>;
92
+ }, z.core.$strip>, z.ZodObject<{
93
+ op: z.ZodLiteral<"move_item">;
94
+ pageSlug: z.ZodString;
95
+ blockId: z.ZodString;
96
+ listKey: z.ZodString;
97
+ itemId: z.ZodOptional<z.ZodString>;
98
+ index: z.ZodOptional<z.ZodNumber>;
99
+ afterItemId: z.ZodOptional<z.ZodString>;
100
+ afterIndex: z.ZodOptional<z.ZodNumber>;
101
+ }, z.core.$strip>, z.ZodObject<{
102
+ op: z.ZodLiteral<"reorder_items">;
103
+ pageSlug: z.ZodString;
104
+ blockId: z.ZodString;
105
+ listKey: z.ZodString;
106
+ order: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
107
+ }, z.core.$strip>, z.ZodObject<{
108
+ op: z.ZodLiteral<"reorder_blocks">;
109
+ pageSlug: z.ZodString;
110
+ order: z.ZodArray<z.ZodString>;
111
+ }, z.core.$strip>, z.ZodObject<{
112
+ op: z.ZodLiteral<"rename_page">;
113
+ pageSlug: z.ZodString;
114
+ newPageSlug: z.ZodOptional<z.ZodString>;
115
+ newTitle: z.ZodOptional<z.ZodString>;
116
+ }, z.core.$strip>, z.ZodObject<{
117
+ op: z.ZodLiteral<"remove_page">;
118
+ pageSlug: z.ZodString;
119
+ }, z.core.$strip>, z.ZodObject<{
120
+ op: z.ZodLiteral<"move_page">;
121
+ pageSlug: z.ZodString;
122
+ afterPageSlug: z.ZodOptional<z.ZodString>;
123
+ }, z.core.$strip>, z.ZodObject<{
124
+ op: z.ZodLiteral<"duplicate_page">;
125
+ pageSlug: z.ZodString;
126
+ newPageSlug: z.ZodOptional<z.ZodString>;
127
+ newTitle: z.ZodOptional<z.ZodString>;
128
+ afterPageSlug: z.ZodOptional<z.ZodString>;
129
+ }, z.core.$strip>, z.ZodObject<{
130
+ op: z.ZodLiteral<"update_page_meta">;
131
+ pageSlug: z.ZodString;
132
+ patch: z.ZodObject<{
133
+ title: z.ZodOptional<z.ZodString>;
134
+ description: z.ZodOptional<z.ZodString>;
135
+ ogImage: z.ZodOptional<z.ZodString>;
136
+ }, z.core.$strip>;
137
+ }, z.core.$strip>, z.ZodObject<{
138
+ op: z.ZodLiteral<"update_site_config">;
139
+ patch: z.ZodObject<{
140
+ name: z.ZodOptional<z.ZodString>;
141
+ logo: z.ZodOptional<z.ZodString>;
142
+ navLabels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
143
+ navGroups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
144
+ }, z.core.$strip>;
145
+ }, z.core.$strip>, z.ZodObject<{
146
+ op: z.ZodLiteral<"update_theme">;
147
+ patch: z.ZodObject<{
148
+ brandColor: z.ZodOptional<z.ZodString>;
149
+ accentColor: z.ZodOptional<z.ZodString>;
150
+ backgroundColor: z.ZodOptional<z.ZodString>;
151
+ surfaceColor: z.ZodOptional<z.ZodString>;
152
+ headingColor: z.ZodOptional<z.ZodString>;
153
+ textColor: z.ZodOptional<z.ZodString>;
154
+ mutedTextColor: z.ZodOptional<z.ZodString>;
155
+ headingFont: z.ZodOptional<z.ZodString>;
156
+ bodyFont: z.ZodOptional<z.ZodString>;
157
+ radius: z.ZodOptional<z.ZodString>;
158
+ }, z.core.$strip>;
159
+ cssVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
160
+ }, z.core.$strip>], "op">;
161
+ export type Operation = z.infer<typeof operationSchema>;
162
+ export declare const editPlanSchema: z.ZodObject<{
163
+ intent: z.ZodEnum<{
164
+ edit_plan: "edit_plan";
165
+ needs_clarification: "needs_clarification";
166
+ content_answer: "content_answer";
167
+ }>;
168
+ summary_for_user: z.ZodString;
169
+ change_log: z.ZodArray<z.ZodString>;
170
+ ops: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
171
+ op: z.ZodLiteral<"create_page">;
172
+ page: z.ZodType<PageDoc, unknown, z.core.$ZodTypeInternals<PageDoc, unknown>>;
173
+ }, z.core.$strip>, z.ZodObject<{
174
+ op: z.ZodLiteral<"add_block">;
175
+ pageSlug: z.ZodString;
176
+ afterBlockId: z.ZodOptional<z.ZodString>;
177
+ block: z.ZodObject<{
178
+ id: z.ZodString;
179
+ props: z.ZodRecord<z.ZodString, z.ZodUnknown>;
180
+ type: z.ZodString;
181
+ }, z.core.$strip>;
182
+ }, z.core.$strip>, z.ZodObject<{
183
+ op: z.ZodLiteral<"update_props">;
184
+ pageSlug: z.ZodString;
185
+ blockId: z.ZodString;
186
+ patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
187
+ }, z.core.$strip>, z.ZodObject<{
188
+ op: z.ZodLiteral<"remove_block">;
189
+ pageSlug: z.ZodString;
190
+ blockId: z.ZodString;
191
+ }, z.core.$strip>, z.ZodObject<{
192
+ op: z.ZodLiteral<"move_block">;
193
+ pageSlug: z.ZodString;
194
+ blockId: z.ZodString;
195
+ afterBlockId: z.ZodOptional<z.ZodString>;
196
+ }, z.core.$strip>, z.ZodObject<{
197
+ op: z.ZodLiteral<"duplicate_block">;
198
+ pageSlug: z.ZodString;
199
+ blockId: z.ZodString;
200
+ toPageSlug: z.ZodOptional<z.ZodString>;
201
+ newBlockId: z.ZodOptional<z.ZodString>;
202
+ afterBlockId: z.ZodOptional<z.ZodString>;
203
+ }, z.core.$strip>, z.ZodObject<{
204
+ op: z.ZodLiteral<"add_item">;
205
+ pageSlug: z.ZodString;
206
+ blockId: z.ZodString;
207
+ listKey: z.ZodString;
208
+ item: z.ZodRecord<z.ZodString, z.ZodUnknown>;
209
+ afterItemId: z.ZodOptional<z.ZodString>;
210
+ afterIndex: z.ZodOptional<z.ZodNumber>;
211
+ }, z.core.$strip>, z.ZodObject<{
212
+ op: z.ZodLiteral<"update_item">;
213
+ pageSlug: z.ZodString;
214
+ blockId: z.ZodString;
215
+ listKey: z.ZodString;
216
+ itemId: z.ZodOptional<z.ZodString>;
217
+ index: z.ZodOptional<z.ZodNumber>;
218
+ patch: z.ZodRecord<z.ZodString, z.ZodUnknown>;
219
+ }, z.core.$strip>, z.ZodObject<{
220
+ op: z.ZodLiteral<"remove_item">;
221
+ pageSlug: z.ZodString;
222
+ blockId: z.ZodString;
223
+ listKey: z.ZodString;
224
+ itemId: z.ZodOptional<z.ZodString>;
225
+ index: z.ZodOptional<z.ZodNumber>;
226
+ }, z.core.$strip>, z.ZodObject<{
227
+ op: z.ZodLiteral<"move_item">;
228
+ pageSlug: z.ZodString;
229
+ blockId: z.ZodString;
230
+ listKey: z.ZodString;
231
+ itemId: z.ZodOptional<z.ZodString>;
232
+ index: z.ZodOptional<z.ZodNumber>;
233
+ afterItemId: z.ZodOptional<z.ZodString>;
234
+ afterIndex: z.ZodOptional<z.ZodNumber>;
235
+ }, z.core.$strip>, z.ZodObject<{
236
+ op: z.ZodLiteral<"reorder_items">;
237
+ pageSlug: z.ZodString;
238
+ blockId: z.ZodString;
239
+ listKey: z.ZodString;
240
+ order: z.ZodArray<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
241
+ }, z.core.$strip>, z.ZodObject<{
242
+ op: z.ZodLiteral<"reorder_blocks">;
243
+ pageSlug: z.ZodString;
244
+ order: z.ZodArray<z.ZodString>;
245
+ }, z.core.$strip>, z.ZodObject<{
246
+ op: z.ZodLiteral<"rename_page">;
247
+ pageSlug: z.ZodString;
248
+ newPageSlug: z.ZodOptional<z.ZodString>;
249
+ newTitle: z.ZodOptional<z.ZodString>;
250
+ }, z.core.$strip>, z.ZodObject<{
251
+ op: z.ZodLiteral<"remove_page">;
252
+ pageSlug: z.ZodString;
253
+ }, z.core.$strip>, z.ZodObject<{
254
+ op: z.ZodLiteral<"move_page">;
255
+ pageSlug: z.ZodString;
256
+ afterPageSlug: z.ZodOptional<z.ZodString>;
257
+ }, z.core.$strip>, z.ZodObject<{
258
+ op: z.ZodLiteral<"duplicate_page">;
259
+ pageSlug: z.ZodString;
260
+ newPageSlug: z.ZodOptional<z.ZodString>;
261
+ newTitle: z.ZodOptional<z.ZodString>;
262
+ afterPageSlug: z.ZodOptional<z.ZodString>;
263
+ }, z.core.$strip>, z.ZodObject<{
264
+ op: z.ZodLiteral<"update_page_meta">;
265
+ pageSlug: z.ZodString;
266
+ patch: z.ZodObject<{
267
+ title: z.ZodOptional<z.ZodString>;
268
+ description: z.ZodOptional<z.ZodString>;
269
+ ogImage: z.ZodOptional<z.ZodString>;
270
+ }, z.core.$strip>;
271
+ }, z.core.$strip>, z.ZodObject<{
272
+ op: z.ZodLiteral<"update_site_config">;
273
+ patch: z.ZodObject<{
274
+ name: z.ZodOptional<z.ZodString>;
275
+ logo: z.ZodOptional<z.ZodString>;
276
+ navLabels: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
277
+ navGroups: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
278
+ }, z.core.$strip>;
279
+ }, z.core.$strip>, z.ZodObject<{
280
+ op: z.ZodLiteral<"update_theme">;
281
+ patch: z.ZodObject<{
282
+ brandColor: z.ZodOptional<z.ZodString>;
283
+ accentColor: z.ZodOptional<z.ZodString>;
284
+ backgroundColor: z.ZodOptional<z.ZodString>;
285
+ surfaceColor: z.ZodOptional<z.ZodString>;
286
+ headingColor: z.ZodOptional<z.ZodString>;
287
+ textColor: z.ZodOptional<z.ZodString>;
288
+ mutedTextColor: z.ZodOptional<z.ZodString>;
289
+ headingFont: z.ZodOptional<z.ZodString>;
290
+ bodyFont: z.ZodOptional<z.ZodString>;
291
+ radius: z.ZodOptional<z.ZodString>;
292
+ }, z.core.$strip>;
293
+ cssVars: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
294
+ }, z.core.$strip>], "op">>;
295
+ suggested_next_actions: z.ZodOptional<z.ZodArray<z.ZodString>>;
296
+ }, z.core.$strip>;
297
+ export type EditPlan = z.infer<typeof editPlanSchema>;
298
+ export type PatchRejectReason = "version_mismatch" | "apply_error" | "unknown_op";
299
+ export type ApplyPatchMessage = {
300
+ type: "applyPatch";
301
+ txId: string;
302
+ op: Operation;
303
+ fromVersion: number;
304
+ toVersion: number;
305
+ focusBlockId?: string;
306
+ };
307
+ export type PatchAckMessage = {
308
+ type: "patchAck";
309
+ txId: string;
310
+ accepted: boolean;
311
+ reason?: PatchRejectReason;
312
+ };
313
+ export type ResetToServerMessage = {
314
+ type: "resetToServer";
315
+ toVersion: number;
316
+ focusBlockId?: string;
317
+ };
318
+ export declare function demoPublishedPages(): PageDoc[];
319
+ /** Demo site header config (name, logo, nav groups/labels) — paired with
320
+ * `demoPublishedPages()` so the editor's demo header matches the live site. */
321
+ export declare function demoSiteConfig(): SiteConfig;
@@ -0,0 +1,238 @@
1
+ import { z } from "zod";
2
+ import { blockInstanceSchema, blockInstanceSchemaLenient } from "./blocks/_registry.js";
3
+ import { semanticThemeTokensSchema } from "./ops/theme-tokens.js";
4
+ import { DEMO_SEED_PAGES, DEMO_SEED_SITE_CONFIG } from "./demo-seed-content.js";
5
+ // ---------------------------------------------------------------------------
6
+ // Site config
7
+ // ---------------------------------------------------------------------------
8
+ export const siteConfigSchema = z.object({
9
+ name: z.string().optional(),
10
+ logo: z.string().optional(),
11
+ purpose: z.string().optional(), // What the site is about — used as AI context for editing
12
+ tone: z.string().optional(), // Voice/tone guide for AI-generated content
13
+ constraints: z.array(z.string()).optional(), // Content rules the AI must follow (e.g. "Never mention competitor X")
14
+ navLabels: z.record(z.string(), z.string()).optional(), // slug → custom label, e.g. { "/pricing": "Plans & Pricing" }
15
+ navGroups: z.record(z.string(), z.array(z.string())).optional(), // parent label → child slugs, e.g. { "Products": ["/bananas", "/cherries"] }
16
+ themeOverrides: z.record(z.string(), z.string()).optional(), // CSS variable overrides, e.g. { "--brand": "#2563eb" }
17
+ });
18
+ export const pageMetaSchema = z.object({
19
+ title: z.string().optional(),
20
+ description: z.string().optional(),
21
+ ogImage: z.string().optional()
22
+ });
23
+ const pageDocFields = {
24
+ id: z.string().min(1),
25
+ slug: z.string().min(1),
26
+ title: z.string().min(1),
27
+ updatedAt: z.string().min(1),
28
+ meta: pageMetaSchema.optional()
29
+ };
30
+ /** Lenient — accepts any block type (for ingesting content from sites with custom blocks). */
31
+ export const pageDocSchemaLenient = z.object({
32
+ ...pageDocFields,
33
+ blocks: z.array(blockInstanceSchemaLenient),
34
+ });
35
+ /** Strict — rejects block types not in the shared registry. */
36
+ export const pageDocSchema = z.object({
37
+ ...pageDocFields,
38
+ blocks: z.array(blockInstanceSchema),
39
+ });
40
+ // ---------------------------------------------------------------------------
41
+ // Operation schemas
42
+ // ---------------------------------------------------------------------------
43
+ const createPageSchema = z.object({
44
+ op: z.literal("create_page"),
45
+ page: pageDocSchema
46
+ });
47
+ const addBlockSchema = z.object({
48
+ op: z.literal("add_block"),
49
+ pageSlug: z.string().min(1),
50
+ afterBlockId: z.string().min(1).optional(),
51
+ block: blockInstanceSchema
52
+ });
53
+ const updatePropsSchema = z.object({
54
+ op: z.literal("update_props"),
55
+ pageSlug: z.string().min(1),
56
+ blockId: z.string().min(1),
57
+ patch: z.record(z.string(), z.unknown())
58
+ });
59
+ const removeBlockSchema = z.object({
60
+ op: z.literal("remove_block"),
61
+ pageSlug: z.string().min(1),
62
+ blockId: z.string().min(1)
63
+ });
64
+ const moveBlockSchema = z.object({
65
+ op: z.literal("move_block"),
66
+ pageSlug: z.string().min(1),
67
+ blockId: z.string().min(1),
68
+ afterBlockId: z.string().min(1).optional()
69
+ });
70
+ const duplicateBlockSchema = z.object({
71
+ op: z.literal("duplicate_block"),
72
+ pageSlug: z.string().min(1),
73
+ blockId: z.string().min(1),
74
+ toPageSlug: z.string().min(1).optional(),
75
+ newBlockId: z.string().min(1).optional(),
76
+ afterBlockId: z.string().min(1).optional()
77
+ });
78
+ // Item ops address a list entry by its stable `itemId` (preferred — every list
79
+ // item carries an `id`, backfilled on load like block ids) or by positional
80
+ // `index` (fallback for direct-manipulation clients like the editor's preview
81
+ // bridge, where a single rendered element's position is reliable). The engine
82
+ // requires at least one — and prefers itemId, which is robust across compound
83
+ // plans where a numeric index reindexes mid-plan. `afterItemId`/`afterIndex`
84
+ // anchor inserts/moves the same way (omit ⇒ append). At-least-one is enforced in
85
+ // ops-engine.ts (a discriminated-union member can't carry a .refine()).
86
+ const addItemSchema = z.object({
87
+ op: z.literal("add_item"),
88
+ pageSlug: z.string().min(1),
89
+ blockId: z.string().min(1),
90
+ listKey: z.string().min(1),
91
+ item: z.record(z.string(), z.unknown()),
92
+ afterItemId: z.string().min(1).optional(),
93
+ afterIndex: z.number().int().min(0).optional()
94
+ });
95
+ const updateItemSchema = z.object({
96
+ op: z.literal("update_item"),
97
+ pageSlug: z.string().min(1),
98
+ blockId: z.string().min(1),
99
+ listKey: z.string().min(1),
100
+ itemId: z.string().min(1).optional(),
101
+ index: z.number().int().min(0).optional(),
102
+ patch: z.record(z.string(), z.unknown())
103
+ });
104
+ const removeItemSchema = z.object({
105
+ op: z.literal("remove_item"),
106
+ pageSlug: z.string().min(1),
107
+ blockId: z.string().min(1),
108
+ listKey: z.string().min(1),
109
+ itemId: z.string().min(1).optional(),
110
+ index: z.number().int().min(0).optional()
111
+ });
112
+ const moveItemSchema = z.object({
113
+ op: z.literal("move_item"),
114
+ pageSlug: z.string().min(1),
115
+ blockId: z.string().min(1),
116
+ listKey: z.string().min(1),
117
+ itemId: z.string().min(1).optional(),
118
+ index: z.number().int().min(0).optional(),
119
+ afterItemId: z.string().min(1).optional(),
120
+ afterIndex: z.number().int().min(0).optional()
121
+ });
122
+ // Declarative reorders: the caller states the FINAL order and the engine
123
+ // computes the moves — one atomic op instead of N move ops with shifting
124
+ // anchors (which planners reliably get wrong). `order` must cover the whole
125
+ // list exactly once. reorder_items entries are stable item ids (strings) or
126
+ // current 0-based indexes (numbers — the only option for lists whose entries
127
+ // can't carry ids, e.g. Table rows). reorder_blocks entries are block ids;
128
+ // chrome blocks (SiteHeader/Footer) are pinned and must be omitted.
129
+ const reorderItemsSchema = z.object({
130
+ op: z.literal("reorder_items"),
131
+ pageSlug: z.string().min(1),
132
+ blockId: z.string().min(1),
133
+ listKey: z.string().min(1),
134
+ order: z.array(z.union([z.string().min(1), z.number().int().min(0)])).min(1)
135
+ });
136
+ const reorderBlocksSchema = z.object({
137
+ op: z.literal("reorder_blocks"),
138
+ pageSlug: z.string().min(1),
139
+ order: z.array(z.string().min(1)).min(1)
140
+ });
141
+ // newPageSlug and newTitle are both optional here, but the ops-engine handler
142
+ // requires at least one of them to produce an effective change. Enforcing that
143
+ // with .refine() would turn this into ZodEffects and break the discriminated
144
+ // union below, so the check lives in ops-engine.ts alongside the value-level
145
+ // comparisons (current vs incoming).
146
+ const renamePageSchema = z.object({
147
+ op: z.literal("rename_page"),
148
+ pageSlug: z.string().min(1),
149
+ newPageSlug: z.string().min(1).optional(),
150
+ newTitle: z.string().min(1).optional()
151
+ });
152
+ const removePageSchema = z.object({
153
+ op: z.literal("remove_page"),
154
+ pageSlug: z.string().min(1)
155
+ });
156
+ const movePageSchema = z.object({
157
+ op: z.literal("move_page"),
158
+ pageSlug: z.string().min(1),
159
+ afterPageSlug: z.string().min(1).optional()
160
+ });
161
+ const duplicatePageSchema = z.object({
162
+ op: z.literal("duplicate_page"),
163
+ pageSlug: z.string().min(1),
164
+ newPageSlug: z.string().min(1).optional(),
165
+ newTitle: z.string().min(1).optional(),
166
+ afterPageSlug: z.string().min(1).optional()
167
+ });
168
+ const updatePageMetaSchema = z.object({
169
+ op: z.literal("update_page_meta"),
170
+ pageSlug: z.string().min(1),
171
+ patch: z.object({
172
+ title: z.string().optional(),
173
+ description: z.string().optional(),
174
+ ogImage: z.string().optional()
175
+ })
176
+ });
177
+ const updateSiteConfigSchema = z.object({
178
+ op: z.literal("update_site_config"),
179
+ patch: z.object({
180
+ name: z.string().optional(),
181
+ logo: z.string().optional(),
182
+ navLabels: z.record(z.string(), z.string()).optional(),
183
+ navGroups: z.record(z.string(), z.array(z.string())).optional(),
184
+ })
185
+ });
186
+ // Visual theme changes (colors, fonts, corner radius). `patch` is a semantic
187
+ // token map (brandColor, headingFont, radius, …) expanded to CSS custom
188
+ // properties by the ops engine; `cssVars` is a raw `--var` → value escape hatch
189
+ // merged after the semantic expansion (raw wins). Both are merge-patches over
190
+ // the site's existing themeOverrides; set any value to "" to clear that override.
191
+ const updateThemeSchema = z.object({
192
+ op: z.literal("update_theme"),
193
+ patch: semanticThemeTokensSchema,
194
+ cssVars: z.record(z.string(), z.string()).optional()
195
+ });
196
+ export const operationSchema = z.discriminatedUnion("op", [
197
+ createPageSchema,
198
+ addBlockSchema,
199
+ updatePropsSchema,
200
+ removeBlockSchema,
201
+ moveBlockSchema,
202
+ duplicateBlockSchema,
203
+ addItemSchema,
204
+ updateItemSchema,
205
+ removeItemSchema,
206
+ moveItemSchema,
207
+ reorderItemsSchema,
208
+ reorderBlocksSchema,
209
+ renamePageSchema,
210
+ removePageSchema,
211
+ movePageSchema,
212
+ duplicatePageSchema,
213
+ updatePageMetaSchema,
214
+ updateSiteConfigSchema,
215
+ updateThemeSchema
216
+ ]);
217
+ export const editPlanSchema = z.object({
218
+ intent: z.enum(["edit_plan", "needs_clarification", "content_answer"]),
219
+ summary_for_user: z.string().min(1),
220
+ change_log: z.array(z.string()),
221
+ ops: z.array(operationSchema),
222
+ suggested_next_actions: z.array(z.string()).max(5).optional()
223
+ });
224
+ // ---------------------------------------------------------------------------
225
+ // Demo data
226
+ // ---------------------------------------------------------------------------
227
+ export function demoPublishedPages() {
228
+ const now = new Date().toISOString();
229
+ // Seed every fresh editor demo session with a deep copy of the live
230
+ // avocado-site.vercel.app content (see demo-seed-content.ts), stamping a fresh
231
+ // `updatedAt` so version/freshness logic behaves like a brand-new draft.
232
+ return DEMO_SEED_PAGES.map((page) => ({ ...structuredClone(page), updatedAt: now }));
233
+ }
234
+ /** Demo site header config (name, logo, nav groups/labels) — paired with
235
+ * `demoPublishedPages()` so the editor's demo header matches the live site. */
236
+ export function demoSiteConfig() {
237
+ return structuredClone(DEMO_SEED_SITE_CONFIG);
238
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@avocadostudio-ai/shared",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/avocadostudio-ai/avocado.git",
10
+ "directory": "packages/shared"
11
+ },
12
+ "publishConfig": {
13
+ "registry": "https://registry.npmjs.org",
14
+ "access": "public"
15
+ },
16
+ "files": [
17
+ "dist",
18
+ "contract"
19
+ ],
20
+ "dependencies": {
21
+ "zod": "^4.3.6"
22
+ },
23
+ "devDependencies": {
24
+ "tsx": "^4.21.0",
25
+ "typescript": "^5.7.3"
26
+ },
27
+ "description": "Shared Zod schemas, block registry, and operation types for Avocado Studio",
28
+ "license": "Apache-2.0",
29
+ "homepage": "https://github.com/avocadostudio-ai/avocado/tree/main/packages/shared#readme",
30
+ "bugs": {
31
+ "url": "https://github.com/avocadostudio-ai/avocado/issues"
32
+ },
33
+ "exports": {
34
+ ".": {
35
+ "types": "./dist/index.d.ts",
36
+ "import": "./dist/index.js"
37
+ },
38
+ "./contract/*": "./contract/*",
39
+ "./package.json": "./package.json"
40
+ },
41
+ "scripts": {
42
+ "build": "tsc -p tsconfig.build.json",
43
+ "typecheck": "tsc --noEmit",
44
+ "test": "NODE_ENV=test tsx --test src/**/*.test.ts",
45
+ "gen:contract": "tsx scripts/gen-contract.ts",
46
+ "gen:contract:check": "tsx scripts/gen-contract.ts --check"
47
+ }
48
+ }