@avocadostudio-ai/shared 0.4.0 → 0.5.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/dist/block-manifest.d.ts +18 -0
- package/dist/block-manifest.js +8 -1
- package/dist/blocks/_registry.d.ts +73 -1
- package/dist/blocks/_registry.js +152 -8
- package/dist/blocks/banner.js +2 -2
- package/dist/blocks/card-grid.js +2 -2
- package/dist/blocks/card.js +2 -2
- package/dist/blocks/carousel.js +2 -2
- package/dist/blocks/cta.js +2 -2
- package/dist/blocks/embed.js +2 -2
- package/dist/blocks/faq-accordion.js +2 -2
- package/dist/blocks/feature-grid.js +2 -2
- package/dist/blocks/footer.js +2 -2
- package/dist/blocks/gallery.js +2 -2
- package/dist/blocks/hero.js +2 -2
- package/dist/blocks/quote.js +2 -2
- package/dist/blocks/rich-text.js +2 -2
- package/dist/blocks/site-header.js +2 -2
- package/dist/blocks/stats.js +2 -2
- package/dist/blocks/table.js +2 -2
- package/dist/blocks/tabs.js +2 -2
- package/dist/blocks/testimonials.js +2 -2
- package/dist/blocks/two-column.js +2 -2
- package/dist/blocks/video.js +2 -2
- package/dist/editable-coverage.js +36 -2
- package/dist/editor-block-meta.d.ts +70 -0
- package/dist/editor-block-meta.js +131 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.js +4 -2
- package/dist/ops/builders.d.ts +34 -0
- package/dist/ops/builders.js +63 -0
- package/dist/panel-coverage.d.ts +105 -0
- package/dist/panel-coverage.js +395 -0
- package/package.json +2 -2
package/dist/block-manifest.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export declare const fieldMetaSchema: z.ZodObject<{
|
|
|
15
15
|
imageAlt: "imageAlt";
|
|
16
16
|
color: "color";
|
|
17
17
|
headingLevel: "headingLevel";
|
|
18
|
+
reference: "reference";
|
|
18
19
|
}>;
|
|
19
20
|
label: z.ZodOptional<z.ZodString>;
|
|
20
21
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -34,6 +35,7 @@ export declare const fieldMetaSchema: z.ZodObject<{
|
|
|
34
35
|
}>>;
|
|
35
36
|
}, z.core.$strip>>;
|
|
36
37
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
38
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
37
39
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
38
40
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
39
41
|
name: z.ZodString;
|
|
@@ -57,6 +59,7 @@ export declare const listFieldMetaSchema: z.ZodObject<{
|
|
|
57
59
|
imageAlt: "imageAlt";
|
|
58
60
|
color: "color";
|
|
59
61
|
headingLevel: "headingLevel";
|
|
62
|
+
reference: "reference";
|
|
60
63
|
}>;
|
|
61
64
|
label: z.ZodOptional<z.ZodString>;
|
|
62
65
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -76,6 +79,7 @@ export declare const listFieldMetaSchema: z.ZodObject<{
|
|
|
76
79
|
}>>;
|
|
77
80
|
}, z.core.$strip>>;
|
|
78
81
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
82
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
79
83
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
80
84
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
81
85
|
name: z.ZodString;
|
|
@@ -98,6 +102,7 @@ export declare const listFieldMetaSchema: z.ZodObject<{
|
|
|
98
102
|
imageAlt: "imageAlt";
|
|
99
103
|
color: "color";
|
|
100
104
|
headingLevel: "headingLevel";
|
|
105
|
+
reference: "reference";
|
|
101
106
|
}>;
|
|
102
107
|
label: z.ZodOptional<z.ZodString>;
|
|
103
108
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -117,6 +122,7 @@ export declare const listFieldMetaSchema: z.ZodObject<{
|
|
|
117
122
|
}>>;
|
|
118
123
|
}, z.core.$strip>>;
|
|
119
124
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
125
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
120
126
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
121
127
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
122
128
|
name: z.ZodString;
|
|
@@ -145,6 +151,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
145
151
|
imageAlt: "imageAlt";
|
|
146
152
|
color: "color";
|
|
147
153
|
headingLevel: "headingLevel";
|
|
154
|
+
reference: "reference";
|
|
148
155
|
}>;
|
|
149
156
|
label: z.ZodOptional<z.ZodString>;
|
|
150
157
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -164,6 +171,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
164
171
|
}>>;
|
|
165
172
|
}, z.core.$strip>>;
|
|
166
173
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
174
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
167
175
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
168
176
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
169
177
|
name: z.ZodString;
|
|
@@ -187,6 +195,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
187
195
|
imageAlt: "imageAlt";
|
|
188
196
|
color: "color";
|
|
189
197
|
headingLevel: "headingLevel";
|
|
198
|
+
reference: "reference";
|
|
190
199
|
}>;
|
|
191
200
|
label: z.ZodOptional<z.ZodString>;
|
|
192
201
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -206,6 +215,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
206
215
|
}>>;
|
|
207
216
|
}, z.core.$strip>>;
|
|
208
217
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
218
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
209
219
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
210
220
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
211
221
|
name: z.ZodString;
|
|
@@ -228,6 +238,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
228
238
|
imageAlt: "imageAlt";
|
|
229
239
|
color: "color";
|
|
230
240
|
headingLevel: "headingLevel";
|
|
241
|
+
reference: "reference";
|
|
231
242
|
}>;
|
|
232
243
|
label: z.ZodOptional<z.ZodString>;
|
|
233
244
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -247,6 +258,7 @@ export declare const blockDefinitionSchema: z.ZodObject<{
|
|
|
247
258
|
}>>;
|
|
248
259
|
}, z.core.$strip>>;
|
|
249
260
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
261
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
250
262
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
251
263
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
252
264
|
name: z.ZodString;
|
|
@@ -281,6 +293,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
281
293
|
imageAlt: "imageAlt";
|
|
282
294
|
color: "color";
|
|
283
295
|
headingLevel: "headingLevel";
|
|
296
|
+
reference: "reference";
|
|
284
297
|
}>;
|
|
285
298
|
label: z.ZodOptional<z.ZodString>;
|
|
286
299
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -300,6 +313,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
300
313
|
}>>;
|
|
301
314
|
}, z.core.$strip>>;
|
|
302
315
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
316
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
303
317
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
304
318
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
305
319
|
name: z.ZodString;
|
|
@@ -323,6 +337,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
323
337
|
imageAlt: "imageAlt";
|
|
324
338
|
color: "color";
|
|
325
339
|
headingLevel: "headingLevel";
|
|
340
|
+
reference: "reference";
|
|
326
341
|
}>;
|
|
327
342
|
label: z.ZodOptional<z.ZodString>;
|
|
328
343
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -342,6 +357,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
342
357
|
}>>;
|
|
343
358
|
}, z.core.$strip>>;
|
|
344
359
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
360
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
345
361
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
346
362
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
347
363
|
name: z.ZodString;
|
|
@@ -364,6 +380,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
364
380
|
imageAlt: "imageAlt";
|
|
365
381
|
color: "color";
|
|
366
382
|
headingLevel: "headingLevel";
|
|
383
|
+
reference: "reference";
|
|
367
384
|
}>;
|
|
368
385
|
label: z.ZodOptional<z.ZodString>;
|
|
369
386
|
inlineEditable: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -383,6 +400,7 @@ export declare const blockManifestSchema: z.ZodObject<{
|
|
|
383
400
|
}>>;
|
|
384
401
|
}, z.core.$strip>>;
|
|
385
402
|
multiline: z.ZodOptional<z.ZodBoolean>;
|
|
403
|
+
referenceLabelKey: z.ZodOptional<z.ZodString>;
|
|
386
404
|
inline: z.ZodOptional<z.ZodBoolean>;
|
|
387
405
|
decorators: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
388
406
|
name: z.ZodString;
|
package/dist/block-manifest.js
CHANGED
|
@@ -36,7 +36,7 @@ export const jsonSchemaLikeSchema = z.lazy(() => z.object({
|
|
|
36
36
|
*/
|
|
37
37
|
const FIELD_KINDS = [
|
|
38
38
|
"text", "richtext", "url", "link", "file", "image", "imageAlt",
|
|
39
|
-
"enum", "color", "number", "boolean", "headingLevel"
|
|
39
|
+
"enum", "color", "number", "boolean", "headingLevel", "reference"
|
|
40
40
|
];
|
|
41
41
|
const _everyKindListed = true;
|
|
42
42
|
void _everyKindListed;
|
|
@@ -54,6 +54,13 @@ export const fieldMetaSchema = z.object({
|
|
|
54
54
|
})
|
|
55
55
|
.optional(),
|
|
56
56
|
multiline: z.boolean().optional(),
|
|
57
|
+
/*
|
|
58
|
+
* Travels with the manifest because the panel is what reads it, and the panel
|
|
59
|
+
* is in a different process. A key this schema omits is stripped here, not
|
|
60
|
+
* rejected — so a reference would have arrived in the editor with nothing to
|
|
61
|
+
* label it and no sign that anything had been declared.
|
|
62
|
+
*/
|
|
63
|
+
referenceLabelKey: z.string().optional(),
|
|
57
64
|
inline: z.boolean().optional(),
|
|
58
65
|
decorators: z
|
|
59
66
|
.array(z.object({ name: z.string().min(1), title: z.string().optional() }))
|
|
@@ -18,8 +18,27 @@ export declare function isImagePlaceholder(url: string | undefined | null): bool
|
|
|
18
18
|
* against nothing at all; a `file` can be checked against what the site's asset
|
|
19
19
|
* store actually holds, which is the only way anyone finds out that a menu link
|
|
20
20
|
* has been pointing at a filename with a typo in it since August.
|
|
21
|
+
*
|
|
22
|
+
* `reference` is a *pointer the CMS owns*, and it is not a flavour of `link`.
|
|
23
|
+
* A Storyblok story link is `{ linktype: "story", id: <uuid>, cached_url: "faq" }`
|
|
24
|
+
* and the Delivery API renders it per language — `/faq` on the German page,
|
|
25
|
+
* `/fr/faq` on the French one. Neither string is what the document holds.
|
|
26
|
+
* Contentful entry links and Sanity references have the same shape and the same
|
|
27
|
+
* problem. Declare one as `link` and the projection stops being invertible in
|
|
28
|
+
* both directions at once: the diff reports every reference on every page as
|
|
29
|
+
* changed because the rendered href never equals the stored object, and writing
|
|
30
|
+
* the href back replaces the reference with a hard-coded URL, which silently
|
|
31
|
+
* stops following renames — the one thing the reference was for.
|
|
32
|
+
*
|
|
33
|
+
* So the panel shows it and does not edit it. The value is carried through
|
|
34
|
+
* untouched, the way `avocadoUnknownBlock` carries a rich-text node the pivot
|
|
35
|
+
* cannot model, and `referenceLabelKey` says where to find something a person
|
|
36
|
+
* can read. Re-pointing a reference needs a picker that knows the CMS's own
|
|
37
|
+
* document ids, which only the integration has; until it exists, showing the
|
|
38
|
+
* target and refusing the edit is the honest answer and the corrupting one is
|
|
39
|
+
* a text input.
|
|
21
40
|
*/
|
|
22
|
-
export type FieldKind = "text" | "richtext" | "url" | "link" | "file" | "image" | "imageAlt" | "enum" | "color" | "number" | "boolean" | "headingLevel";
|
|
41
|
+
export type FieldKind = "text" | "richtext" | "url" | "link" | "file" | "image" | "imageAlt" | "enum" | "color" | "number" | "boolean" | "headingLevel" | "reference";
|
|
23
42
|
/** Recommended image dimensions for an image field. */
|
|
24
43
|
export type ImageSpec = {
|
|
25
44
|
aspectRatio: "landscape" | "square" | "portrait";
|
|
@@ -76,6 +95,38 @@ export type FieldMeta = {
|
|
|
76
95
|
}[];
|
|
77
96
|
/** Whether the field is required. Auto-derived from Zod schema at registration. */
|
|
78
97
|
required?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* This field is edited in the property panel and nowhere else, by design.
|
|
100
|
+
*
|
|
101
|
+
* Not every editable prop is *drawn* as an element. A `sectionId` becomes an
|
|
102
|
+
* HTML `id`; a video's `poster` and an input's `placeholder` are attributes;
|
|
103
|
+
* a form field's `key` and `type` are configuration. None of them has an
|
|
104
|
+
* element that could carry `data-editable-target`, so the preview cannot
|
|
105
|
+
* offer them and the panel is the whole of their UI.
|
|
106
|
+
*
|
|
107
|
+
* `editableCoverage` takes this as "no marker expected", which is what makes
|
|
108
|
+
* the number mean something: without it a correct integration reports 88%
|
|
109
|
+
* and the integrator gating on it has to hard-code a magic threshold, at
|
|
110
|
+
* which point a real regression that drops one marker while a new field adds
|
|
111
|
+
* another slips through. With it, 100% is reachable and anything less is
|
|
112
|
+
* actionable.
|
|
113
|
+
*
|
|
114
|
+
* `inlineEditable: false` is a *different* statement, and narrower: it says a
|
|
115
|
+
* text field is not typed into on the page, and it says nothing about images.
|
|
116
|
+
* Use this one when there is no element at all.
|
|
117
|
+
*/
|
|
118
|
+
panelOnly?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* For `kind: "reference"`: the key inside the value holding something a
|
|
121
|
+
* person can read.
|
|
122
|
+
*
|
|
123
|
+
* Optional, because the common spellings are tried anyway — Storyblok's
|
|
124
|
+
* `cached_url`, a `slug`, a `title`, a `name`. Set it when the reference
|
|
125
|
+
* carries a readable key under a name nothing would guess, or when it carries
|
|
126
|
+
* several and the wrong one wins. Purely a display concern: it never selects
|
|
127
|
+
* what is written, because a reference is not written here at all.
|
|
128
|
+
*/
|
|
129
|
+
referenceLabelKey?: string;
|
|
79
130
|
};
|
|
80
131
|
/** Metadata for list-type props (features, items, cards). */
|
|
81
132
|
export type ListFieldMeta = {
|
|
@@ -116,6 +167,14 @@ export type BlockRegistration = {
|
|
|
116
167
|
};
|
|
117
168
|
/** Test seam: forget what has already been warned about. */
|
|
118
169
|
export declare function resetListFieldWarnings(): void;
|
|
170
|
+
/** Whether `type` is currently backed by Avocado's own definition. */
|
|
171
|
+
export declare function isBuiltinBlock(type: string): boolean;
|
|
172
|
+
/**
|
|
173
|
+
* Register one of Avocado's own blocks. Internal — a site uses `registerBlock`,
|
|
174
|
+
* and registering over a built-in name deliberately drops the built-in flag
|
|
175
|
+
* with the built-in schema.
|
|
176
|
+
*/
|
|
177
|
+
export declare function registerBuiltinBlock(type: string, config: BlockRegistration): void;
|
|
119
178
|
export declare function registerBlock(type: string, config: BlockRegistration): void;
|
|
120
179
|
/** Get metadata for a registered block type, or undefined. */
|
|
121
180
|
export declare function getBlockMeta(type: string): BlockMeta | undefined;
|
|
@@ -205,6 +264,19 @@ export declare function isInBlockCatalogue(type: string): boolean;
|
|
|
205
264
|
export declare function catalogueBlockTypes(): string[];
|
|
206
265
|
/** Declared types with no registration behind them — a typo, or a missing import. */
|
|
207
266
|
export declare function undeclaredBlockTypes(): string[];
|
|
267
|
+
/**
|
|
268
|
+
* Something a person can read out of a reference value.
|
|
269
|
+
*
|
|
270
|
+
* A reference is opaque by design — the panel shows it and never rewrites it —
|
|
271
|
+
* but "opaque" must not mean `[object Object]` in the one place a person looks
|
|
272
|
+
* to check which document a CTA points at. Storyblok spells the readable half
|
|
273
|
+
* `cached_url`, Contentful puts a `slug` or `title` on the resolved entry,
|
|
274
|
+
* Sanity hands over a bare `_ref`. Falls back to the id, and then to nothing,
|
|
275
|
+
* which the panel renders as "not set" rather than as a broken label.
|
|
276
|
+
*
|
|
277
|
+
* `referenceLabelKey` wins when the meta declares it.
|
|
278
|
+
*/
|
|
279
|
+
export declare function referenceLabel(value: unknown, labelKey?: string): string;
|
|
208
280
|
export declare function getPropDisplayName(blockType: string | undefined, propKey: string): string;
|
|
209
281
|
export declare function defaultListItemForBlock(type: BlockType, listKey: string): Record<string, unknown> | null;
|
|
210
282
|
/** Base schema — accepts any block type. Used for ingesting external site content with custom blocks. */
|
package/dist/blocks/_registry.js
CHANGED
|
@@ -68,6 +68,72 @@ function unwrapZod(schema) {
|
|
|
68
68
|
}
|
|
69
69
|
return current;
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* The object shapes a list's rows can take.
|
|
73
|
+
*
|
|
74
|
+
* A monomorphic list has one. A polymorphic list — `z.discriminatedUnion`, the
|
|
75
|
+
* shape our own docs tell an integrator to use when rows differ — has one per
|
|
76
|
+
* branch and no `.shape` of its own. Reading `.element.shape` and stopping there
|
|
77
|
+
* declared every correct polymorphic list unbacked, warned about it at startup,
|
|
78
|
+
* and prescribed `z.array(z.object({ … }))`: the one edit that really would
|
|
79
|
+
* break it, because collapsing the union is what stops each row narrowing to its
|
|
80
|
+
* own field set. The panel never used this path — it reads the JSON Schema,
|
|
81
|
+
* where the branches survive as `anyOf` — so the lists worked exactly as
|
|
82
|
+
* declared while their author was being told they could not.
|
|
83
|
+
*/
|
|
84
|
+
function listElementBranches(element) {
|
|
85
|
+
const unwrapped = unwrapZod(element);
|
|
86
|
+
if (!unwrapped)
|
|
87
|
+
return undefined;
|
|
88
|
+
const ownShape = unwrapped.shape;
|
|
89
|
+
if (ownShape)
|
|
90
|
+
return [{ discriminantValues: [], shape: ownShape }];
|
|
91
|
+
const def = unwrapped.def ?? unwrapped._def;
|
|
92
|
+
const options = def?.options;
|
|
93
|
+
if (!Array.isArray(options) || options.length === 0)
|
|
94
|
+
return undefined;
|
|
95
|
+
const discriminator = def?.discriminator;
|
|
96
|
+
const branches = [];
|
|
97
|
+
for (const option of options) {
|
|
98
|
+
const optionShape = unwrapZod(option)?.shape;
|
|
99
|
+
if (!optionShape)
|
|
100
|
+
continue;
|
|
101
|
+
branches.push({
|
|
102
|
+
discriminantValues: discriminator ? literalValuesOf(optionShape[discriminator]) : [],
|
|
103
|
+
shape: optionShape
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
return branches.length > 0 ? branches : undefined;
|
|
107
|
+
}
|
|
108
|
+
/** The values a discriminant field admits, spelled as the strings a row carries. */
|
|
109
|
+
function literalValuesOf(schema) {
|
|
110
|
+
const def = unwrapZod(schema)?.def ?? unwrapZod(schema)?._def;
|
|
111
|
+
if (!def)
|
|
112
|
+
return [];
|
|
113
|
+
if (Array.isArray(def.values))
|
|
114
|
+
return def.values.map(String); // z.literal in Zod 4
|
|
115
|
+
if (def.value !== undefined)
|
|
116
|
+
return [String(def.value)]; // z.literal in Zod 3
|
|
117
|
+
if (def.entries)
|
|
118
|
+
return Object.values(def.entries).map(String); // z.enum
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Fill in `required` from the Zod shapes a field set can be backed by.
|
|
123
|
+
*
|
|
124
|
+
* More than one shape means the set spans branches, and a key only one branch
|
|
125
|
+
* declares cannot be required of a row that might be another branch.
|
|
126
|
+
*/
|
|
127
|
+
function deriveRequired(fields, shapes) {
|
|
128
|
+
for (const [key, field] of Object.entries(fields)) {
|
|
129
|
+
if (field.required !== undefined)
|
|
130
|
+
continue;
|
|
131
|
+
const backing = shapes.filter((s) => s[key]);
|
|
132
|
+
if (backing.length === 0)
|
|
133
|
+
continue;
|
|
134
|
+
field.required = backing.length === shapes.length && backing.every((s) => !s[key].isOptional());
|
|
135
|
+
}
|
|
136
|
+
}
|
|
71
137
|
const warnedListFields = new Set();
|
|
72
138
|
function warnUnbackedListField(type, listKey, present) {
|
|
73
139
|
const key = `${type}.${listKey}`;
|
|
@@ -91,8 +157,35 @@ function warnUnbackedListField(type, listKey, present) {
|
|
|
91
157
|
export function resetListFieldWarnings() {
|
|
92
158
|
warnedListFields.clear();
|
|
93
159
|
}
|
|
160
|
+
/**
|
|
161
|
+
* The block types Avocado itself defines, as opposed to a site's own.
|
|
162
|
+
*
|
|
163
|
+
* A site that renders its own `Hero` registers it under that name, and from
|
|
164
|
+
* then on the name is all the two shapes have in common. Anything that reasons
|
|
165
|
+
* about a block by hard-coded key — the demo-content migrations in
|
|
166
|
+
* `session-state`, which stamp `imageUrl` and a `left`/`right` pair onto
|
|
167
|
+
* whatever is called `Hero` or `TwoColumn` — is reasoning about *our* schema,
|
|
168
|
+
* and must not run against a schema it has never seen. Registration order
|
|
169
|
+
* settles it: whoever registered last owns the name, so the flag is written on
|
|
170
|
+
* every call rather than only on the first.
|
|
171
|
+
*/
|
|
172
|
+
const _builtinBlockTypes = G.__ase_builtinBlockTypes ?? (G.__ase_builtinBlockTypes = new Set());
|
|
173
|
+
/** Whether `type` is currently backed by Avocado's own definition. */
|
|
174
|
+
export function isBuiltinBlock(type) {
|
|
175
|
+
return _builtinBlockTypes.has(type);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Register one of Avocado's own blocks. Internal — a site uses `registerBlock`,
|
|
179
|
+
* and registering over a built-in name deliberately drops the built-in flag
|
|
180
|
+
* with the built-in schema.
|
|
181
|
+
*/
|
|
182
|
+
export function registerBuiltinBlock(type, config) {
|
|
183
|
+
registerBlock(type, config);
|
|
184
|
+
_builtinBlockTypes.add(type);
|
|
185
|
+
}
|
|
94
186
|
export function registerBlock(type, config) {
|
|
95
187
|
_blockSchemas[type] = config.schema;
|
|
188
|
+
_builtinBlockTypes.delete(type);
|
|
96
189
|
// Auto-derive `required` on each FieldMeta from the Zod schema shape
|
|
97
190
|
const shape = config.schema.shape;
|
|
98
191
|
if (shape) {
|
|
@@ -112,18 +205,20 @@ export function registerBlock(type, config) {
|
|
|
112
205
|
// Without the outer unwrap, `z.array(...).optional()` — which is how a
|
|
113
206
|
// list should be declared — looked like no array at all, so every
|
|
114
207
|
// optional list silently skipped its own `required` derivation.
|
|
115
|
-
const
|
|
116
|
-
if (!
|
|
208
|
+
const branches = listElementBranches(unwrapZod(listZod)?.element);
|
|
209
|
+
if (!branches) {
|
|
117
210
|
warnUnbackedListField(type, listKey, listZod !== undefined);
|
|
118
211
|
continue;
|
|
119
212
|
}
|
|
120
|
-
|
|
121
|
-
|
|
213
|
+
// `itemFields` is the union of every branch — the set a row is edited
|
|
214
|
+
// against when its discriminant matches nothing — so a key is required
|
|
215
|
+
// there only if every branch has it and no branch makes it optional.
|
|
216
|
+
deriveRequired(listMeta.itemFields, branches.map((b) => b.shape));
|
|
217
|
+
for (const [value, branchFields] of Object.entries(listMeta.itemFieldsByType ?? {})) {
|
|
218
|
+
const matching = branches.filter((b) => b.discriminantValues.includes(value));
|
|
219
|
+
if (matching.length === 0)
|
|
122
220
|
continue;
|
|
123
|
-
|
|
124
|
-
if (zodItem) {
|
|
125
|
-
itemField.required = !zodItem.isOptional();
|
|
126
|
-
}
|
|
221
|
+
deriveRequired(branchFields, matching.map((b) => b.shape));
|
|
127
222
|
}
|
|
128
223
|
}
|
|
129
224
|
}
|
|
@@ -391,6 +486,47 @@ export function undeclaredBlockTypes() {
|
|
|
391
486
|
return [];
|
|
392
487
|
return [...declared].filter((t) => !(t in _blockSchemas));
|
|
393
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Something a person can read out of a reference value.
|
|
491
|
+
*
|
|
492
|
+
* A reference is opaque by design — the panel shows it and never rewrites it —
|
|
493
|
+
* but "opaque" must not mean `[object Object]` in the one place a person looks
|
|
494
|
+
* to check which document a CTA points at. Storyblok spells the readable half
|
|
495
|
+
* `cached_url`, Contentful puts a `slug` or `title` on the resolved entry,
|
|
496
|
+
* Sanity hands over a bare `_ref`. Falls back to the id, and then to nothing,
|
|
497
|
+
* which the panel renders as "not set" rather than as a broken label.
|
|
498
|
+
*
|
|
499
|
+
* `referenceLabelKey` wins when the meta declares it.
|
|
500
|
+
*/
|
|
501
|
+
export function referenceLabel(value, labelKey) {
|
|
502
|
+
if (value === null || value === undefined)
|
|
503
|
+
return "";
|
|
504
|
+
if (typeof value === "string" || typeof value === "number")
|
|
505
|
+
return String(value).trim();
|
|
506
|
+
if (typeof value !== "object" || Array.isArray(value))
|
|
507
|
+
return "";
|
|
508
|
+
const rec = value;
|
|
509
|
+
const readable = (key) => {
|
|
510
|
+
const candidate = rec[key];
|
|
511
|
+
return typeof candidate === "string" || typeof candidate === "number" ? String(candidate).trim() : "";
|
|
512
|
+
};
|
|
513
|
+
if (labelKey) {
|
|
514
|
+
const declared = readable(labelKey);
|
|
515
|
+
if (declared !== "")
|
|
516
|
+
return declared;
|
|
517
|
+
}
|
|
518
|
+
for (const key of ["cached_url", "slug", "url", "href", "title", "name", "label"]) {
|
|
519
|
+
const found = readable(key);
|
|
520
|
+
if (found !== "")
|
|
521
|
+
return found;
|
|
522
|
+
}
|
|
523
|
+
for (const key of ["_ref", "id", "_id", "uuid", "entryId"]) {
|
|
524
|
+
const found = readable(key);
|
|
525
|
+
if (found !== "")
|
|
526
|
+
return found;
|
|
527
|
+
}
|
|
528
|
+
return "";
|
|
529
|
+
}
|
|
394
530
|
export function getPropDisplayName(blockType, propKey) {
|
|
395
531
|
if (!blockType)
|
|
396
532
|
return propKey;
|
|
@@ -429,6 +565,14 @@ function defaultScalarForField(field, fieldKey) {
|
|
|
429
565
|
return false;
|
|
430
566
|
if (field.kind === "enum")
|
|
431
567
|
return Array.isArray(field.options) && field.options.length > 0 ? field.options[0] : "";
|
|
568
|
+
/*
|
|
569
|
+
* A reference points at a document in the CMS. There is no default one, and
|
|
570
|
+
* `"New Story link"` in a field the site resolves to a URL is a broken link
|
|
571
|
+
* dressed as content. An added row simply has no reference until somebody
|
|
572
|
+
* sets one upstream.
|
|
573
|
+
*/
|
|
574
|
+
if (field.kind === "reference")
|
|
575
|
+
return null;
|
|
432
576
|
return `New ${label}`;
|
|
433
577
|
}
|
|
434
578
|
export function defaultListItemForBlock(type, listKey) {
|
package/dist/blocks/banner.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Banner", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
text: z.string().min(1),
|
|
7
7
|
variant: z.enum(["info", "success", "warning"]).default("info").catch("info"),
|
package/dist/blocks/card-grid.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("CardGrid", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().min(1),
|
|
7
7
|
subtitle: z.string().optional(),
|
package/dist/blocks/card.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Card", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().min(1),
|
|
7
7
|
description: z.string().min(1),
|
package/dist/blocks/carousel.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock, IMAGE_PLACEHOLDER } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Carousel", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
items: z.array(z.object({
|
|
7
7
|
id: z.string().optional(),
|
package/dist/blocks/cta.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("CTA", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().min(1),
|
|
7
7
|
description: z.string().min(1),
|
package/dist/blocks/embed.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Embed", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
embedType: z.enum(["map", "social", "custom"]).default("map").catch("map"),
|
|
7
7
|
url: z.string().min(1),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("FAQAccordion", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().min(1),
|
|
7
7
|
items: z.array(z.object({ id: z.string().optional(), q: z.string().min(1), a: z.string().min(1) })).min(1)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("FeatureGrid", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().min(1),
|
|
7
7
|
columns: z.enum(["2", "3", "4"]).default("3").catch("3"),
|
package/dist/blocks/footer.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Footer", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
copyright: z.string().min(1),
|
|
7
7
|
columns: z.array(z.object({ id: z.string().optional(), title: z.string().min(1), links: z.string().min(1) })).min(1)
|
package/dist/blocks/gallery.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock, IMAGE_PLACEHOLDER } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Gallery", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().optional(),
|
|
7
7
|
headingLevel: z.string().optional(),
|
package/dist/blocks/hero.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock, IMAGE_PLACEHOLDER } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Hero", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
heading: z.string().min(1),
|
|
7
7
|
subheading: z.string().min(1),
|
package/dist/blocks/quote.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Quote", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
quote: z.string().min(1),
|
|
7
7
|
author: z.string().optional(),
|
package/dist/blocks/rich-text.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("RichText", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string(),
|
|
7
7
|
body: z.string().min(1)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
4
|
const navLinkLeaf = z.object({
|
|
5
5
|
id: z.string().optional(),
|
|
@@ -7,7 +7,7 @@ const navLinkLeaf = z.object({
|
|
|
7
7
|
href: z.string().min(1),
|
|
8
8
|
newTab: z.boolean().optional(),
|
|
9
9
|
});
|
|
10
|
-
|
|
10
|
+
registerBuiltinBlock("SiteHeader", {
|
|
11
11
|
schema: z.object({
|
|
12
12
|
siteName: z.string().min(1),
|
|
13
13
|
logoUrl: z.string().min(1),
|
package/dist/blocks/stats.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Stats", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().optional(),
|
|
7
7
|
stats: z.array(z.object({
|
package/dist/blocks/table.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Table", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().optional(),
|
|
7
7
|
headingLevel: z.string().optional(),
|
package/dist/blocks/tabs.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { registerBuiltinBlock } from "./_registry.js";
|
|
3
3
|
import { f } from "./_helpers.js";
|
|
4
|
-
|
|
4
|
+
registerBuiltinBlock("Tabs", {
|
|
5
5
|
schema: z.object({
|
|
6
6
|
title: z.string().optional(),
|
|
7
7
|
tabs: z.array(z.object({
|