@wocart/theme 1.6.0 → 2.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.
- package/CHANGELOG.md +107 -0
- package/README.md +211 -9
- package/dist/astro/Block.astro +29 -0
- package/dist/astro/Blocks.astro +23 -0
- package/dist/astro/InstanceView.astro +34 -10
- package/dist/astro/ThemeStyles.astro +34 -0
- package/dist/astro/index.d.ts +49 -1
- package/dist/astro/index.js +43 -5
- package/dist/blocks.d.ts +61 -0
- package/dist/blocks.js +144 -0
- package/dist/conditions.d.ts +21 -0
- package/dist/conditions.js +65 -0
- package/dist/definitions.js +399 -29
- package/dist/index.d.ts +6 -1
- package/dist/index.js +4 -0
- package/dist/instances.d.ts +4 -0
- package/dist/instances.js +72 -4
- package/dist/manifest-diff.js +95 -2
- package/dist/manifest.d.ts +245 -13
- package/dist/manifest.js +64 -1
- package/dist/operations.d.ts +39 -13
- package/dist/operations.js +253 -55
- package/dist/page-data.js +63 -27
- package/dist/preview-bridge.js +10 -7
- package/dist/preview-protocol.d.ts +8 -3
- package/dist/rich-text.d.ts +2 -0
- package/dist/rich-text.js +4 -0
- package/dist/schemas.d.ts +2 -1
- package/dist/schemas.js +3 -1
- package/dist/settings.js +20 -1
- package/dist/style.d.ts +45 -0
- package/dist/style.js +248 -0
- package/dist/surface-document.d.ts +49 -0
- package/dist/surface-document.js +162 -0
- package/dist/types.d.ts +217 -7
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +4 -1
package/dist/definitions.js
CHANGED
|
@@ -4,8 +4,11 @@
|
|
|
4
4
|
* data — the validator exists so a theme's test suite fails loudly on catalog
|
|
5
5
|
* bugs when the catalog is assembled.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
7
|
+
import { COMPONENT_PLACEMENTS, COMPONENT_ROLES, THEME_LIMITS, areaSurface, guaranteedRole, innerGuarantees, placementAllows, surfaceRules, } from "./blocks.js";
|
|
8
|
+
import { instantiatePreset, surfaceCovers } from "./composition.js";
|
|
9
|
+
import { conditionProblem } from "./conditions.js";
|
|
10
|
+
import { COLOR_SCHEME_ID_PATTERN, STYLE_PANELS, STYLE_STEPS, STYLE_WIDTHS, colorSchemesProblem, styleSizeProblem, styleValueProblem, } from "./style.js";
|
|
11
|
+
import { HANDLE_LIST_MAX, checkStoredValue, handleListProblem, onStepGrid } from "./instances.js";
|
|
9
12
|
import { validatePageData } from "./page-data.js";
|
|
10
13
|
import { RICH_TEXT_FORMATS, isRichTextDocumentShape, richTextProblem } from "./rich-text.js";
|
|
11
14
|
import { COLOR_PATTERN, MENU_HANDLE_PATTERN, COLLECTION_HANDLE_PATTERN, PAGE_TYPES, RESOURCE_HANDLE_PATTERN, SURFACE_KEY_PATTERN, THEME_KEY_PATTERN, TYPE_KEY_PATTERN, isLocaleMap, isLocaleMapOf, isLocaleTag, isSafeLinkTarget, } from "./schemas.js";
|
|
@@ -95,17 +98,27 @@ export function validateThemeDefinition(theme, options = {}) {
|
|
|
95
98
|
addIssue(issues, "surfaces", "invalid-theme", "`surfaces` must be an array of surface declarations");
|
|
96
99
|
}
|
|
97
100
|
else {
|
|
98
|
-
inspectSurfaces(theme.surfaces, issues, locales);
|
|
101
|
+
inspectSurfaces(theme.surfaces, issues, locales, new Set(Object.keys(theme.components)));
|
|
99
102
|
}
|
|
100
103
|
}
|
|
104
|
+
if (theme.areas !== undefined)
|
|
105
|
+
inspectAreas(theme.areas, issues, locales, new Set(Object.keys(theme.components)));
|
|
101
106
|
const rules = {
|
|
102
107
|
contentKeys,
|
|
103
108
|
catalogKeys: new Set(Object.keys(theme.components)),
|
|
104
109
|
locales,
|
|
105
|
-
surfaceKeys: new Set(
|
|
106
|
-
.
|
|
107
|
-
|
|
110
|
+
surfaceKeys: new Set([
|
|
111
|
+
...(Array.isArray(theme.surfaces) ? theme.surfaces : [])
|
|
112
|
+
.map((surface) => (isRecord(surface) ? surface.key : undefined))
|
|
113
|
+
.filter((key) => typeof key === "string"),
|
|
114
|
+
...(Array.isArray(theme.areas) ? theme.areas : [])
|
|
115
|
+
.map((area) => (isRecord(area) && typeof area.key === "string" ? areaSurface(area.key) : undefined))
|
|
116
|
+
.filter((key) => key !== undefined),
|
|
117
|
+
]),
|
|
118
|
+
style: isRecord(theme.style) ? theme.style : undefined,
|
|
108
119
|
};
|
|
120
|
+
if (theme.style !== undefined)
|
|
121
|
+
inspectStyle(theme.style, issues);
|
|
109
122
|
if (theme.settings !== undefined)
|
|
110
123
|
inspectSettings(theme.settings, issues, rules);
|
|
111
124
|
for (const [key, definition] of Object.entries(theme.components)) {
|
|
@@ -120,8 +133,12 @@ export function validateThemeDefinition(theme, options = {}) {
|
|
|
120
133
|
addIssue(issues, `${path}.type`, "type-mismatch", `definition type "${definition.type}" must equal its catalog key "${key}"`);
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
|
-
//
|
|
124
|
-
//
|
|
136
|
+
// Block rules span components, and documents and presets are read through
|
|
137
|
+
// the catalog, so each is checked once everything before it is sound.
|
|
138
|
+
if (issues.length === 0)
|
|
139
|
+
inspectColorSchemes(theme, issues);
|
|
140
|
+
if (issues.length === 0)
|
|
141
|
+
inspectBlockRules(theme, issues);
|
|
125
142
|
if (issues.length === 0)
|
|
126
143
|
inspectComposition(theme, issues);
|
|
127
144
|
return issues;
|
|
@@ -169,17 +186,21 @@ function inspectContent(content, issues, locales, localesDeclared) {
|
|
|
169
186
|
}
|
|
170
187
|
/** Default documents and presets, read through the (sound) catalog as a writer would. */
|
|
171
188
|
function inspectComposition(theme, issues) {
|
|
172
|
-
|
|
173
|
-
|
|
189
|
+
const documents = [
|
|
190
|
+
...(theme.surfaces ?? []).map((surface, index) => ({ key: surface.key, document: surface.document, path: `surfaces[${index}].document` })),
|
|
191
|
+
...(theme.areas ?? []).map((area, index) => ({ key: areaSurface(area.key), document: area.document, path: `areas[${index}].document` })),
|
|
192
|
+
];
|
|
193
|
+
documents.forEach(({ key, document, path }) => {
|
|
194
|
+
if (document === undefined)
|
|
195
|
+
return;
|
|
196
|
+
const checked = validatePageData(theme, document);
|
|
197
|
+
if (checked.page !== null && checked.page.surface !== key) {
|
|
198
|
+
addIssue(issues, `${path}.surface`, "invalid-document", `the default document is for "${checked.page.surface}", not "${key}"`);
|
|
174
199
|
return;
|
|
175
|
-
const path = `surfaces[${index}].document`;
|
|
176
|
-
const checked = validatePageData(theme, surface.document);
|
|
177
|
-
if (checked.page !== null && checked.page.surface !== surface.key) {
|
|
178
|
-
addIssue(issues, `${path}.surface`, "invalid-document", `the default document is for "${checked.page.surface}", not "${surface.key}"`);
|
|
179
200
|
}
|
|
180
201
|
for (const issue of checked.issues) {
|
|
181
|
-
//
|
|
182
|
-
if (issue.code === "missing-required-prop")
|
|
202
|
+
// Filled at render: content-keyed text from the store's pack, required blocks by completion.
|
|
203
|
+
if (issue.code === "missing-required-prop" || issue.code === "missing-required-block")
|
|
183
204
|
continue;
|
|
184
205
|
addIssue(issues, issue.path === "" ? path : `${path}.${issue.path}`, "invalid-document", issue.message);
|
|
185
206
|
}
|
|
@@ -217,6 +238,9 @@ function inspectSettings(settings, issues, rules) {
|
|
|
217
238
|
}
|
|
218
239
|
const groupKeys = new Set();
|
|
219
240
|
const settingKeys = new Set();
|
|
241
|
+
const allSettingKeys = new Set(settings.flatMap((group) => isRecord(group) && Array.isArray(group.fields)
|
|
242
|
+
? group.fields.map((field) => (isRecord(field) && typeof field.key === "string" ? field.key : "")).filter((key) => key !== "")
|
|
243
|
+
: []));
|
|
220
244
|
settings.forEach((group, index) => {
|
|
221
245
|
const path = `settings[${index}]`;
|
|
222
246
|
if (!isRecord(group)) {
|
|
@@ -239,7 +263,7 @@ function inspectSettings(settings, issues, rules) {
|
|
|
239
263
|
addIssue(issues, `${path}.fields`, "invalid-settings", "`fields` must be an array");
|
|
240
264
|
return;
|
|
241
265
|
}
|
|
242
|
-
inspectFields(group.fields, path, issues, rules);
|
|
266
|
+
inspectFields(group.fields, path, issues, rules, { settings: true, siblings: allSettingKeys });
|
|
243
267
|
for (const [fieldIndex, field] of group.fields.entries()) {
|
|
244
268
|
if (!isRecord(field) || typeof field.key !== "string")
|
|
245
269
|
continue;
|
|
@@ -250,7 +274,38 @@ function inspectSettings(settings, issues, rules) {
|
|
|
250
274
|
}
|
|
251
275
|
});
|
|
252
276
|
}
|
|
253
|
-
|
|
277
|
+
/** The parts every page shares: kebab-case keys, once each, labelled, bounded like a surface. */
|
|
278
|
+
function inspectAreas(areas, issues, locales, catalogKeys) {
|
|
279
|
+
if (!Array.isArray(areas)) {
|
|
280
|
+
addIssue(issues, "areas", "invalid-theme", "`areas` must be an array of area declarations");
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const keys = new Set();
|
|
284
|
+
areas.forEach((area, index) => {
|
|
285
|
+
const path = `areas[${index}]`;
|
|
286
|
+
if (!isRecord(area)) {
|
|
287
|
+
addIssue(issues, path, "invalid-theme", "an area must be { key, label, document?, required?, max? }");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
if (typeof area.key !== "string" || !TYPE_KEY_PATTERN.test(area.key)) {
|
|
291
|
+
addIssue(issues, `${path}.key`, "invalid-theme", "`key` must be lowercase kebab-case");
|
|
292
|
+
}
|
|
293
|
+
else if (keys.has(area.key)) {
|
|
294
|
+
addIssue(issues, `${path}.key`, "invalid-theme", `duplicate area "${area.key}"`);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
keys.add(area.key);
|
|
298
|
+
}
|
|
299
|
+
const labelProblem = localizedTextProblem(area.label, locales, { allowEmpty: false });
|
|
300
|
+
if (labelProblem !== null)
|
|
301
|
+
addIssue(issues, `${path}.label`, "invalid-theme", `\`label\` ${labelProblem}`);
|
|
302
|
+
if (area.document !== undefined && !isRecord(area.document)) {
|
|
303
|
+
addIssue(issues, `${path}.document`, "invalid-document", "`document` must be a page document");
|
|
304
|
+
}
|
|
305
|
+
inspectBlockBounds(area, path, issues, catalogKeys, THEME_LIMITS.sections, "invalid-theme");
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
function inspectSurfaces(surfaces, issues, locales, catalogKeys) {
|
|
254
309
|
const seenKeys = new Set();
|
|
255
310
|
surfaces.forEach((surface, index) => {
|
|
256
311
|
const surfacePath = `surfaces[${index}]`;
|
|
@@ -278,6 +333,7 @@ function inspectSurfaces(surfaces, issues, locales) {
|
|
|
278
333
|
if (surface.document !== undefined && !isRecord(surface.document)) {
|
|
279
334
|
addIssue(issues, `${surfacePath}.document`, "invalid-document", "`document` must be a page document");
|
|
280
335
|
}
|
|
336
|
+
inspectBlockBounds(surface, surfacePath, issues, catalogKeys, THEME_LIMITS.sections, "invalid-theme");
|
|
281
337
|
if (typeof surface.key === "string" && surface.key.includes(".")) {
|
|
282
338
|
const baseKey = surface.key.slice(0, surface.key.indexOf("."));
|
|
283
339
|
const base = surfaces.find((candidate) => isRecord(candidate) && candidate.key === baseKey);
|
|
@@ -358,7 +414,7 @@ function inspectDefinition(definition, path, issues, rules) {
|
|
|
358
414
|
addIssue(issues, `${path}.fields`, "invalid-field", "`fields` must be an array");
|
|
359
415
|
}
|
|
360
416
|
else {
|
|
361
|
-
inspectFields(definition.fields, path, issues, rules);
|
|
417
|
+
inspectFields(definition.fields, path, issues, rules, { props: true });
|
|
362
418
|
}
|
|
363
419
|
}
|
|
364
420
|
if (definition.slots !== undefined) {
|
|
@@ -369,10 +425,178 @@ function inspectDefinition(definition, path, issues, rules) {
|
|
|
369
425
|
inspectSlots(definition.slots, path, issues, rules.catalogKeys);
|
|
370
426
|
}
|
|
371
427
|
}
|
|
428
|
+
if (definition.placement !== undefined && !COMPONENT_PLACEMENTS.includes(definition.placement)) {
|
|
429
|
+
addIssue(issues, `${path}.placement`, "invalid-placement", `\`placement\` must be one of ${COMPONENT_PLACEMENTS.join(", ")}`);
|
|
430
|
+
}
|
|
431
|
+
if (definition.role !== undefined && !COMPONENT_ROLES.includes(definition.role)) {
|
|
432
|
+
addIssue(issues, `${path}.role`, "invalid-role", `\`role\` must be one of ${COMPONENT_ROLES.join(", ")}`);
|
|
433
|
+
}
|
|
434
|
+
if (definition.fixed !== undefined)
|
|
435
|
+
inspectFixed(definition.fixed, path, issues, rules);
|
|
436
|
+
}
|
|
437
|
+
/** The blocks a component's code places: named once each, of a type in the catalog. */
|
|
438
|
+
function inspectFixed(fixed, path, issues, rules) {
|
|
439
|
+
if (!Array.isArray(fixed) || fixed.length === 0) {
|
|
440
|
+
addIssue(issues, `${path}.fixed`, "invalid-fixed", "`fixed` must be a non-empty array of fixed blocks (omit it for none)");
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
const names = new Set();
|
|
444
|
+
fixed.forEach((block, index) => {
|
|
445
|
+
const blockPath = `${path}.fixed[${index}]`;
|
|
446
|
+
if (!isRecord(block)) {
|
|
447
|
+
addIssue(issues, blockPath, "invalid-fixed", "a fixed block must be { name, type, label? }");
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
if (typeof block.name !== "string" || !TYPE_KEY_PATTERN.test(block.name)) {
|
|
451
|
+
addIssue(issues, `${blockPath}.name`, "invalid-fixed", "`name` must be lowercase kebab-case");
|
|
452
|
+
}
|
|
453
|
+
else if (names.has(block.name)) {
|
|
454
|
+
addIssue(issues, `${blockPath}.name`, "invalid-fixed", `duplicate fixed block "${block.name}"`);
|
|
455
|
+
}
|
|
456
|
+
else {
|
|
457
|
+
names.add(block.name);
|
|
458
|
+
}
|
|
459
|
+
if (typeof block.type !== "string" || !rules.catalogKeys.has(block.type)) {
|
|
460
|
+
addIssue(issues, `${blockPath}.type`, "invalid-fixed", `${JSON.stringify(block.type)} is not a component type in this catalog`);
|
|
461
|
+
}
|
|
462
|
+
if (block.label !== undefined) {
|
|
463
|
+
const problem = localizedTextProblem(block.label, rules.locales, { allowEmpty: false });
|
|
464
|
+
if (problem !== null)
|
|
465
|
+
addIssue(issues, `${blockPath}.label`, "invalid-fixed", `\`label\` ${problem}`);
|
|
466
|
+
}
|
|
467
|
+
});
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* A slot's or surface's `max` and `required`: whole-number bounds within the
|
|
471
|
+
* platform's cap, each required type one the catalog has and the list takes,
|
|
472
|
+
* and the minimums fitting under the cap.
|
|
473
|
+
*/
|
|
474
|
+
function inspectBlockBounds(holder, path, issues, catalogKeys, platformCap, code) {
|
|
475
|
+
const { max, required } = holder;
|
|
476
|
+
if (max !== undefined && !(typeof max === "number" && Number.isInteger(max) && max >= 1 && max <= platformCap)) {
|
|
477
|
+
addIssue(issues, `${path}.max`, code, `\`max\` must be a whole number from 1 to ${platformCap}`);
|
|
478
|
+
}
|
|
479
|
+
if (required === undefined)
|
|
480
|
+
return;
|
|
481
|
+
if (!Array.isArray(required) || required.length === 0) {
|
|
482
|
+
addIssue(issues, `${path}.required`, code, "`required` must be a non-empty array of { type, min?, max? } (omit it for none)");
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
const allow = Array.isArray(holder.allow) ? holder.allow : undefined;
|
|
486
|
+
const types = new Set();
|
|
487
|
+
let minimums = 0;
|
|
488
|
+
required.forEach((entry, index) => {
|
|
489
|
+
const entryPath = `${path}.required[${index}]`;
|
|
490
|
+
if (!isRecord(entry)) {
|
|
491
|
+
addIssue(issues, entryPath, code, "a required entry must be { type, min?, max? }");
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
if (typeof entry.type !== "string" || !catalogKeys.has(entry.type)) {
|
|
495
|
+
addIssue(issues, `${entryPath}.type`, code, `${JSON.stringify(entry.type)} is not a component type in this catalog`);
|
|
496
|
+
}
|
|
497
|
+
else if (allow !== undefined && !allow.includes(entry.type)) {
|
|
498
|
+
addIssue(issues, `${entryPath}.type`, code, `"${entry.type}" is required but not in \`allow\``);
|
|
499
|
+
}
|
|
500
|
+
else if (types.has(entry.type)) {
|
|
501
|
+
addIssue(issues, `${entryPath}.type`, code, `"${entry.type}" is required twice`);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
types.add(entry.type);
|
|
505
|
+
}
|
|
506
|
+
const min = entry.min ?? 1;
|
|
507
|
+
if (!(typeof min === "number" && Number.isInteger(min) && min >= 1)) {
|
|
508
|
+
addIssue(issues, `${entryPath}.min`, code, "`min` must be a whole number of at least 1");
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (entry.max !== undefined && !(typeof entry.max === "number" && Number.isInteger(entry.max) && entry.max >= min)) {
|
|
512
|
+
addIssue(issues, `${entryPath}.max`, code, `\`max\` must be a whole number of at least \`min\` (${min})`);
|
|
513
|
+
}
|
|
514
|
+
minimums += min;
|
|
515
|
+
});
|
|
516
|
+
const cap = typeof max === "number" ? Math.min(max, platformCap) : holder.multiple === false ? 1 : platformCap;
|
|
517
|
+
if (minimums > cap)
|
|
518
|
+
addIssue(issues, `${path}.required`, code, `the required minimums (${minimums}) exceed what the list holds (${cap})`);
|
|
372
519
|
}
|
|
373
|
-
|
|
520
|
+
/**
|
|
521
|
+
* Rules that span components, once each one is sound: fixed and required
|
|
522
|
+
* blocks are placeable where they go, never contain themselves, nest within
|
|
523
|
+
* the platform's depth, and every product surface guarantees an order form.
|
|
524
|
+
*/
|
|
525
|
+
function inspectBlockRules(theme, issues) {
|
|
526
|
+
const definitionOf = (type) => (Object.hasOwn(theme.components, type) ? theme.components[type] : undefined);
|
|
527
|
+
for (const [type, definition] of Object.entries(theme.components)) {
|
|
528
|
+
for (const [index, block] of (definition.fixed ?? []).entries()) {
|
|
529
|
+
if (!placementAllows(definitionOf(block.type)?.placement, "block")) {
|
|
530
|
+
addIssue(issues, `components.${type}.fixed[${index}].type`, "invalid-fixed", `"${block.type}" is placed only as a section, not as a block`);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
for (const [slotIndex, slot] of (definition.slots ?? []).entries()) {
|
|
534
|
+
for (const [index, entry] of (slot.required ?? []).entries()) {
|
|
535
|
+
if (!placementAllows(definitionOf(entry.type)?.placement, "block")) {
|
|
536
|
+
addIssue(issues, `components.${type}.slots[${slotIndex}].required[${index}].type`, "invalid-slot", `"${entry.type}" is placed only as a section, not as a block`);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const holders = [
|
|
542
|
+
...(theme.surfaces ?? []).map((surface, index) => ({ key: surface.key, required: surface.required, path: `surfaces[${index}]` })),
|
|
543
|
+
...(theme.areas ?? []).map((area, index) => ({ key: areaSurface(area.key), required: area.required, path: `areas[${index}]` })),
|
|
544
|
+
];
|
|
545
|
+
for (const holder of holders) {
|
|
546
|
+
(holder.required ?? []).forEach((entry, index) => {
|
|
547
|
+
const definition = definitionOf(entry.type);
|
|
548
|
+
const path = `${holder.path}.required[${index}].type`;
|
|
549
|
+
if (!placementAllows(definition?.placement, "section")) {
|
|
550
|
+
addIssue(issues, path, "invalid-theme", `"${entry.type}" is placed only as a block, not as a section`);
|
|
551
|
+
}
|
|
552
|
+
else if (definition?.capabilities?.surfaces !== undefined && !definition.capabilities.surfaces.some((entry) => surfaceCovers(entry, holder.key))) {
|
|
553
|
+
addIssue(issues, path, "invalid-theme", `"${entry.type}" is not available on "${holder.key}"`);
|
|
554
|
+
}
|
|
555
|
+
});
|
|
556
|
+
}
|
|
557
|
+
const depths = new Map();
|
|
558
|
+
const reported = new Set();
|
|
559
|
+
const nesting = (type, trail) => {
|
|
560
|
+
const known = depths.get(type);
|
|
561
|
+
if (known !== undefined)
|
|
562
|
+
return known;
|
|
563
|
+
const loop = trail.indexOf(type);
|
|
564
|
+
if (loop >= 0) {
|
|
565
|
+
const cycle = [...trail.slice(loop), type];
|
|
566
|
+
if (!reported.has(cycle[0])) {
|
|
567
|
+
reported.add(cycle[0]);
|
|
568
|
+
addIssue(issues, `components.${cycle[0]}`, "invalid-fixed", `fixed and required blocks contain themselves: ${cycle.join(" → ")}`);
|
|
569
|
+
}
|
|
570
|
+
return 0;
|
|
571
|
+
}
|
|
572
|
+
const definition = definitionOf(type);
|
|
573
|
+
const inner = definition === undefined ? [] : innerGuarantees(definition);
|
|
574
|
+
const depth = inner.reduce((deepest, { type: child }) => Math.max(deepest, 1 + nesting(child, [...trail, type])), 0);
|
|
575
|
+
depths.set(type, depth);
|
|
576
|
+
return depth;
|
|
577
|
+
};
|
|
578
|
+
for (const type of Object.keys(theme.components)) {
|
|
579
|
+
const depth = nesting(type, []);
|
|
580
|
+
if (depth > THEME_LIMITS.depth) {
|
|
581
|
+
addIssue(issues, `components.${type}`, "invalid-fixed", `fixed and required blocks nest ${depth} levels inside "${type}"; at most ${THEME_LIMITS.depth}`);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
if (issues.length > 0)
|
|
585
|
+
return;
|
|
586
|
+
(theme.surfaces ?? []).forEach((surface, index) => {
|
|
587
|
+
if (surface.pageType !== "product")
|
|
588
|
+
return;
|
|
589
|
+
const rules = surfaceRules(theme, surface.key);
|
|
590
|
+
const guaranteed = (rules.required ?? []).reduce((count, entry) => count + (entry.min ?? 1) * guaranteedRole(theme, entry.type, "order-form"), 0);
|
|
591
|
+
if (guaranteed === 0) {
|
|
592
|
+
addIssue(issues, `surfaces[${index}]`, "order-form-not-guaranteed", `product surface "${surface.key}" must always render an order form: require a section that places a component with role "order-form" through fixed or required blocks`);
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
function inspectFields(fields, path, issues, rules, { inList = false, props = false, settings = false, siblings, } = {}) {
|
|
374
597
|
const { contentKeys, locales } = rules;
|
|
375
598
|
const seenKeys = new Set();
|
|
599
|
+
const siblingKeys = new Set(fields.map((field) => (isRecord(field) && typeof field.key === "string" ? field.key : "")).filter((key) => key !== ""));
|
|
376
600
|
fields.forEach((field, index) => {
|
|
377
601
|
const fieldPath = `${path}.fields[${index}]`;
|
|
378
602
|
if (!isRecord(field)) {
|
|
@@ -396,6 +620,9 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
396
620
|
else if (field.key === "__proto__") {
|
|
397
621
|
addIssue(issues, `${fieldPath}.key`, "invalid-field", "`__proto__` cannot be a field key: as a prop name it would set a prototype");
|
|
398
622
|
}
|
|
623
|
+
else if (props && field.key === "blocks") {
|
|
624
|
+
addIssue(issues, `${fieldPath}.key`, "invalid-field", "`blocks` cannot be a field key: the renderer gives every component its blocks under that prop");
|
|
625
|
+
}
|
|
399
626
|
else if (seenKeys.has(field.key)) {
|
|
400
627
|
addIssue(issues, `${fieldPath}.key`, "duplicate-field-key", `duplicate field key "${field.key}"`);
|
|
401
628
|
}
|
|
@@ -415,6 +642,11 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
415
642
|
if (field.required !== undefined && typeof field.required !== "boolean") {
|
|
416
643
|
addIssue(issues, `${fieldPath}.required`, "invalid-field", "`required` must be a boolean");
|
|
417
644
|
}
|
|
645
|
+
if (field.visibleIf !== undefined) {
|
|
646
|
+
const problem = conditionProblem(field.visibleIf, siblings ?? siblingKeys, typeof field.key === "string" ? field.key : "");
|
|
647
|
+
if (problem !== null)
|
|
648
|
+
addIssue(issues, `${fieldPath}.visibleIf`, "invalid-field", `\`visibleIf\` ${problem}`);
|
|
649
|
+
}
|
|
418
650
|
switch (field.kind) {
|
|
419
651
|
case "text":
|
|
420
652
|
case "textarea": {
|
|
@@ -456,6 +688,12 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
456
688
|
if (field.step !== undefined && !(typeof field.step === "number" && Number.isFinite(field.step) && field.step > 0)) {
|
|
457
689
|
addIssue(issues, `${fieldPath}.step`, "invalid-field", "`step` must be a finite number greater than 0");
|
|
458
690
|
}
|
|
691
|
+
if (field.display !== undefined && field.display !== "input" && field.display !== "slider") {
|
|
692
|
+
addIssue(issues, `${fieldPath}.display`, "invalid-field", "`display` must be input or slider");
|
|
693
|
+
}
|
|
694
|
+
else if (field.display === "slider" && (field.min === undefined || field.max === undefined || field.step === undefined)) {
|
|
695
|
+
addIssue(issues, `${fieldPath}.display`, "invalid-field", "a slider needs `min`, `max` and `step`");
|
|
696
|
+
}
|
|
459
697
|
// A default outside the declared bounds would sail through assembly
|
|
460
698
|
// and then behave incoherently at runtime (`resolveInstance` trusts
|
|
461
699
|
// authored defaults while rejecting the same out-of-range instance
|
|
@@ -492,6 +730,9 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
492
730
|
(typeof field.default !== "string" || !options.includes(field.default))) {
|
|
493
731
|
addIssue(issues, `${fieldPath}.default`, "invalid-field", "`default` must be one of `options`");
|
|
494
732
|
}
|
|
733
|
+
if (field.display !== undefined && !["dropdown", "radio", "segmented"].includes(field.display)) {
|
|
734
|
+
addIssue(issues, `${fieldPath}.display`, "invalid-field", "`display` must be dropdown, radio or segmented");
|
|
735
|
+
}
|
|
495
736
|
if (field.optionLabels !== undefined) {
|
|
496
737
|
if (!isRecord(field.optionLabels)) {
|
|
497
738
|
addIssue(issues, `${fieldPath}.optionLabels`, "invalid-field", "`optionLabels` must map options to labels");
|
|
@@ -550,6 +791,56 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
550
791
|
inspectListField(field, fieldPath, issues, rules);
|
|
551
792
|
break;
|
|
552
793
|
}
|
|
794
|
+
case "style": {
|
|
795
|
+
const panels = field.panels;
|
|
796
|
+
if (!Array.isArray(panels) || panels.length === 0 || !panels.every((panel) => STYLE_PANELS.includes(panel))) {
|
|
797
|
+
addIssue(issues, `${fieldPath}.panels`, "invalid-field", `\`panels\` must be a non-empty array of ${STYLE_PANELS.join(", ")}`);
|
|
798
|
+
}
|
|
799
|
+
else if (new Set(panels).size !== panels.length) {
|
|
800
|
+
addIssue(issues, `${fieldPath}.panels`, "invalid-field", "`panels` names a panel twice");
|
|
801
|
+
}
|
|
802
|
+
else if (field.default !== undefined) {
|
|
803
|
+
const problem = styleValueProblem(panels, field.default) ?? styleSizeProblem(rules.style, field.default);
|
|
804
|
+
if (problem !== null)
|
|
805
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` ${problem}`);
|
|
806
|
+
}
|
|
807
|
+
break;
|
|
808
|
+
}
|
|
809
|
+
case "productList":
|
|
810
|
+
case "collectionList": {
|
|
811
|
+
for (const bound of ["min", "max"]) {
|
|
812
|
+
const value = field[bound];
|
|
813
|
+
const least = bound === "max" ? 1 : 0;
|
|
814
|
+
if (value !== undefined && !(typeof value === "number" && Number.isInteger(value) && value >= least && value <= HANDLE_LIST_MAX)) {
|
|
815
|
+
addIssue(issues, `${fieldPath}.${bound}`, "invalid-field", `\`${bound}\` must be a whole number from ${least} to ${HANDLE_LIST_MAX}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (typeof field.min === "number" && typeof field.max === "number" && field.min > field.max) {
|
|
819
|
+
addIssue(issues, fieldPath, "invalid-field", "`min` must be <= `max`");
|
|
820
|
+
}
|
|
821
|
+
if (field.default !== undefined) {
|
|
822
|
+
const problem = handleListProblem(field.kind, field.min, field.max, field.default);
|
|
823
|
+
if (problem !== null)
|
|
824
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` ${problem}`);
|
|
825
|
+
}
|
|
826
|
+
break;
|
|
827
|
+
}
|
|
828
|
+
case "colorScheme": {
|
|
829
|
+
if (field.default !== undefined && (typeof field.default !== "string" || !COLOR_SCHEME_ID_PATTERN.test(field.default))) {
|
|
830
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", "`default` must be a color scheme id");
|
|
831
|
+
}
|
|
832
|
+
break;
|
|
833
|
+
}
|
|
834
|
+
case "colorSchemes": {
|
|
835
|
+
if (!settings) {
|
|
836
|
+
addIssue(issues, `${fieldPath}.kind`, "invalid-field", "a `colorSchemes` field holds the store's schemes: it belongs in the theme's settings");
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
const problem = colorSchemesProblem(field.default);
|
|
840
|
+
if (problem !== null)
|
|
841
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` (the theme's schemes) ${problem}`);
|
|
842
|
+
break;
|
|
843
|
+
}
|
|
553
844
|
case "url": {
|
|
554
845
|
if (field.default !== undefined &&
|
|
555
846
|
(typeof field.default !== "string" || !isSafeLinkTarget(field.default))) {
|
|
@@ -562,6 +853,71 @@ function inspectFields(fields, path, issues, rules, { inList = false } = {}) {
|
|
|
562
853
|
}
|
|
563
854
|
});
|
|
564
855
|
}
|
|
856
|
+
/** The sizes a theme gives the platform's style steps and widths: whole pixels, within reason. */
|
|
857
|
+
function inspectStyle(style, issues) {
|
|
858
|
+
if (!isRecord(style)) {
|
|
859
|
+
addIssue(issues, "style", "invalid-theme", "`style` must be { spacing?, widths? }");
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
const scales = [
|
|
863
|
+
["spacing", STYLE_STEPS, 0, 1024],
|
|
864
|
+
["widths", STYLE_WIDTHS, 160, 4096],
|
|
865
|
+
];
|
|
866
|
+
for (const key of Object.keys(style)) {
|
|
867
|
+
if (!scales.some(([name]) => name === key))
|
|
868
|
+
addIssue(issues, `style.${key}`, "invalid-theme", `"${key}" is not a style scale (spacing, widths)`);
|
|
869
|
+
}
|
|
870
|
+
for (const [name, keys, min, max] of scales) {
|
|
871
|
+
const scale = style[name];
|
|
872
|
+
if (scale === undefined)
|
|
873
|
+
continue;
|
|
874
|
+
if (!isRecord(scale)) {
|
|
875
|
+
addIssue(issues, `style.${name}`, "invalid-theme", `\`${name}\` must map ${keys.join(", ")} to pixels`);
|
|
876
|
+
continue;
|
|
877
|
+
}
|
|
878
|
+
for (const [key, size] of Object.entries(scale)) {
|
|
879
|
+
if (!keys.includes(key)) {
|
|
880
|
+
addIssue(issues, `style.${name}.${key}`, "invalid-theme", `"${key}" is not one of ${keys.join(", ")}`);
|
|
881
|
+
}
|
|
882
|
+
else if (!(typeof size === "number" && Number.isInteger(size) && size >= min && size <= max)) {
|
|
883
|
+
addIssue(issues, `style.${name}.${key}`, "invalid-theme", `must be a whole number of pixels from ${min} to ${max}`);
|
|
884
|
+
}
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The store's color schemes and what picks them: at most one `colorSchemes`
|
|
890
|
+
* setting, which every `colorScheme` field needs, and whose schemes include
|
|
891
|
+
* each picker's default.
|
|
892
|
+
*/
|
|
893
|
+
function inspectColorSchemes(theme, issues) {
|
|
894
|
+
const holders = [];
|
|
895
|
+
(theme.settings ?? []).forEach((group, groupIndex) => group.fields.forEach((field, index) => {
|
|
896
|
+
if (field.kind === "colorSchemes")
|
|
897
|
+
holders.push({ path: `settings[${groupIndex}].fields[${index}]`, ids: new Set(field.default.map((scheme) => scheme.id)) });
|
|
898
|
+
}));
|
|
899
|
+
for (const extra of holders.slice(1))
|
|
900
|
+
addIssue(issues, extra.path, "invalid-settings", "a theme has at most one `colorSchemes` setting");
|
|
901
|
+
const ids = holders[0]?.ids;
|
|
902
|
+
const pickers = [];
|
|
903
|
+
const collect = (fields, path) => fields.forEach((field, index) => {
|
|
904
|
+
if (field.kind === "colorScheme")
|
|
905
|
+
pickers.push({ path: `${path}.fields[${index}]`, default: field.default });
|
|
906
|
+
if (field.kind === "list")
|
|
907
|
+
collect(field.fields, `${path}.fields[${index}]`);
|
|
908
|
+
});
|
|
909
|
+
for (const [type, definition] of Object.entries(theme.components))
|
|
910
|
+
collect(definition.fields ?? [], `components.${type}`);
|
|
911
|
+
(theme.settings ?? []).forEach((group, index) => collect(group.fields, `settings[${index}]`));
|
|
912
|
+
for (const picker of pickers) {
|
|
913
|
+
if (ids === undefined) {
|
|
914
|
+
addIssue(issues, picker.path, "invalid-field", "a `colorScheme` field needs the theme's `colorSchemes` setting");
|
|
915
|
+
}
|
|
916
|
+
else if (picker.default !== undefined && !ids.has(picker.default)) {
|
|
917
|
+
addIssue(issues, `${picker.path}.default`, "invalid-field", `"${picker.default}" is not one of the theme's color schemes`);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
}
|
|
565
921
|
/** Preset shapes; what they build is checked once the catalog is sound (`inspectComposition`). */
|
|
566
922
|
function inspectPresets(presets, path, issues, rules) {
|
|
567
923
|
if (!Array.isArray(presets)) {
|
|
@@ -621,27 +977,40 @@ function inspectRichTextField(field, fieldPath, issues, locales) {
|
|
|
621
977
|
formats = declared.filter((format) => RICH_TEXT_FORMATS.includes(format));
|
|
622
978
|
}
|
|
623
979
|
}
|
|
980
|
+
if (field.inline !== undefined && typeof field.inline !== "boolean") {
|
|
981
|
+
addIssue(issues, `${fieldPath}.inline`, "invalid-field", "`inline` must be a boolean");
|
|
982
|
+
}
|
|
983
|
+
else if (field.inline === true && (formats ?? RICH_TEXT_FORMATS).some((format) => format === "heading" || format === "list")) {
|
|
984
|
+
addIssue(issues, `${fieldPath}.formats`, "invalid-field", "an inline field is one line: list `formats` without `heading` or `list`");
|
|
985
|
+
}
|
|
624
986
|
if (field.default === undefined)
|
|
625
987
|
return;
|
|
626
988
|
const value = field.default;
|
|
989
|
+
const before = issues.length;
|
|
627
990
|
if (isRichTextDocumentShape(value)) {
|
|
628
991
|
const problem = richTextProblem(value, formats);
|
|
629
992
|
if (problem !== null)
|
|
630
993
|
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` ${problem}`);
|
|
631
|
-
return;
|
|
632
994
|
}
|
|
633
|
-
if (!isLocaleMapOf(value, isRecord)) {
|
|
995
|
+
else if (!isLocaleMapOf(value, isRecord)) {
|
|
634
996
|
addIssue(issues, `${fieldPath}.default`, "invalid-field", "`default` must be a rich-text document or a map from language tags to documents");
|
|
635
997
|
return;
|
|
636
998
|
}
|
|
637
|
-
|
|
638
|
-
const
|
|
639
|
-
|
|
640
|
-
|
|
999
|
+
else {
|
|
1000
|
+
for (const [locale, document] of Object.entries(value)) {
|
|
1001
|
+
const problem = richTextProblem(document, formats);
|
|
1002
|
+
if (problem !== null)
|
|
1003
|
+
addIssue(issues, `${fieldPath}.default.${locale}`, "invalid-field", `\`default\` (${locale}) ${problem}`);
|
|
1004
|
+
}
|
|
1005
|
+
const missing = locales.filter((locale) => !Object.hasOwn(value, locale));
|
|
1006
|
+
if (missing.length > 0) {
|
|
1007
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` is missing the theme's languages: ${missing.join(", ")}`);
|
|
1008
|
+
}
|
|
641
1009
|
}
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
1010
|
+
if (field.inline === true && issues.length === before) {
|
|
1011
|
+
const checked = checkStoredValue(field, value);
|
|
1012
|
+
if (!checked.ok)
|
|
1013
|
+
addIssue(issues, `${fieldPath}.default`, "invalid-field", `\`default\` ${checked.message}`);
|
|
645
1014
|
}
|
|
646
1015
|
}
|
|
647
1016
|
function inspectListField(field, fieldPath, issues, rules) {
|
|
@@ -703,6 +1072,7 @@ function inspectSlots(slots, path, issues, catalogKeys) {
|
|
|
703
1072
|
if (slot.allow !== undefined) {
|
|
704
1073
|
inspectAllow(slot.allow, `${slotPath}.allow`, issues, catalogKeys);
|
|
705
1074
|
}
|
|
1075
|
+
inspectBlockBounds(slot, slotPath, issues, catalogKeys, THEME_LIMITS.slotInstances, "invalid-slot");
|
|
706
1076
|
});
|
|
707
1077
|
}
|
|
708
1078
|
function inspectAllow(allow, path, issues, catalogKeys) {
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,11 @@ export { resolveInstance, createInstance, checkStoredValue as checkFieldValue }
|
|
|
17
17
|
export type { CheckedValue as FieldValueCheck } from "./instances.js";
|
|
18
18
|
export { RICH_TEXT_FORMATS, richTextToHtml, richTextToPlainText } from "./rich-text.js";
|
|
19
19
|
export { instantiatePreset, isInsertableOn } from "./composition.js";
|
|
20
|
+
export { COMPONENT_PLACEMENTS, COMPONENT_ROLES, THEME_LIMITS, areaSurface, fixedBlockId } from "./blocks.js";
|
|
21
|
+
export { completePage, resolveSurfaceDocument } from "./surface-document.js";
|
|
22
|
+
export { CONDITION_DEPTH, isFieldVisible } from "./conditions.js";
|
|
23
|
+
export { COLOR_ROLES, COLOR_SCHEMES_MAX, STYLE_BREAKPOINTS, STYLE_PANELS, STYLE_STEPS, STYLE_WIDTHS, THEME_STYLES_PATH, colorSchemeCss, contrastRatio, schemeClass, styleClasses, themeStyleSheet, } from "./style.js";
|
|
24
|
+
export type { SurfaceDocument, SurfaceDocumentReason } from "./surface-document.js";
|
|
20
25
|
export { checkPageData, duplicateInstance, findInstance, insertInstance, moveInstance, newInstanceId, removeInstance, setInstanceHidden, setInstanceProps, } from "./operations.js";
|
|
21
26
|
export type { DocumentEdit, DocumentIssue, DocumentIssueCode, InstanceLocation, InstancePlacement, } from "./operations.js";
|
|
22
27
|
export { THEME_SDK_VERSION } from "./version.js";
|
|
@@ -30,4 +35,4 @@ export { diffThemeManifests } from "./manifest-diff.js";
|
|
|
30
35
|
export { PREVIEW_PROTOCOL_VERSION, THEME_PREVIEW_PATH, parsePreviewMarker, previewEndMarker, previewStartMarker, } from "./preview-protocol.js";
|
|
31
36
|
export type { PreviewEditorMessage, PreviewInstance, PreviewMode, PreviewPageMessage, PreviewPatch, PreviewRect, } from "./preview-protocol.js";
|
|
32
37
|
export type { ManifestChange, ManifestDiff } from "./manifest-diff.js";
|
|
33
|
-
export type { JsonValue, JsonObject, LocaleCode, LocalizedText, ResolutionScope, FieldKind, FieldDefinition, FieldValue, InferProps, FieldValues, SlotDefinition, ComponentCapabilities, ComponentPreset, PresetInstance, SurfacePageType, ComponentDefinition, ComponentSpec, SurfaceSpec, ThemeDefinition, SettingsGroup, InferSettings, ThemeSettingsIssue, ThemeSettingsIssueCode, ThemeSettingsResult, NumberFieldDefinition, ColorFieldDefinition, MenuFieldDefinition, CollectionFieldDefinition, ProductFieldDefinition, CategoryFieldDefinition, ImageFieldDefinition, RichTextFieldDefinition, RichTextFormat, ListFieldDefinition, ListItemFieldDefinition, ListItem, MediaReference, ResolvedImage, RichTextValue, RichTextDocument, RichTextBlock, RichTextParagraph, RichTextHeading, RichTextList, RichTextListItem, RichTextInline, RichTextText, RichTextBreak, RichTextMark, ThemeManifest, ThemeManifestIssue, ThemeManifestIssueCode, ThemeManifestResult, ComponentInstance, PageData, ThemeRenderContext, LoadContext, LoadFn, ReadCache, PageDataIssue, PageDataIssueCode, PageDataResult, ResolvedInstance, ThemeDefinitionIssue, ThemeDefinitionIssueCode, ThemeValidationOptions, } from "./types.js";
|
|
38
|
+
export type { JsonValue, JsonObject, LocaleCode, LocalizedText, ResolutionScope, FieldKind, FieldDefinition, FieldValue, InferProps, FieldValues, SlotDefinition, RequiredBlock, ComponentPlacement, ComponentRole, FixedBlockDefinition, Breakpoint, Responsive, StyleStep, StyleWidth, StylePanel, StyleValue, StyleFieldDefinition, ColorRole, ColorScheme, ColorSchemeFieldDefinition, ColorSchemesFieldDefinition, ThemeStyle, ProductListFieldDefinition, CollectionListFieldDefinition, FieldCondition, ConditionValue, ComponentCapabilities, ComponentPreset, PresetInstance, SurfacePageType, ComponentDefinition, ComponentSpec, SurfaceSpec, AreaSpec, ThemeDefinition, SettingsGroup, InferSettings, ThemeSettingsIssue, ThemeSettingsIssueCode, ThemeSettingsResult, NumberFieldDefinition, ColorFieldDefinition, MenuFieldDefinition, CollectionFieldDefinition, ProductFieldDefinition, CategoryFieldDefinition, ImageFieldDefinition, RichTextFieldDefinition, RichTextFormat, ListFieldDefinition, ListItemFieldDefinition, ListItem, MediaReference, ResolvedImage, RichTextValue, RichTextDocument, RichTextBlock, RichTextParagraph, RichTextHeading, RichTextList, RichTextListItem, RichTextInline, RichTextText, RichTextBreak, RichTextMark, ThemeManifest, ThemeManifestIssue, ThemeManifestIssueCode, ThemeManifestResult, ComponentInstance, PageData, ThemeRenderContext, LoadContext, LoadFn, ReadCache, PageDataIssue, PageDataIssueCode, PageDataResult, ResolvedInstance, ThemeDefinitionIssue, ThemeDefinitionIssueCode, ThemeValidationOptions, } from "./types.js";
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,10 @@ export { parsePageData, validatePageData } from "./page-data.js";
|
|
|
16
16
|
export { resolveInstance, createInstance, checkStoredValue as checkFieldValue } from "./instances.js";
|
|
17
17
|
export { RICH_TEXT_FORMATS, richTextToHtml, richTextToPlainText } from "./rich-text.js";
|
|
18
18
|
export { instantiatePreset, isInsertableOn } from "./composition.js";
|
|
19
|
+
export { COMPONENT_PLACEMENTS, COMPONENT_ROLES, THEME_LIMITS, areaSurface, fixedBlockId } from "./blocks.js";
|
|
20
|
+
export { completePage, resolveSurfaceDocument } from "./surface-document.js";
|
|
21
|
+
export { CONDITION_DEPTH, isFieldVisible } from "./conditions.js";
|
|
22
|
+
export { COLOR_ROLES, COLOR_SCHEMES_MAX, STYLE_BREAKPOINTS, STYLE_PANELS, STYLE_STEPS, STYLE_WIDTHS, THEME_STYLES_PATH, colorSchemeCss, contrastRatio, schemeClass, styleClasses, themeStyleSheet, } from "./style.js";
|
|
19
23
|
export { checkPageData, duplicateInstance, findInstance, insertInstance, moveInstance, newInstanceId, removeInstance, setInstanceHidden, setInstanceProps, } from "./operations.js";
|
|
20
24
|
export { THEME_SDK_VERSION } from "./version.js";
|
|
21
25
|
export { resolveThemeSettings, checkThemeSettings } from "./settings.js";
|
package/dist/instances.d.ts
CHANGED
|
@@ -30,6 +30,10 @@ export declare function resolveField(field: FieldDefinition, raw: unknown, conte
|
|
|
30
30
|
* refused, never stripped.
|
|
31
31
|
*/
|
|
32
32
|
export declare function checkStoredValue(field: FieldDefinition, value: unknown): CheckedValue;
|
|
33
|
+
/** The most handles a `productList` or `collectionList` holds. */
|
|
34
|
+
export declare const HANDLE_LIST_MAX = 50;
|
|
35
|
+
/** Why `value` is not a list of distinct handles within `min` and `max`, or `null`. */
|
|
36
|
+
export declare function handleListProblem(kind: "productList" | "collectionList", min: number | undefined, max: number | undefined, value: unknown): string | null;
|
|
33
37
|
/** On a grid of `step` from `base`, allowing for binary floating point. */
|
|
34
38
|
export declare function onStepGrid(value: number, base: number, step: number): boolean;
|
|
35
39
|
/**
|