@bettercms-ai/kit 2.0.0-next.6 → 2.0.0-next.7
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/adapters/lossy.d.ts +3 -3
- package/dist/adapters/lossy.js +3 -3
- package/dist/adapters/lossy.js.map +1 -1
- package/dist/adapters/prod-types.d.ts +48 -4
- package/dist/adapters/prod-types.js +4 -3
- package/dist/adapters/prod-types.js.map +1 -1
- package/dist/adapters/schema.d.ts +39 -0
- package/dist/adapters/schema.js +177 -32
- package/dist/adapters/schema.js.map +1 -1
- package/dist/controls/icon.d.ts +1 -1
- package/dist/controls/icon.js +1 -1
- package/dist/controls/icon.js.map +1 -1
- package/dist/index.js +2 -2
- package/dist/ledgers/exclusions.v1.js +103 -0
- package/dist/ledgers/exclusions.v1.js.map +1 -0
- package/dist/library/coverage.d.ts +13 -1
- package/dist/library/coverage.js +12 -1
- package/dist/library/coverage.js.map +1 -1
- package/dist/library/kit-catalog.json +149 -149
- package/dist/testing/coverage-extras.d.ts +8 -0
- package/dist/testing/coverage-extras.js +44 -0
- package/dist/testing/coverage-extras.js.map +1 -0
- package/dist/testing/exclusions.d.ts +30 -0
- package/dist/testing/exclusions.js +13 -0
- package/dist/testing/exclusions.js.map +1 -0
- package/package.json +2 -2
package/dist/adapters/lossy.d.ts
CHANGED
|
@@ -277,7 +277,7 @@ export interface CatalogItem {
|
|
|
277
277
|
reason: string;
|
|
278
278
|
}
|
|
279
279
|
export declare const CATALOG: {
|
|
280
|
-
readonly "block icon": "The block picker's icon
|
|
280
|
+
readonly "block icon": "The block picker's icon: a component stores it as component.icon and a model as content_models.icon, both a dock icon, so only one with no dock name for it lands here.";
|
|
281
281
|
readonly category: "The block picker's category is catalog metadata. A component takes the nearest prod category (CATEGORY_TO_PROD) when there is one; a model has none.";
|
|
282
282
|
readonly "document preview": "The document-level preview (how a row of this type reads in a parent's list) has no place on a model; a component stores it as component.preview.";
|
|
283
283
|
readonly "document preview path": "component.preview names a top-level prop key or a dotted path into one (`slides.0.heading`, componentPreviewRefusals); a key no prop has, or a path through a renamed value key (`image.alt`), is not stored (preview_key_unknown), so the slot row reads the child's first text instead.";
|
|
@@ -287,14 +287,14 @@ export declare const CATALOG: {
|
|
|
287
287
|
readonly "kind:object": "An object type is stored as a model.";
|
|
288
288
|
readonly "kind:entry (props)": "Only blocks become components; the host decides what an entry schema is placed as.";
|
|
289
289
|
readonly variants: "Variants are separate components sharing a sectionType in prod: the host seeds one per variant.";
|
|
290
|
-
readonly ai: "
|
|
290
|
+
readonly ai: "Not lost since A3: a field and a prop, at any depth, store the AI-assist sparkle (`ai`).";
|
|
291
291
|
readonly "item openMode": "Prod keeps one openMode per list, the array's own; the V2 engine reads only that one too (lib/schema.ts itemOpenMode ignores the item's), so the item's changes nothing the dock shows.";
|
|
292
292
|
readonly "itemNoun (derived)": "A list with no itemNoun whose item title is not the field title's singular: ui.itemLabel keeps the item title, and the read side names the noun from it, which is the noun the V2 engine already falls back to (lib/schema.ts itemNoun).";
|
|
293
293
|
readonly "fieldset (derived)": "A field names a fieldset the schema does not declare: the V2 engine draws it as a section titled by its name (engine/field-list.tsx arrange), so it is stored as that prop group and the read side declares it. Not a loss.";
|
|
294
294
|
readonly "prop target": "A V2 prop has no binding path, so fromTypeSchema('props') returns none: seeding binds each prop to its target. Not a loss.";
|
|
295
295
|
readonly "block placement": "Where a block fits (inline / section / page) is the host's: the block picker and a body's Insert menu place it; a model or component carries none.";
|
|
296
296
|
readonly "slot allow": "A slot keeps its V2 allow list as config.allow (1–50 block names of 1–64 characters, no repeats: slot_allow_invalid), which the dock resolves to the project's components; this list breaks those bounds, so the slot takes any component.";
|
|
297
|
-
readonly "segmented fallback": "A segmented select of
|
|
297
|
+
readonly "segmented fallback": "A segmented select of more than SEGMENTED_MAX (8) options: V2 draws the dropdown (D46, choice.tsx fitsSegmented) and prod stores the select with no control (control_segmented holds 2–8), which the dock draws as the same dropdown. Nothing drawn differs.";
|
|
298
298
|
readonly "seeded instance": "A starting value the host writes into a new instance when it seeds or places one: a schema-level initialValue, a reference prop's default (a definition holds none: reference_in_definition), or a slot's children (they name V2 block types, which become component instances at seeding).";
|
|
299
299
|
readonly "not applicable (props)": "unique on a scalar prop, or on a sub-field outside a table: each placement holds its own value, so there is nothing to be unique across (unique_prop_scope). The instance is the whole document.";
|
|
300
300
|
readonly "host config": "A prod-only prop config key the host reads, not the V2 schema: a rich text's Accept list, its one-paragraph flags (allowMultipleParagraphs, the older inline), allowTargetBlank and component allowlist (the dock's rich-text codec and insert menu read them from the stored prop: dock-v2 richTextCodec configAt, kitServices insertableComponents), a section label, and the import's write-back metadata no editor reads (SERVER_CONFIG_KEYS). Read back, it names no loss: the kit never writes a definition, so the config rides untouched.";
|
package/dist/adapters/lossy.js
CHANGED
|
@@ -234,7 +234,7 @@ var FEATURES = {
|
|
|
234
234
|
}
|
|
235
235
|
};
|
|
236
236
|
var CATALOG = {
|
|
237
|
-
"block icon": "The block picker's icon
|
|
237
|
+
"block icon": "The block picker's icon: a component stores it as component.icon and a model as content_models.icon, both a dock icon, so only one with no dock name for it lands here.",
|
|
238
238
|
category: "The block picker's category is catalog metadata. A component takes the nearest prod category (CATEGORY_TO_PROD) when there is one; a model has none.",
|
|
239
239
|
"document preview": "The document-level preview (how a row of this type reads in a parent's list) has no place on a model; a component stores it as component.preview.",
|
|
240
240
|
"document preview path": "component.preview names a top-level prop key or a dotted path into one (`slides.0.heading`, componentPreviewRefusals); a key no prop has, or a path through a renamed value key (`image.alt`), is not stored (preview_key_unknown), so the slot row reads the child's first text instead.",
|
|
@@ -244,14 +244,14 @@ var CATALOG = {
|
|
|
244
244
|
"kind:object": "An object type is stored as a model.",
|
|
245
245
|
"kind:entry (props)": "Only blocks become components; the host decides what an entry schema is placed as.",
|
|
246
246
|
variants: "Variants are separate components sharing a sectionType in prod: the host seeds one per variant.",
|
|
247
|
-
ai: "
|
|
247
|
+
ai: "Not lost since A3: a field and a prop, at any depth, store the AI-assist sparkle (`ai`).",
|
|
248
248
|
"item openMode": "Prod keeps one openMode per list, the array's own; the V2 engine reads only that one too (lib/schema.ts itemOpenMode ignores the item's), so the item's changes nothing the dock shows.",
|
|
249
249
|
"itemNoun (derived)": "A list with no itemNoun whose item title is not the field title's singular: ui.itemLabel keeps the item title, and the read side names the noun from it, which is the noun the V2 engine already falls back to (lib/schema.ts itemNoun).",
|
|
250
250
|
"fieldset (derived)": "A field names a fieldset the schema does not declare: the V2 engine draws it as a section titled by its name (engine/field-list.tsx arrange), so it is stored as that prop group and the read side declares it. Not a loss.",
|
|
251
251
|
"prop target": "A V2 prop has no binding path, so fromTypeSchema('props') returns none: seeding binds each prop to its target. Not a loss.",
|
|
252
252
|
"block placement": "Where a block fits (inline / section / page) is the host's: the block picker and a body's Insert menu place it; a model or component carries none.",
|
|
253
253
|
"slot allow": "A slot keeps its V2 allow list as config.allow (1–50 block names of 1–64 characters, no repeats: slot_allow_invalid), which the dock resolves to the project's components; this list breaks those bounds, so the slot takes any component.",
|
|
254
|
-
"segmented fallback": "A segmented select of
|
|
254
|
+
"segmented fallback": "A segmented select of more than SEGMENTED_MAX (8) options: V2 draws the dropdown (D46, choice.tsx fitsSegmented) and prod stores the select with no control (control_segmented holds 2–8), which the dock draws as the same dropdown. Nothing drawn differs.",
|
|
255
255
|
"seeded instance": "A starting value the host writes into a new instance when it seeds or places one: a schema-level initialValue, a reference prop's default (a definition holds none: reference_in_definition), or a slot's children (they name V2 block types, which become component instances at seeding).",
|
|
256
256
|
"not applicable (props)": "unique on a scalar prop, or on a sub-field outside a table: each placement holds its own value, so there is nothing to be unique across (unique_prop_scope). The instance is the whole document.",
|
|
257
257
|
"host config": "A prod-only prop config key the host reads, not the V2 schema: a rich text's Accept list, its one-paragraph flags (allowMultipleParagraphs, the older inline), allowTargetBlank and component allowlist (the dock's rich-text codec and insert menu read them from the stored prop: dock-v2 richTextCodec configAt, kitServices insertableComponents), a section label, and the import's write-back metadata no editor reads (SERVER_CONFIG_KEYS). Read back, it names no loss: the kit never writes a definition, so the config rides untouched."
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lossy.js","names":[],"sources":["../../src/adapters/lossy.ts"],"sourcesContent":["/**\n * LOSSY ITEMS — every V2 schema feature the production language cannot hold is named, never dropped silently.\n * CATALOG ITEMS (below) are the V2 metadata a host sets at seeding time, which the dock never stores.\n *\n * `path` is a JSON pointer into the V2 TypeSchema (\"/fields/3/of/0/fields/1/localized\"). `feature` is a stable name\n * from FEATURES (what the lossy ledger counts), `reason` says why prod cannot keep it, and FEATURES[feature].backlog\n * names what removes the loss.\n */\nexport interface LossyItem {\n path: string;\n feature: LossyFeature;\n reason: string;\n}\n\n/**\n * What removes a lossy feature: a named backend backlog item (its receipt is this feature's lossy count reaching 0), or\n * an explicit disposition that is not backend work: `corpus-fix` (the V2 schema breaks a prod rule the author can\n * satisfy: rename, shorten, reorder, drop a key the type cannot use), `dashboard:F12` (prod stores it; only the dock's\n * DockFieldTabs drops it, and the kit's GroupTabs replaces that at the dock cutover), `wontfix:depth` (a prod rule kept\n * on purpose: the backend refuses an 11th level with field_depth_exceeded), `wontfix:item-config` (the backend refuses\n * per-item config on a list of scalars on purpose, array_config_key_unknown), `wontfix:nesting` (the backend keeps a\n * slot's settings leaf fields, on purpose: slot_settings_invalid), `deferred:shopify` (owner 2026-10-11: a Shopify\n * reference stays locked text until the Shopify integration pipeline is built; allowed-deviations.json \"deferred\"). Owner 2026-10-11 (C11): the backend keeps a scalar list's\n * item settings (config.item), a list's bounds to 500, openMode on code, a slot's allow names (config.allow), a dotted\n * document preview path, the tabs no top-level prop sits in (components.tabs) and a nested field's tab (ui.tab { id }).\n * corpus-fix items are V2 corpus defects, an\n * owner note for the demo repo: the sandbox renders them from the V2 schema, seeding skips them.\n *\n * Landed on origin/dev and mapped here, so they name no feature any more: prop-reference (73a120c50), prop reference targets + filter (a4ce1ae95; shopify stays named), validation-parity\n * (dd021ceba), option-meta (ab15fe893), chrome-keys (98e48b9e3, 324580b2e), adapter:icons (the V2 icon vocabulary,\n * e126bf33d), localized-flag (279a10490), table / polymorphic list nesting (B1/B2: a data table or a polymorphic list at\n * any depth, in an object, a list's rows or an item type), slot nesting (C1/C2: a slot prop at any depth). What those\n * keys still cannot hold is a value the backend refuses, named below as corpus-fix.\n */\nexport type Backlog =\n | \"reference-targets\"\n | \"table\"\n | \"slot\"\n | \"block-model-fields\"\n | \"corpus-fix\"\n | \"dashboard:F12\"\n | \"wontfix:depth\"\n | \"wontfix:nesting\"\n | \"wontfix:item-config\"\n | \"deferred:shopify\";\n\nexport const FEATURES = {\n /* field types prod has no home for (the value is kept in a json field, or a near type) */\n \"type:table\": { backlog: \"table\", reason: \"Prod has no table field type (BE: a new type): the TableValue is stored verbatim in a json field.\" },\n \"type:slot\": { backlog: \"slot\", reason: \"Blocks inside a block on a content model: the backend's slot field (kind:'block' models by blockSlugs) is not mapped by this adapter yet, so it is stored as json. A component's top-level slot is a slot prop.\" },\n \"type:polymorphic-array\": { backlog: \"slot\", reason: \"An array of several item types: a model stores it as modular and a prop as a polymorphic list (table, config.mode \\\"types\\\"), both of object item types only. A list mixing scalar item types is stored as json.\" },\n \"item chrome (types)\": { backlog: \"slot\", reason: \"A polymorphic list's item type is { name, label?, fields, icon?, preview? } (item_types_invalid): it keeps its dock icon and row preview, and no other chrome.\" },\n \"type:primitive-array\": { backlog: \"wontfix:item-config\", reason: \"A list of this item type has no prod array form: config.itemType is text, number, date, image, link or richtext. Stored as json.\" },\n \"depth>10\": { backlog: \"wontfix:depth\", reason: \"Prod allows 10 field levels: the object at level 10 that would nest an 11th is stored as json.\" },\n /* document level */\n \"type name\": { backlog: \"corpus-fix\", reason: \"The schema name is stored as a kebab-case slug, which does not read back as this name.\" },\n /* tabs and sections */\n \"group name>40\": { backlog: \"corpus-fix\", reason: \"A tab name is at most 40 characters; truncated.\" },\n \"group title\": { backlog: \"corpus-fix\", reason: \"A prop tab's label is the group title trimmed (1–20 characters): a blank title defines no tab, so the group is not stored and its props sit in the default tab; spaces around a title are not kept.\" },\n \"group default\": { backlog: \"corpus-fix\", reason: \"The default tab is always the first one in prod.\" },\n \"group icon\": { backlog: \"corpus-fix\", reason: \"A tab icon is a dock icon name (DOCK_ICON_NAMES); this name is not one.\" },\n \"group (nested)\": { backlog: \"corpus-fix\", reason: \"A nested field names its tab as ui.tab { id } only when the tab is stored: this group is not (named on the group itself).\" },\n \"groups>3\": { backlog: \"dashboard:F12\", reason: \"The dock shows at most 3 tabs (DockFieldTabs), so the adapter keeps 3 until the kit GroupTabs replaces DockFieldTabs at the dock cutover: later groups' fields land in the default tab meanwhile.\" },\n \"group name collision\": { backlog: \"corpus-fix\", reason: \"Two tabs share an id, or a stored name (the first 20 characters, trimmed, any case): the backend refuses that (tab_name, fieldset ids), so the later tab is not stored.\" },\n \"prop tab limit\": { backlog: \"corpus-fix\", reason: \"A component holds at most 8 tabs, each with an id of 1–40 characters (the backend's ui.tab rule): this group is not stored and its props sit in the default tab.\" },\n \"fieldset spans groups\": { backlog: \"corpus-fix\", reason: \"A prod fieldset sits in one tab; this field's tab differs from its fieldset's.\" },\n \"fieldset (props)\": { backlog: \"corpus-fix\", reason: \"A prop group is defined on the first prop that uses it: a fieldset no prop uses is not stored (and the ones after it move up a place).\" },\n \"fieldset name>limit\": { backlog: \"corpus-fix\", reason: \"A fieldset name is at most 60 characters (a prop group label 32); truncated.\" },\n \"fieldset collision\": { backlog: \"corpus-fix\", reason: \"A fieldset shares its id with a group, or this field names a fieldset the schema does not define.\" },\n /* field chrome and rules */\n \"duplicate name\": { backlog: \"corpus-fix\", reason: \"Two sibling fields share a name; prod keys are unique per level, so the later one is renamed.\" },\n \"reserved name\": {\n backlog: \"corpus-fix\",\n reason: \"A model field's key is not one the API reserves (types' KEY_RESERVED: derived, layout, page, a forbidden key or a leading _) and is a letter, then letters, digits, - and _ (KEY_FORMAT): this one is stored renamed. id is allowed.\",\n },\n rows: { backlog: \"corpus-fix\", reason: \"ui.rows is a whole number from 1 to 40, on a text or long text field only.\" },\n synced: { backlog: \"corpus-fix\", reason: \"ui.synced names its global in 1–64 characters (trimmed).\" },\n valueShape: { backlog: \"corpus-fix\", reason: \"ui.valueShape is for a link, image or button field only.\" },\n \"default value\": { backlog: \"corpus-fix\", reason: \"This initial value holds what the prod value cannot keep (named under it): a crop preset with no image size, a datetime without seconds, a button icon outside the dock vocabulary…\" },\n slotSettings: { backlog: \"wontfix:nesting\", reason: \"A slot's per-child settings are at most 20 leaf fields (slot_settings_invalid): this setting is an object, a list, a table, a slot or a form, or past the 20th, or the field holding them is not a slot.\" },\n icon: { backlog: \"corpus-fix\", reason: \"A field icon is ui.icon, a dock icon name, on a nesting field only (an object, a list of objects, a modular list); this field is a leaf, or the name is not a dock icon.\" },\n layout: { backlog: \"corpus-fix\", reason: \"This layout does not fit the field in prod: ui.control is dropdown / radio / grid on a select, cards on a select or multiselect, checkbox on a boolean or multiselect, switch on a boolean, segmented on a select of 2–4 options, slider on a number with min < max (≤ 1000 stops); a list of scalars or entries lays out as rows or a gallery.\" },\n \"help>limit\": { backlog: \"corpus-fix\", reason: \"Help text is at most 500 characters on a field and 200 on a prop (a placeholder 120, a prop group description 200); truncated.\" },\n \"option icon\": { backlog: \"corpus-fix\", reason: \"Option icons are dock icon names, all or none per list: an icon in this list is not a dock name, so the list stores none (option_icon_unknown).\" },\n \"option description\": { backlog: \"corpus-fix\", reason: \"An option description is one line of 1–140 characters (option_description_invalid).\" },\n condition: { backlog: \"corpus-fix\", reason: \"This condition has no prod showIf form: it names something that is not a sibling (the block's _variant is one only for a top-level prop: show_if_variant_field), names it by `scope` (prod resolves a rule at the nearest level that declares its field), or tests a value showIf cannot.\" },\n access: { backlog: \"corpus-fix\", reason: \"ui.access takes view \\\"schema.view\\\" and edit \\\"schema.manage\\\" only (access_cap_unknown), and a developer lock (readOnly \\\"developer\\\") is not also a plain read-only flag.\" },\n timezone: { backlog: \"corpus-fix\", reason: \"A field's time zone is true, false, \\\"site\\\" or an IANA zone name, on a date or datetime.\" },\n \"richtext references\": {\n backlog: \"corpus-fix\",\n reason: \"A rich text's references (config.references) are { posts?, authors? }, each a content-model id (letters, digits, - and _, 1-64), on a rich text or document field only: anything else is not stored.\",\n },\n \"richtext features\": {\n backlog: \"corpus-fix\",\n reason: \"A rich text's features (config.features) are distinct names from types RICHTEXT_FEATURES, on a rich text or document field only: a name outside it is not stored, and the rest are.\",\n },\n \"shopify reference\": {\n backlog: \"deferred:shopify\",\n reason:\n \"A prop reference to Shopify: no Shopify integration backs one (reference_target_unsupported), so a Shopify-only reference is locked text holding the product id (SHOPIFY_REF_LOCK) and a mixed one keeps its other targets. Owner 2026-10-11: deferred until the Shopify integration pipeline is built (allowed-deviations.json status deferred).\",\n },\n \"reference target\": { backlog: \"reference-targets\", reason: \"A prop reference keeps entry models (contentModelId / allowedModelSlugs) and page, form and collection (config.targets, backend a4ce1ae95); Shopify has no integration behind it (reference_target_unsupported), so a shopify target is dropped and a reference with only shopify is stored as json. A content-model field keeps entry models only.\" },\n \"reference order\": { backlog: \"corpus-fix\", reason: \"Prod keeps a reference's entry models apart from its page / form / collection targets: the list reads back entry models first, then the targets.\" },\n \"reference filter\": { backlog: \"reference-targets\", reason: \"A prop reference keeps its filter as config.filter (1–500 characters, no $parameters or ^ parent: reference_filter_invalid); a content-model field keeps none.\" },\n \"numeric unit\": { backlog: \"corpus-fix\", reason: \"config.unit is a number's (1–24 characters); a rating has no unit.\" },\n \"rating bounds\": { backlog: \"corpus-fix\", reason: \"A prod rating runs from config.min (≥ 0) to config.max (a whole 1–10) in steps of config.step, min < max and at most 100 whole steps apart.\" },\n \"scale rules\": { backlog: \"corpus-fix\", reason: \"This scale breaks prod's scale rules: token values 1–32 characters and distinct, labels 1–24, at most 50 steps and 20 presets, a token preset naming a step, a { value, unit } preset needing allowCustom.\" },\n \"validation.patternMessage\": { backlog: \"corpus-fix\", reason: \"A pattern message needs a stored pattern and is 1–200 characters.\" },\n \"validation.pattern\": { backlog: \"corpus-fix\", reason: \"A pattern is kept on text, long text and url values, a regex of at most 200 characters that compiles.\" },\n \"validation.length\": { backlog: \"corpus-fix\", reason: \"Length limits are whole numbers ≥ 0, on text, long text, url and rich text values.\" },\n \"validation.items\": { backlog: \"corpus-fix\", reason: \"Item limits are whole numbers ≥ 0 on a list, min ≤ max: a tags max 1–500 and min 0–500, a prop's own list (table, array) at most 500 (LIST_ITEMS_CAP), a slot's at most 50 (SLOT_ITEMS_CAP).\" },\n \"validation.unique\": { backlog: \"corpus-fix\", reason: \"unique is for text, long text, slug, email, phone, link, number, select, date and single-model reference fields, and lists; a polymorphic reference has no one id to compare.\" },\n \"validation.recommendedMax\": { backlog: \"corpus-fix\", reason: \"A soft limit is a positive whole number in prod.\" },\n \"item identity\": { backlog: \"corpus-fix\", reason: \"A list item's name is an identifier (a letter, then letters, digits or _; ≤ 64) and its title 1–60 characters (ui.itemName / ui.itemTitle); an object item keeps no other key.\" },\n \"item settings\": { backlog: \"corpus-fix\", reason: \"A list of scalars keeps its item's description (1–200 characters), localized flag, required, a text or rich text item's length and a text item's pattern (config.item, array_item_settings_invalid): this setting is none of those, or out of their bounds (a multi-line text item reads back single-line).\" },\n itemNoun: { backlog: \"corpus-fix\", reason: \"The item noun is ui.itemLabel, capitalised, at most 24 characters.\" },\n \"block model field\": { backlog: \"block-model-fields\", reason: \"A modular item type is a kind:'block' model, which holds only fields a block primitive renders (text, long text, rich text, image, link, slug, email, phone, color, select, number, rating, date, datetime, boolean): this field is not stored.\" },\n \"item icon\": { backlog: \"corpus-fix\", reason: \"Prod keeps one icon per list (ui.icon, a dock name) and one per polymorphic item type: the list has its own icon, or this name is not a dock icon.\" },\n \"preview icon\": { backlog: \"corpus-fix\", reason: \"A row preview's icon is a dock icon name.\" },\n \"preview path\": { backlog: \"corpus-fix\", reason: \"A row preview names a child key, or a dotted path whose later segments are children or keys of an image, video, link, button, file, location, json, code or reference value, or a data table's caption or notes (preview_path_unknown).\" },\n openMode: { backlog: \"corpus-fix\", reason: \"openMode is for object, array, table and code fields only (openMode_leaf).\" },\n \"unsupported key\": { backlog: \"corpus-fix\", reason: \"This V2 key has no prod mapping.\" },\n} as const satisfies Record<string, { reason: string; backlog: Backlog }>;\n\nexport type LossyFeature = keyof typeof FEATURES;\n\n/**\n * CATALOG ITEMS — V2 metadata the dock never stores, set when a schema is seeded or placed by its host: not a loss of\n * stored content. Reported apart from LossyItem so the lossy ledger counts only what prod drops.\n */\nexport interface CatalogItem {\n path: string;\n feature: CatalogFeature;\n reason: string;\n}\n\nexport const CATALOG = {\n \"block icon\": \"The block picker's icon belongs to the catalog entry; a model carries none (a component stores it as component.icon, a dock icon, so only one with no dock name for it lands here).\",\n category: \"The block picker's category is catalog metadata. A component takes the nearest prod category (CATEGORY_TO_PROD) when there is one; a model has none.\",\n \"document preview\": \"The document-level preview (how a row of this type reads in a parent's list) has no place on a model; a component stores it as component.preview.\",\n \"document preview path\": \"component.preview names a top-level prop key or a dotted path into one (`slides.0.heading`, componentPreviewRefusals); a key no prop has, or a path through a renamed value key (`image.alt`), is not stored (preview_key_unknown), so the slot row reads the child's first text instead.\",\n \"kind:block (fields)\": \"A section's fields are stored as a model (kind model): which host places it is set at seeding.\",\n \"kind:global\": \"A global (synced) block is a host-level placement; its fields are stored as a model or a component.\",\n \"kind:page\": \"A page schema is stored as a model; the host makes it a page.\",\n \"kind:object\": \"An object type is stored as a model.\",\n \"kind:entry (props)\": \"Only blocks become components; the host decides what an entry schema is placed as.\",\n variants: \"Variants are separate components sharing a sectionType in prod: the host seeds one per variant.\",\n ai: \"The AI-assist sparkle is a host setting, not stored on the field.\",\n \"item openMode\": \"Prod keeps one openMode per list, the array's own; the V2 engine reads only that one too (lib/schema.ts itemOpenMode ignores the item's), so the item's changes nothing the dock shows.\",\n \"itemNoun (derived)\": \"A list with no itemNoun whose item title is not the field title's singular: ui.itemLabel keeps the item title, and the read side names the noun from it, which is the noun the V2 engine already falls back to (lib/schema.ts itemNoun).\",\n \"fieldset (derived)\": \"A field names a fieldset the schema does not declare: the V2 engine draws it as a section titled by its name (engine/field-list.tsx arrange), so it is stored as that prop group and the read side declares it. Not a loss.\",\n \"prop target\": \"A V2 prop has no binding path, so fromTypeSchema('props') returns none: seeding binds each prop to its target. Not a loss.\",\n \"block placement\": \"Where a block fits (inline / section / page) is the host's: the block picker and a body's Insert menu place it; a model or component carries none.\",\n \"slot allow\": \"A slot keeps its V2 allow list as config.allow (1–50 block names of 1–64 characters, no repeats: slot_allow_invalid), which the dock resolves to the project's components; this list breaks those bounds, so the slot takes any component.\",\n \"segmented fallback\": \"A segmented select of 5+ options: V2 draws the dropdown (D46, choice.tsx fitsSegmented) and prod stores the select with no control (control_segmented holds 2–4), which the dock draws as the same dropdown. Nothing drawn differs.\",\n \"seeded instance\": \"A starting value the host writes into a new instance when it seeds or places one: a schema-level initialValue, a reference prop's default (a definition holds none: reference_in_definition), or a slot's children (they name V2 block types, which become component instances at seeding).\",\n \"not applicable (props)\": \"unique on a scalar prop, or on a sub-field outside a table: each placement holds its own value, so there is nothing to be unique across (unique_prop_scope). The instance is the whole document.\",\n \"host config\": \"A prod-only prop config key the host reads, not the V2 schema: a rich text's Accept list, its one-paragraph flags (allowMultipleParagraphs, the older inline), allowTargetBlank and component allowlist (the dock's rich-text codec and insert menu read them from the stored prop: dock-v2 richTextCodec configAt, kitServices insertableComponents), a section label, and the import's write-back metadata no editor reads (SERVER_CONFIG_KEYS). Read back, it names no loss: the kit never writes a definition, so the config rides untouched.\",\n} as const;\n\nexport type CatalogFeature = keyof typeof CATALOG;\n\n/**\n * Keys the import (BE derive-schema.ts, register-editable-pages.ts, template-copy.ts) writes onto a field for the\n * SERVER: how publish writes a value back into the site's own HTML (`attr`, `numeric`, `iconFont`, `htmlClasses`,\n * `units`, `lead`), which band a field belongs to (`band`, `family`, `shape`), and import markers (`seeded`, `shared`,\n * `sharedValue`). No form reads them, so no form loses anything by leaving them alone, and no agent may write them: the\n * backend MCP drops them from what an agent sends, and its contract test fails on any key the import writes that is\n * neither here, a host key, nor read by the kit (kit-catalog.json `fieldConfig`).\n */\nexport const SERVER_CONFIG_KEYS: ReadonlySet<string> = new Set([\n \"seeded\", \"shared\", \"sharedValue\", \"attr\", \"numeric\", \"iconFont\", \"htmlClasses\", \"units\", \"lead\", \"band\", \"family\", \"shape\",\n]);\n\n/** The prop config keys CATALOG \"host config\" covers: any type's, then a rich text's (richtext, document) own. */\nexport const HOST_CONFIG_KEYS: { any: ReadonlySet<string>; richtext: ReadonlySet<string> } = {\n // A band's name, its kind and its own heading, which the derive lane writes on the band's first field.\n // `format`: how a number is shown ($1,200+), which the canvas, the classic preview and publish read; the value stays a number\n any: new Set([\"sectionLabel\", \"sectionShape\", \"sectionTitle\", \"format\", ...SERVER_CONFIG_KEYS]),\n // `hostTag` is read from the stored field by the rich-text host, like the rest. `features` is a V2 facet now (RT0, facets.ts)\n richtext: new Set([\"accept\", \"allowMultipleParagraphs\", \"inline\", \"allowTargetBlank\", \"components\", \"hostTag\"]),\n};\n\nexport const isHostConfigKey = (type: string, key: string): boolean =>\n HOST_CONFIG_KEYS.any.has(key) || ((type === \"richtext\" || type === \"document\") && HOST_CONFIG_KEYS.richtext.has(key));\n\n/**\n * The prod config keys the kit adapter READS, per prod field type (`*` = every type): what an agent may write on a\n * field and still edit it in the kit. Exported to the backend with the host and server keys (kit-catalog.json\n * `fieldConfig`), where the MCP warns on any other key and its contract test checks what the import writes.\n * field-config-contract.test.ts proves each entry against `toTypeSchema`, so this list cannot claim a key the\n * adapter drops; a key missing here only costs a warning.\n */\nexport const KIT_READ_CONFIG_KEYS: Readonly<Record<string, readonly string[]>> = {\n \"*\": [\"placeholder\", \"zones\", \"options\", \"fields\", \"linkHref\"],\n text: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n longtext: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n url: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n richtext: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\", \"features\", \"references\"],\n document: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\", \"features\", \"references\"],\n number: [\"min\", \"max\", \"step\", \"scale\", \"unit\", \"validation\"],\n rating: [\"min\", \"max\", \"step\", \"scale\", \"unit\", \"validation\"],\n select: [\"multiSelect\"],\n multiselect: [\"min\", \"max\", \"allowCustom\"],\n tags: [\"min\", \"max\", \"allowCustom\"],\n code: [\"language\", \"languages\"],\n date: [\"timezone\", \"includeTime\"],\n datetime: [\"timezone\", \"includeTime\"],\n reference: [\"contentModelId\", \"polymorphic\", \"allowedModelSlugs\", \"targets\", \"filter\"],\n \"multi-reference\": [\"contentModelId\", \"polymorphic\", \"allowedModelSlugs\", \"targets\", \"filter\", \"min\", \"max\"],\n array: [\"itemType\", \"minItems\", \"maxItems\", \"item\"],\n image: [\"media\"],\n modular: [\"blockSlugs\", \"minItems\", \"maxItems\"],\n};\n\n/** The field-config contract the backend reads (kit-catalog.json `fieldConfig`). */\nexport const fieldConfigContract = () => ({\n read: KIT_READ_CONFIG_KEYS,\n host: { any: [...HOST_CONFIG_KEYS.any].filter((k) => !SERVER_CONFIG_KEYS.has(k)), richtext: [...HOST_CONFIG_KEYS.richtext] },\n server: [...SERVER_CONFIG_KEYS],\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"lossy.js","names":[],"sources":["../../src/adapters/lossy.ts"],"sourcesContent":["/**\n * LOSSY ITEMS — every V2 schema feature the production language cannot hold is named, never dropped silently.\n * CATALOG ITEMS (below) are the V2 metadata a host sets at seeding time, which the dock never stores.\n *\n * `path` is a JSON pointer into the V2 TypeSchema (\"/fields/3/of/0/fields/1/localized\"). `feature` is a stable name\n * from FEATURES (what the lossy ledger counts), `reason` says why prod cannot keep it, and FEATURES[feature].backlog\n * names what removes the loss.\n */\nexport interface LossyItem {\n path: string;\n feature: LossyFeature;\n reason: string;\n}\n\n/**\n * What removes a lossy feature: a named backend backlog item (its receipt is this feature's lossy count reaching 0), or\n * an explicit disposition that is not backend work: `corpus-fix` (the V2 schema breaks a prod rule the author can\n * satisfy: rename, shorten, reorder, drop a key the type cannot use), `dashboard:F12` (prod stores it; only the dock's\n * DockFieldTabs drops it, and the kit's GroupTabs replaces that at the dock cutover), `wontfix:depth` (a prod rule kept\n * on purpose: the backend refuses an 11th level with field_depth_exceeded), `wontfix:item-config` (the backend refuses\n * per-item config on a list of scalars on purpose, array_config_key_unknown), `wontfix:nesting` (the backend keeps a\n * slot's settings leaf fields, on purpose: slot_settings_invalid), `deferred:shopify` (owner 2026-10-11: a Shopify\n * reference stays locked text until the Shopify integration pipeline is built; allowed-deviations.json \"deferred\"). Owner 2026-10-11 (C11): the backend keeps a scalar list's\n * item settings (config.item), a list's bounds to 500, openMode on code, a slot's allow names (config.allow), a dotted\n * document preview path, the tabs no top-level prop sits in (components.tabs) and a nested field's tab (ui.tab { id }).\n * corpus-fix items are V2 corpus defects, an\n * owner note for the demo repo: the sandbox renders them from the V2 schema, seeding skips them.\n *\n * Landed on origin/dev and mapped here, so they name no feature any more: prop-reference (73a120c50), prop reference targets + filter (a4ce1ae95; shopify stays named), validation-parity\n * (dd021ceba), option-meta (ab15fe893), chrome-keys (98e48b9e3, 324580b2e), adapter:icons (the V2 icon vocabulary,\n * e126bf33d), localized-flag (279a10490), table / polymorphic list nesting (B1/B2: a data table or a polymorphic list at\n * any depth, in an object, a list's rows or an item type), slot nesting (C1/C2: a slot prop at any depth). What those\n * keys still cannot hold is a value the backend refuses, named below as corpus-fix.\n */\nexport type Backlog =\n | \"reference-targets\"\n | \"table\"\n | \"slot\"\n | \"block-model-fields\"\n | \"corpus-fix\"\n | \"dashboard:F12\"\n | \"wontfix:depth\"\n | \"wontfix:nesting\"\n | \"wontfix:item-config\"\n | \"deferred:shopify\";\n\nexport const FEATURES = {\n /* field types prod has no home for (the value is kept in a json field, or a near type) */\n \"type:table\": { backlog: \"table\", reason: \"Prod has no table field type (BE: a new type): the TableValue is stored verbatim in a json field.\" },\n \"type:slot\": { backlog: \"slot\", reason: \"Blocks inside a block on a content model: the backend's slot field (kind:'block' models by blockSlugs) is not mapped by this adapter yet, so it is stored as json. A component's top-level slot is a slot prop.\" },\n \"type:polymorphic-array\": { backlog: \"slot\", reason: \"An array of several item types: a model stores it as modular and a prop as a polymorphic list (table, config.mode \\\"types\\\"), both of object item types only. A list mixing scalar item types is stored as json.\" },\n \"item chrome (types)\": { backlog: \"slot\", reason: \"A polymorphic list's item type is { name, label?, fields, icon?, preview? } (item_types_invalid): it keeps its dock icon and row preview, and no other chrome.\" },\n \"type:primitive-array\": { backlog: \"wontfix:item-config\", reason: \"A list of this item type has no prod array form: config.itemType is text, number, date, image, link or richtext. Stored as json.\" },\n \"depth>10\": { backlog: \"wontfix:depth\", reason: \"Prod allows 10 field levels: the object at level 10 that would nest an 11th is stored as json.\" },\n /* document level */\n \"type name\": { backlog: \"corpus-fix\", reason: \"The schema name is stored as a kebab-case slug, which does not read back as this name.\" },\n /* tabs and sections */\n \"group name>40\": { backlog: \"corpus-fix\", reason: \"A tab name is at most 40 characters; truncated.\" },\n \"group title\": { backlog: \"corpus-fix\", reason: \"A prop tab's label is the group title trimmed (1–20 characters): a blank title defines no tab, so the group is not stored and its props sit in the default tab; spaces around a title are not kept.\" },\n \"group default\": { backlog: \"corpus-fix\", reason: \"The default tab is always the first one in prod.\" },\n \"group icon\": { backlog: \"corpus-fix\", reason: \"A tab icon is a dock icon name (DOCK_ICON_NAMES); this name is not one.\" },\n \"group (nested)\": { backlog: \"corpus-fix\", reason: \"A nested field names its tab as ui.tab { id } only when the tab is stored: this group is not (named on the group itself).\" },\n \"groups>3\": { backlog: \"dashboard:F12\", reason: \"The dock shows at most 3 tabs (DockFieldTabs), so the adapter keeps 3 until the kit GroupTabs replaces DockFieldTabs at the dock cutover: later groups' fields land in the default tab meanwhile.\" },\n \"group name collision\": { backlog: \"corpus-fix\", reason: \"Two tabs share an id, or a stored name (the first 20 characters, trimmed, any case): the backend refuses that (tab_name, fieldset ids), so the later tab is not stored.\" },\n \"prop tab limit\": { backlog: \"corpus-fix\", reason: \"A component holds at most 8 tabs, each with an id of 1–40 characters (the backend's ui.tab rule): this group is not stored and its props sit in the default tab.\" },\n \"fieldset spans groups\": { backlog: \"corpus-fix\", reason: \"A prod fieldset sits in one tab; this field's tab differs from its fieldset's.\" },\n \"fieldset (props)\": { backlog: \"corpus-fix\", reason: \"A prop group is defined on the first prop that uses it: a fieldset no prop uses is not stored (and the ones after it move up a place).\" },\n \"fieldset name>limit\": { backlog: \"corpus-fix\", reason: \"A fieldset name is at most 60 characters (a prop group label 32); truncated.\" },\n \"fieldset collision\": { backlog: \"corpus-fix\", reason: \"A fieldset shares its id with a group, or this field names a fieldset the schema does not define.\" },\n /* field chrome and rules */\n \"duplicate name\": { backlog: \"corpus-fix\", reason: \"Two sibling fields share a name; prod keys are unique per level, so the later one is renamed.\" },\n \"reserved name\": {\n backlog: \"corpus-fix\",\n reason: \"A model field's key is not one the API reserves (types' KEY_RESERVED: derived, layout, page, a forbidden key or a leading _) and is a letter, then letters, digits, - and _ (KEY_FORMAT): this one is stored renamed. id is allowed.\",\n },\n rows: { backlog: \"corpus-fix\", reason: \"ui.rows is a whole number from 1 to 40, on a text or long text field only.\" },\n synced: { backlog: \"corpus-fix\", reason: \"ui.synced names its global in 1–64 characters (trimmed).\" },\n valueShape: { backlog: \"corpus-fix\", reason: \"ui.valueShape is for a link, image or button field only.\" },\n \"default value\": { backlog: \"corpus-fix\", reason: \"This initial value holds what the prod value cannot keep (named under it): a crop preset with no image size, a datetime without seconds, a button icon outside the dock vocabulary…\" },\n slotSettings: { backlog: \"wontfix:nesting\", reason: \"A slot's per-child settings are at most 20 leaf fields (slot_settings_invalid): this setting is an object, a list, a table, a slot or a form, or past the 20th, or the field holding them is not a slot.\" },\n icon: { backlog: \"corpus-fix\", reason: \"A field icon is ui.icon, a dock icon name, on a nesting field only (an object, a list of objects, a modular list); this field is a leaf, or the name is not a dock icon.\" },\n layout: { backlog: \"corpus-fix\", reason: \"This layout does not fit the field in prod: ui.control is dropdown / radio / grid on a select, cards on a select or multiselect, checkbox on a boolean or multiselect, switch on a boolean, segmented on a select of 2–4 options, slider on a number with min < max (≤ 1000 stops); a list of scalars or entries lays out as rows or a gallery.\" },\n \"help>limit\": { backlog: \"corpus-fix\", reason: \"Help text is at most 500 characters on a field and 200 on a prop (a placeholder 120, a prop group description 200); truncated.\" },\n \"option icon\": { backlog: \"corpus-fix\", reason: \"Option icons are dock icon names, all or none per list: an icon in this list is not a dock name, so the list stores none (option_icon_unknown).\" },\n \"option description\": { backlog: \"corpus-fix\", reason: \"An option description is one line of 1–140 characters (option_description_invalid).\" },\n condition: { backlog: \"corpus-fix\", reason: \"This condition has no prod showIf form: it names something that is not a sibling (the block's _variant is one only for a top-level prop: show_if_variant_field), names it by `scope` (prod resolves a rule at the nearest level that declares its field), or tests a value showIf cannot.\" },\n access: { backlog: \"corpus-fix\", reason: \"ui.access takes view \\\"schema.view\\\" and edit \\\"schema.manage\\\" only (access_cap_unknown), and a developer lock (readOnly \\\"developer\\\") is not also a plain read-only flag.\" },\n timezone: { backlog: \"corpus-fix\", reason: \"A field's time zone is true, false, \\\"site\\\" or an IANA zone name, on a date or datetime.\" },\n \"richtext references\": {\n backlog: \"corpus-fix\",\n reason: \"A rich text's references (config.references) are { posts?, authors? }, each a content-model id (letters, digits, - and _, 1-64), on a rich text or document field only: anything else is not stored.\",\n },\n \"richtext features\": {\n backlog: \"corpus-fix\",\n reason: \"A rich text's features (config.features) are distinct names from types RICHTEXT_FEATURES, on a rich text or document field only: a name outside it is not stored, and the rest are.\",\n },\n \"shopify reference\": {\n backlog: \"deferred:shopify\",\n reason:\n \"A prop reference to Shopify: no Shopify integration backs one (reference_target_unsupported), so a Shopify-only reference is locked text holding the product id (SHOPIFY_REF_LOCK) and a mixed one keeps its other targets. Owner 2026-10-11: deferred until the Shopify integration pipeline is built (allowed-deviations.json status deferred).\",\n },\n \"reference target\": { backlog: \"reference-targets\", reason: \"A prop reference keeps entry models (contentModelId / allowedModelSlugs) and page, form and collection (config.targets, backend a4ce1ae95); Shopify has no integration behind it (reference_target_unsupported), so a shopify target is dropped and a reference with only shopify is stored as json. A content-model field keeps entry models only.\" },\n \"reference order\": { backlog: \"corpus-fix\", reason: \"Prod keeps a reference's entry models apart from its page / form / collection targets: the list reads back entry models first, then the targets.\" },\n \"reference filter\": { backlog: \"reference-targets\", reason: \"A prop reference keeps its filter as config.filter (1–500 characters, no $parameters or ^ parent: reference_filter_invalid); a content-model field keeps none.\" },\n \"numeric unit\": { backlog: \"corpus-fix\", reason: \"config.unit is a number's (1–24 characters); a rating has no unit.\" },\n \"rating bounds\": { backlog: \"corpus-fix\", reason: \"A prod rating runs from config.min (≥ 0) to config.max (a whole 1–10) in steps of config.step, min < max and at most 100 whole steps apart.\" },\n \"scale rules\": { backlog: \"corpus-fix\", reason: \"This scale breaks prod's scale rules: token values 1–32 characters and distinct, labels 1–24, at most 50 steps and 20 presets, a token preset naming a step, a { value, unit } preset needing allowCustom.\" },\n \"validation.patternMessage\": { backlog: \"corpus-fix\", reason: \"A pattern message needs a stored pattern and is 1–200 characters.\" },\n \"validation.pattern\": { backlog: \"corpus-fix\", reason: \"A pattern is kept on text, long text and url values, a regex of at most 200 characters that compiles.\" },\n \"validation.length\": { backlog: \"corpus-fix\", reason: \"Length limits are whole numbers ≥ 0, on text, long text, url and rich text values.\" },\n \"validation.items\": { backlog: \"corpus-fix\", reason: \"Item limits are whole numbers ≥ 0 on a list, min ≤ max: a tags max 1–500 and min 0–500, a prop's own list (table, array) at most 500 (LIST_ITEMS_CAP), a slot's at most 50 (SLOT_ITEMS_CAP).\" },\n \"validation.unique\": { backlog: \"corpus-fix\", reason: \"unique is for text, long text, slug, email, phone, link, number, select, date and single-model reference fields, and lists; a polymorphic reference has no one id to compare.\" },\n \"validation.recommendedMax\": { backlog: \"corpus-fix\", reason: \"A soft limit is a positive whole number in prod.\" },\n \"item identity\": { backlog: \"corpus-fix\", reason: \"A list item's name is an identifier (a letter, then letters, digits or _; ≤ 64) and its title 1–60 characters (ui.itemName / ui.itemTitle); an object item keeps no other key.\" },\n \"item settings\": { backlog: \"corpus-fix\", reason: \"A list of scalars keeps its item's description (1–200 characters), localized flag, required, a text or rich text item's length and a text item's pattern (config.item, array_item_settings_invalid): this setting is none of those, or out of their bounds (a multi-line text item reads back single-line).\" },\n itemNoun: { backlog: \"corpus-fix\", reason: \"The item noun is ui.itemLabel, capitalised, at most 24 characters.\" },\n \"block model field\": { backlog: \"block-model-fields\", reason: \"A modular item type is a kind:'block' model, which holds only fields a block primitive renders (text, long text, rich text, image, link, slug, email, phone, color, select, number, rating, date, datetime, boolean): this field is not stored.\" },\n \"item icon\": { backlog: \"corpus-fix\", reason: \"Prod keeps one icon per list (ui.icon, a dock name) and one per polymorphic item type: the list has its own icon, or this name is not a dock icon.\" },\n \"preview icon\": { backlog: \"corpus-fix\", reason: \"A row preview's icon is a dock icon name.\" },\n \"preview path\": { backlog: \"corpus-fix\", reason: \"A row preview names a child key, or a dotted path whose later segments are children or keys of an image, video, link, button, file, location, json, code or reference value, or a data table's caption or notes (preview_path_unknown).\" },\n openMode: { backlog: \"corpus-fix\", reason: \"openMode is for object, array, table and code fields only (openMode_leaf).\" },\n \"unsupported key\": { backlog: \"corpus-fix\", reason: \"This V2 key has no prod mapping.\" },\n} as const satisfies Record<string, { reason: string; backlog: Backlog }>;\n\nexport type LossyFeature = keyof typeof FEATURES;\n\n/**\n * CATALOG ITEMS — V2 metadata the dock never stores, set when a schema is seeded or placed by its host: not a loss of\n * stored content. Reported apart from LossyItem so the lossy ledger counts only what prod drops.\n */\nexport interface CatalogItem {\n path: string;\n feature: CatalogFeature;\n reason: string;\n}\n\nexport const CATALOG = {\n \"block icon\": \"The block picker's icon: a component stores it as component.icon and a model as content_models.icon, both a dock icon, so only one with no dock name for it lands here.\",\n category: \"The block picker's category is catalog metadata. A component takes the nearest prod category (CATEGORY_TO_PROD) when there is one; a model has none.\",\n \"document preview\": \"The document-level preview (how a row of this type reads in a parent's list) has no place on a model; a component stores it as component.preview.\",\n \"document preview path\": \"component.preview names a top-level prop key or a dotted path into one (`slides.0.heading`, componentPreviewRefusals); a key no prop has, or a path through a renamed value key (`image.alt`), is not stored (preview_key_unknown), so the slot row reads the child's first text instead.\",\n \"kind:block (fields)\": \"A section's fields are stored as a model (kind model): which host places it is set at seeding.\",\n \"kind:global\": \"A global (synced) block is a host-level placement; its fields are stored as a model or a component.\",\n \"kind:page\": \"A page schema is stored as a model; the host makes it a page.\",\n \"kind:object\": \"An object type is stored as a model.\",\n \"kind:entry (props)\": \"Only blocks become components; the host decides what an entry schema is placed as.\",\n variants: \"Variants are separate components sharing a sectionType in prod: the host seeds one per variant.\",\n ai: \"Not lost since A3: a field and a prop, at any depth, store the AI-assist sparkle (`ai`).\",\n \"item openMode\": \"Prod keeps one openMode per list, the array's own; the V2 engine reads only that one too (lib/schema.ts itemOpenMode ignores the item's), so the item's changes nothing the dock shows.\",\n \"itemNoun (derived)\": \"A list with no itemNoun whose item title is not the field title's singular: ui.itemLabel keeps the item title, and the read side names the noun from it, which is the noun the V2 engine already falls back to (lib/schema.ts itemNoun).\",\n \"fieldset (derived)\": \"A field names a fieldset the schema does not declare: the V2 engine draws it as a section titled by its name (engine/field-list.tsx arrange), so it is stored as that prop group and the read side declares it. Not a loss.\",\n \"prop target\": \"A V2 prop has no binding path, so fromTypeSchema('props') returns none: seeding binds each prop to its target. Not a loss.\",\n \"block placement\": \"Where a block fits (inline / section / page) is the host's: the block picker and a body's Insert menu place it; a model or component carries none.\",\n \"slot allow\": \"A slot keeps its V2 allow list as config.allow (1–50 block names of 1–64 characters, no repeats: slot_allow_invalid), which the dock resolves to the project's components; this list breaks those bounds, so the slot takes any component.\",\n \"segmented fallback\": \"A segmented select of more than SEGMENTED_MAX (8) options: V2 draws the dropdown (D46, choice.tsx fitsSegmented) and prod stores the select with no control (control_segmented holds 2–8), which the dock draws as the same dropdown. Nothing drawn differs.\",\n \"seeded instance\": \"A starting value the host writes into a new instance when it seeds or places one: a schema-level initialValue, a reference prop's default (a definition holds none: reference_in_definition), or a slot's children (they name V2 block types, which become component instances at seeding).\",\n \"not applicable (props)\": \"unique on a scalar prop, or on a sub-field outside a table: each placement holds its own value, so there is nothing to be unique across (unique_prop_scope). The instance is the whole document.\",\n \"host config\": \"A prod-only prop config key the host reads, not the V2 schema: a rich text's Accept list, its one-paragraph flags (allowMultipleParagraphs, the older inline), allowTargetBlank and component allowlist (the dock's rich-text codec and insert menu read them from the stored prop: dock-v2 richTextCodec configAt, kitServices insertableComponents), a section label, and the import's write-back metadata no editor reads (SERVER_CONFIG_KEYS). Read back, it names no loss: the kit never writes a definition, so the config rides untouched.\",\n} as const;\n\nexport type CatalogFeature = keyof typeof CATALOG;\n\n/**\n * Keys the import (BE derive-schema.ts, register-editable-pages.ts, template-copy.ts) writes onto a field for the\n * SERVER: how publish writes a value back into the site's own HTML (`attr`, `numeric`, `iconFont`, `htmlClasses`,\n * `units`, `lead`), which band a field belongs to (`band`, `family`, `shape`), and import markers (`seeded`, `shared`,\n * `sharedValue`). No form reads them, so no form loses anything by leaving them alone, and no agent may write them: the\n * backend MCP drops them from what an agent sends, and its contract test fails on any key the import writes that is\n * neither here, a host key, nor read by the kit (kit-catalog.json `fieldConfig`).\n */\nexport const SERVER_CONFIG_KEYS: ReadonlySet<string> = new Set([\n \"seeded\", \"shared\", \"sharedValue\", \"attr\", \"numeric\", \"iconFont\", \"htmlClasses\", \"units\", \"lead\", \"band\", \"family\", \"shape\",\n]);\n\n/** The prop config keys CATALOG \"host config\" covers: any type's, then a rich text's (richtext, document) own. */\nexport const HOST_CONFIG_KEYS: { any: ReadonlySet<string>; richtext: ReadonlySet<string> } = {\n // A band's name, its kind and its own heading, which the derive lane writes on the band's first field.\n // `format`: how a number is shown ($1,200+), which the canvas, the classic preview and publish read; the value stays a number\n any: new Set([\"sectionLabel\", \"sectionShape\", \"sectionTitle\", \"format\", ...SERVER_CONFIG_KEYS]),\n // `hostTag` is read from the stored field by the rich-text host, like the rest. `features` is a V2 facet now (RT0, facets.ts)\n richtext: new Set([\"accept\", \"allowMultipleParagraphs\", \"inline\", \"allowTargetBlank\", \"components\", \"hostTag\"]),\n};\n\nexport const isHostConfigKey = (type: string, key: string): boolean =>\n HOST_CONFIG_KEYS.any.has(key) || ((type === \"richtext\" || type === \"document\") && HOST_CONFIG_KEYS.richtext.has(key));\n\n/**\n * The prod config keys the kit adapter READS, per prod field type (`*` = every type): what an agent may write on a\n * field and still edit it in the kit. Exported to the backend with the host and server keys (kit-catalog.json\n * `fieldConfig`), where the MCP warns on any other key and its contract test checks what the import writes.\n * field-config-contract.test.ts proves each entry against `toTypeSchema`, so this list cannot claim a key the\n * adapter drops; a key missing here only costs a warning.\n */\nexport const KIT_READ_CONFIG_KEYS: Readonly<Record<string, readonly string[]>> = {\n \"*\": [\"placeholder\", \"zones\", \"options\", \"fields\", \"linkHref\"],\n text: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n longtext: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n url: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\"],\n richtext: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\", \"features\", \"references\"],\n document: [\"minLength\", \"maxLength\", \"pattern\", \"patternMessage\", \"inputType\", \"features\", \"references\"],\n number: [\"min\", \"max\", \"step\", \"scale\", \"unit\", \"validation\"],\n rating: [\"min\", \"max\", \"step\", \"scale\", \"unit\", \"validation\"],\n select: [\"multiSelect\"],\n multiselect: [\"min\", \"max\", \"allowCustom\"],\n tags: [\"min\", \"max\", \"allowCustom\"],\n code: [\"language\", \"languages\"],\n date: [\"timezone\", \"includeTime\"],\n datetime: [\"timezone\", \"includeTime\"],\n reference: [\"contentModelId\", \"polymorphic\", \"allowedModelSlugs\", \"targets\", \"filter\"],\n \"multi-reference\": [\"contentModelId\", \"polymorphic\", \"allowedModelSlugs\", \"targets\", \"filter\", \"min\", \"max\"],\n array: [\"itemType\", \"minItems\", \"maxItems\", \"item\"],\n image: [\"media\"],\n modular: [\"blockSlugs\", \"minItems\", \"maxItems\"],\n};\n\n/** The field-config contract the backend reads (kit-catalog.json `fieldConfig`). */\nexport const fieldConfigContract = () => ({\n read: KIT_READ_CONFIG_KEYS,\n host: { any: [...HOST_CONFIG_KEYS.any].filter((k) => !SERVER_CONFIG_KEYS.has(k)), richtext: [...HOST_CONFIG_KEYS.richtext] },\n server: [...SERVER_CONFIG_KEYS],\n});\n"],"mappings":"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"}
|
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PROD SHAPES — the production schema language and stored values, typed locally. The caps and key rules the backend
|
|
3
|
+
* and the kit must agree on come from @bettercms-ai/types (W1a, a peer of the kit since W1c): one home per constant.
|
|
4
|
+
*
|
|
5
|
+
* Mirrors the backend at F1 + F1.x-props + link objects + tags / rating / code (72ea2b68d), then reference props
|
|
6
|
+
* (73a120c50), validation parity (dd021ceba), option meta (ab15fe893), V2 chrome keys and types (98e48b9e3, 324580b2e),
|
|
7
|
+
* the V2 icon vocabulary (e126bf33d) and the localized flag (279a10490) — origin/dev 81f385ff3:
|
|
8
|
+
* packages/types/src/content-model.ts, component.ts, link-value.ts, button-look.ts, dock-icon-names.ts, video-value.ts
|
|
9
|
+
* src/lib/validation/schemas/content.ts (baseField, fieldUiSchema, chromeUiProblems, fieldsets, MAX_FIELD_DEPTH)
|
|
10
|
+
* src/lib/validation/schemas/components.ts (componentPropDefSchema, subFieldSchema, propUiSchema, propValidationHardening)
|
|
11
|
+
* src/lib/content/validation-parity.ts, src/lib/content/dock-vocabulary.ts (TAB_MAX, TAB_NAME_MAX)
|
|
12
|
+
* When the backend widens, widen these and the adapters together; the round-trip test then shrinks the lossy ledger.
|
|
13
|
+
*/
|
|
14
|
+
import { type KitCategory, type V2Placement, type V2Shape } from "@bettercms-ai/types";
|
|
1
15
|
/** Field levels one schema may nest (a top-level field, or a prop, is level 1). */
|
|
2
16
|
export declare const MAX_FIELD_DEPTH = 10;
|
|
3
|
-
/** Tabs (`kind: "tab"` fieldsets) a model screen holds
|
|
4
|
-
export declare const TAB_MAX =
|
|
17
|
+
/** Tabs (`kind: "tab"` fieldsets) a model screen holds (types' TAB_MAX: no render cap, the dock folds the rest into More), and a tab name's length. */
|
|
18
|
+
export declare const TAB_MAX = 8;
|
|
5
19
|
export declare const TAB_NAME_MAX = 40;
|
|
6
20
|
export declare const FIELDSETS_MAX = 30;
|
|
7
21
|
export declare const FIELDSET_NAME_MAX = 60;
|
|
@@ -51,7 +65,7 @@ export declare const SCALE_STEPS_MAX = 50;
|
|
|
51
65
|
export declare const SCALE_PRESETS_MAX = 20;
|
|
52
66
|
/** The video block's transcript cap (VIDEO_TRANSCRIPT_MAX). */
|
|
53
67
|
export declare const VIDEO_TRANSCRIPT_MAX = 100000;
|
|
54
|
-
export declare const DOCK_ICON_NAMES: readonly ["abc", "access-denied", "activity", "add", "add-circle", "add-comment", "add-document", "add-user", "address-book", "adjustments", "adjustments-horizontal", "alarm", "alert-triangle", "align-left", "api", "app-window", "apple", "apps", "archive", "arrow-back-up", "arrow-down", "arrow-guide", "arrow-left", "arrow-right", "arrow-top-right", "arrow-up", "arrows-diagonal", "arrows-down", "arrows-exchange", "arrows-horizontal", "arrows-left-right", "arrows-shuffle", "arrows-sort", "article", "aspect-ratio", "asterisk", "award", "axis-x", "backpack", "badge", "badge-cc", "badges", "bar-chart", "basket", "bell", "bill", "binary-document", "block-content", "block-element", "blockquote", "bold", "bolt", "book", "book-2", "bookmark", "bookmark-filled", "books", "bottle", "box", "brand-dribbble", "brand-facebook", "brand-instagram", "brand-linkedin", "brand-pinterest", "brand-tiktok", "brand-x", "brand-youtube", "briefcase", "broadcast", "browser", "bug", "building", "building-skyscraper", "building-store", "bulb-filled", "bulb-outline", "calculator", "calendar", "calendar-dollar", "calendar-event", "calendar-exclamation", "calendar-off", "calendar-plus", "calendar-star", "calendar-time", "camera", "campfire", "carousel-horizontal", "case", "cash", "category", "category-2", "chart-area", "chart-arrows", "chart-bar", "chart-bar-popular", "chart-donut", "chart-dots", "chart-line", "chart-upward", "check", "checkbox", "checklist", "checkmark", "checkmark-circle", "chef-hat", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-right", "circle", "circle-check", "circle-dot", "circle-number-1", "circle-plus", "click", "clipboard", "clipboard-image", "clipboard-text", "clock", "clock-check", "clock-hour-4", "close", "close-circle", "code", "code-block", "cog", "collapse", "color-wheel", "columns", "columns-2", "columns-3", "comment", "component", "compose", "compose-sparkles", "confetti", "controls", "cookie", "copy", "credit-card", "crop", "cube", "currency-dollar", "dashboard", "database", "database-search", "desktop", "device-desktop", "device-mobile", "device-tablet", "devices", "diamond", "discount", "document", "document-pdf", "document-remove", "document-sheet", "document-text", "document-video", "document-word", "document-zip", "documents", "door-exit", "dot", "double-chevron-down", "double-chevron-left", "double-chevron-right", "double-chevron-up", "double-quote", "download", "drag-handle", "drop", "droplet", "droplet-half", "earth-americas", "earth-globe", "edit", "egg", "ellipsis-horizontal", "ellipsis-vertical", "empty", "enter", "enter-right", "envelope", "equal", "error-filled", "error-outline", "error-screen", "expand", "eye-closed", "eye-off", "eye-open", "face-happy", "face-indifferent", "face-sad", "feather", "feedback", "file", "file-certificate", "file-code", "file-download", "file-text", "filter", "flag", "flame", "flask", "focus-2", "folder", "folders", "forms", "frame", "generate", "gift", "git-branch", "github", "grid-dots", "groq", "hammer", "hand-finger", "hanger", "hash", "headphones", "headset", "heart", "heart-filled", "heart-handshake", "help-circle", "highlight", "history", "home", "hourglass", "ice-cream", "id-badge", "id-badge-2", "image", "image-remove", "images", "inbox", "infinity", "info-circle", "info-filled", "info-outline", "info-square-rounded", "inline", "inline-element", "input-search", "insert-above", "insert-below", "italic", "joystick", "json", "language", "launch", "layout", "layout-align-center", "layout-board", "layout-board-split", "layout-bottombar", "layout-bottombar-expand", "layout-cards", "layout-columns", "layout-dashboard", "layout-grid", "layout-list", "layout-navbar", "layout-rows", "layout-sidebar", "layout-sidebar-right", "leaf", "leave", "lemon", "library-photo", "lifebuoy", "link", "link-removed", "linkedin", "list", "list-check", "list-details", "list-numbers", "list-tree", "lock", "logo-js", "logo-ts", "mail", "map-2", "map-pin", "map-pins", "marker", "marker-removed", "master-detail", "math-function", "meat", "menu", "message", "message-2", "message-check", "message-circle", "message-circle-2", "message-plus", "message-star", "microphone", "microphone-slash", "mobile-device", "mood-empty", "moon", "movie", "news", "notes", "number", "ok-hand", "olist", "overage", "package", "packages", "palette", "panel-left", "panel-right", "paperclip", "pause", "pencil", "phone", "photo", "photo-scan", "photo-search", "pig-money", "pin", "pin-filled", "pin-removed", "play", "player-play", "player-track-next", "playlist-add", "plug", "plug-connected", "plus-minus", "point", "presentation", "printer", "progress-50", "progress-75", "projects", "publish", "qrcode", "quote", "read-only", "receipt", "recycle", "redo", "refresh", "remove", "remove-circle", "repeat", "reset", "restore", "retrieve", "retry", "revert", "robot", "rocket", "rosette", "route", "ruler", "ruler-measure", "salad", "scale", "schema", "school", "search", "section", "select", "send", "server", "settings", "share", "shield-check", "shield-lock", "shopping-bag", "shopping-cart", "shopping-cart-plus", "signature", "sitemap", "slideshow", "sort", "sort-descending", "spacing-vertical", "sparkle", "sparkles", "speakerphone", "spinner", "split-horizontal", "split-vertical", "square", "stack", "stack-2", "stack-compact", "stairs", "stairs-up", "star", "star-filled", "stars", "stop", "strikethrough", "string", "subtask", "sun", "superscript", "sync", "table", "table-options", "tablet-device", "tag", "tags", "target", "task", "template", "tent", "terminal", "terminal-2", "text", "th-large", "th-list", "thumb-up", "thumbs-down", "thumbs-up", "ticket", "tiers", "timeline", "toggle-arrow-right", "toggle-right", "token", "tool", "tools-kitchen-2", "transfer", "translate", "trash", "trend-upward", "triangle-outline", "trolley", "truck", "truck-delivery", "truncate", "twitter", "typography", "ulist", "unarchive", "underline", "undo", "unknown", "unlink", "unlock", "unpublish", "upload", "user", "user-circle", "user-plus", "user-search", "user-star", "users", "users-group", "versions", "video", "vocabulary", "vs", "warning-filled", "warning-outline", "weight", "world", "wrench", "zoom-question"];
|
|
68
|
+
export declare const DOCK_ICON_NAMES: readonly ["abc", "access-denied", "activity", "add", "add-circle", "add-comment", "add-document", "add-user", "address-book", "adjustments", "adjustments-horizontal", "alarm", "alert-triangle", "align-left", "api", "app-window", "apple", "apps", "archive", "arrow-back-up", "arrow-down", "arrow-guide", "arrow-left", "arrow-right", "arrow-top-right", "arrow-up", "arrows-diagonal", "arrows-down", "arrows-exchange", "arrows-horizontal", "arrows-left-right", "arrows-shuffle", "arrows-sort", "article", "aspect-ratio", "asterisk", "award", "axis-x", "backpack", "badge", "badge-cc", "badges", "bar-chart", "basket", "bell", "bill", "binary-document", "block-content", "block-element", "blockquote", "bold", "bolt", "book", "book-2", "bookmark", "bookmark-filled", "books", "bottle", "box", "brand-dribbble", "brand-facebook", "brand-instagram", "brand-linkedin", "brand-pinterest", "brand-tiktok", "brand-x", "brand-youtube", "briefcase", "broadcast", "browser", "bug", "building", "building-skyscraper", "building-store", "bulb-filled", "bulb-outline", "calculator", "calendar", "calendar-dollar", "calendar-event", "calendar-exclamation", "calendar-off", "calendar-plus", "calendar-star", "calendar-time", "camera", "campfire", "carousel-horizontal", "case", "cash", "category", "category-2", "chart-area", "chart-arrows", "chart-bar", "chart-bar-popular", "chart-donut", "chart-dots", "chart-line", "chart-upward", "check", "checkbox", "checklist", "checkmark", "checkmark-circle", "chef-hat", "chevron-down", "chevron-left", "chevron-right", "chevron-up", "chevrons-right", "circle", "circle-check", "circle-dot", "circle-number-1", "circle-plus", "click", "clipboard", "clipboard-image", "clipboard-text", "clock", "clock-check", "clock-hour-4", "close", "close-circle", "code", "code-block", "cog", "collapse", "color-wheel", "columns", "columns-2", "columns-3", "comment", "component", "compose", "compose-sparkles", "confetti", "controls", "cookie", "copy", "credit-card", "crop", "cube", "currency-dollar", "dashboard", "database", "database-search", "desktop", "device-desktop", "device-mobile", "device-tablet", "devices", "diamond", "discount", "document", "document-pdf", "document-remove", "document-sheet", "document-text", "document-video", "document-word", "document-zip", "documents", "door-exit", "dot", "double-chevron-down", "double-chevron-left", "double-chevron-right", "double-chevron-up", "double-quote", "download", "drag-handle", "drop", "droplet", "droplet-half", "earth-americas", "earth-globe", "edit", "egg", "ellipsis-horizontal", "ellipsis-vertical", "empty", "enter", "enter-right", "envelope", "equal", "error-filled", "error-outline", "error-screen", "expand", "eye-closed", "eye-off", "eye-open", "face-happy", "face-indifferent", "face-sad", "feather", "feedback", "file", "file-certificate", "file-code", "file-download", "file-settings", "file-text", "filter", "flag", "flame", "flask", "focus-2", "folder", "folders", "forms", "frame", "generate", "gift", "git-branch", "github", "grid-dots", "groq", "hammer", "hand-finger", "hanger", "hash", "headphones", "headset", "heart", "heart-filled", "heart-handshake", "help-circle", "highlight", "history", "home", "hourglass", "ice-cream", "id-badge", "id-badge-2", "image", "image-remove", "images", "inbox", "infinity", "info-circle", "info-filled", "info-outline", "info-square-rounded", "inline", "inline-element", "input-search", "insert-above", "insert-below", "italic", "joystick", "json", "language", "launch", "layout", "layout-align-center", "layout-board", "layout-board-split", "layout-bottombar", "layout-bottombar-expand", "layout-cards", "layout-columns", "layout-dashboard", "layout-grid", "layout-list", "layout-navbar", "layout-rows", "layout-sidebar", "layout-sidebar-right", "leaf", "leave", "lemon", "library-photo", "lifebuoy", "link", "link-removed", "linkedin", "list", "list-check", "list-details", "list-numbers", "list-tree", "lock", "logo-js", "logo-ts", "mail", "map-2", "map-pin", "map-pins", "marker", "marker-removed", "master-detail", "math-function", "meat", "menu", "message", "message-2", "message-check", "message-circle", "message-circle-2", "message-plus", "message-star", "microphone", "microphone-slash", "mobile-device", "mood-empty", "moon", "movie", "news", "notes", "number", "ok-hand", "olist", "overage", "package", "packages", "palette", "panel-left", "panel-right", "paperclip", "pause", "pencil", "phone", "photo", "photo-scan", "photo-search", "pig-money", "pin", "pin-filled", "pin-removed", "play", "player-play", "player-track-next", "playlist-add", "plug", "plug-connected", "plus-minus", "point", "presentation", "printer", "progress-50", "progress-75", "projects", "publish", "qrcode", "quote", "read-only", "receipt", "recycle", "redo", "refresh", "remove", "remove-circle", "repeat", "reset", "restore", "retrieve", "retry", "revert", "robot", "rocket", "rosette", "route", "ruler", "ruler-measure", "salad", "scale", "schema", "school", "search", "section", "select", "send", "server", "settings", "share", "shield-check", "shield-lock", "shopping-bag", "shopping-cart", "shopping-cart-plus", "signature", "sitemap", "slideshow", "sort", "sort-descending", "spacing-vertical", "sparkle", "sparkles", "speakerphone", "spinner", "split-horizontal", "split-vertical", "square", "stack", "stack-2", "stack-compact", "stairs", "stairs-up", "star", "star-filled", "stars", "stop", "strikethrough", "string", "subtask", "sun", "superscript", "sync", "table", "table-options", "tablet-device", "tag", "tags", "target", "task", "template", "tent", "terminal", "terminal-2", "text", "th-large", "th-list", "thumb-up", "thumbs-down", "thumbs-up", "ticket", "tiers", "timeline", "toggle-arrow-right", "toggle-right", "token", "tool", "tools-kitchen-2", "transfer", "translate", "trash", "trend-upward", "triangle-outline", "trolley", "truck", "truck-delivery", "truncate", "twitter", "typography", "ulist", "unarchive", "underline", "undo", "unknown", "unlink", "unlock", "unpublish", "upload", "user", "user-circle", "user-plus", "user-search", "user-star", "users", "users-group", "versions", "video", "vocabulary", "vs", "warning-filled", "warning-outline", "weight", "world", "wrench", "zoom-question"];
|
|
55
69
|
export type DockIconName = (typeof DOCK_ICON_NAMES)[number];
|
|
56
70
|
export declare const DOCK_ICON_NAME_SET: ReadonlySet<string>;
|
|
57
71
|
export type ProdFieldType = "text" | "richtext" | "document" | "image" | "boolean" | "number" | "select" | "reference" | "multi-reference" | "multiReference" | "array" | "date" | "datetime" | "longtext" | "slug" | "email" | "phone" | "link" | "color" | "json" | "file" | "component-ref" | "modular" | "sections" | "location" | "button" | "multiselect" | "icon" | "object" | "tags" | "rating" | "code"
|
|
@@ -66,6 +80,8 @@ export interface ShowIfRule {
|
|
|
66
80
|
field: string;
|
|
67
81
|
op: ShowIfOp;
|
|
68
82
|
value?: string | string[];
|
|
83
|
+
/** past the siblings (A3, showIfScopeSchema): the parent's level, or the document's top level */
|
|
84
|
+
scope?: "parent" | "root";
|
|
69
85
|
}
|
|
70
86
|
export interface ShowIf {
|
|
71
87
|
match: "all" | "any";
|
|
@@ -260,6 +276,8 @@ export interface ContentModelField {
|
|
|
260
276
|
openMode?: OpenMode;
|
|
261
277
|
helpMore?: string;
|
|
262
278
|
advanced?: boolean;
|
|
279
|
+
/** the AI-assist sparkle (A3: baseField and a prop's `ai`) */
|
|
280
|
+
ai?: boolean;
|
|
263
281
|
recommendedMax?: number;
|
|
264
282
|
fieldsetId?: string;
|
|
265
283
|
placement?: "cover" | "title" | "excerpt" | "body" | "inline" | "panel";
|
|
@@ -291,8 +309,22 @@ export interface ProdContentModel {
|
|
|
291
309
|
slug: string;
|
|
292
310
|
description?: string;
|
|
293
311
|
kind?: "model" | "block";
|
|
312
|
+
/** the collection's dock icon (content_models.icon, migration 0458; modelIconSchema) */
|
|
313
|
+
icon?: DockIconName;
|
|
294
314
|
fields: ContentModelField[];
|
|
295
315
|
fieldsets?: Fieldset[];
|
|
316
|
+
/** how an entry row reads in the list (content_models.list_preview, widened to V2 PreviewConfig by A3) */
|
|
317
|
+
listPreview?: ComponentPreview | null;
|
|
318
|
+
/** content_models.v2 (A3, contentModelV2Schema): what V2 says that has no native column */
|
|
319
|
+
v2?: {
|
|
320
|
+
shape?: V2Shape;
|
|
321
|
+
placement?: V2Placement;
|
|
322
|
+
category?: KitCategory;
|
|
323
|
+
variants?: Record<string, {
|
|
324
|
+
icon?: DockIconName;
|
|
325
|
+
description?: string;
|
|
326
|
+
}>;
|
|
327
|
+
};
|
|
296
328
|
}
|
|
297
329
|
export declare const COMPONENT_PROP_TYPES: readonly ["text", "richtext", "image", "url", "boolean", "number", "select", "group", "table", "slot", "form", "link", "button", "date", "color", "file", "icon", "multiselect", "object", "datetime", "slug", "email", "phone", "json", "location", "tags", "rating", "code", "reference", "multi-reference", "video", "array", "longtext"];
|
|
298
330
|
export type ComponentPropType = (typeof COMPONENT_PROP_TYPES)[number];
|
|
@@ -342,6 +374,8 @@ export interface ComponentSubField {
|
|
|
342
374
|
options?: SelectOption[];
|
|
343
375
|
helpMore?: string;
|
|
344
376
|
advanced?: boolean;
|
|
377
|
+
/** the AI-assist sparkle (A3: baseField and a prop's `ai`) */
|
|
378
|
+
ai?: boolean;
|
|
345
379
|
recommendedMax?: number;
|
|
346
380
|
/** on a table's sub-field: no two rows share it; on a list: no item repeats. */
|
|
347
381
|
unique?: boolean;
|
|
@@ -378,6 +412,8 @@ export interface ComponentPropDef {
|
|
|
378
412
|
showIf?: ShowIf;
|
|
379
413
|
helpMore?: string;
|
|
380
414
|
advanced?: boolean;
|
|
415
|
+
/** the AI-assist sparkle (A3: baseField and a prop's `ai`) */
|
|
416
|
+
ai?: boolean;
|
|
381
417
|
recommendedMax?: number;
|
|
382
418
|
/** a list prop's items (a table's rows) distinct; refused on a top-level scalar (unique_prop_scope). */
|
|
383
419
|
unique?: boolean;
|
|
@@ -445,13 +481,21 @@ export interface ProdComponent {
|
|
|
445
481
|
name: string;
|
|
446
482
|
slug: string;
|
|
447
483
|
description?: string;
|
|
448
|
-
category
|
|
484
|
+
/** a library id, or since A3 the kit catalogue's own category verbatim (CATEGORY_ORDER) */
|
|
485
|
+
category?: ComponentCategory | KitCategory;
|
|
449
486
|
props: ComponentPropDef[];
|
|
450
487
|
preview?: ComponentPreview | null;
|
|
451
488
|
/** The component's dock icon (backend `components.icon`, dockIconSchema): V2's block icon. */
|
|
452
489
|
icon?: DockIconName | null;
|
|
453
490
|
/** The V2 tabs no top-level prop sits in (backend `components.tabs`, componentTabsSchema): declared, shown by none. */
|
|
454
491
|
tabs?: PropTab[] | null;
|
|
492
|
+
/** components.v2 (A3, componentV2Schema): category and icon are native columns */
|
|
493
|
+
v2?: {
|
|
494
|
+
shape?: V2Shape;
|
|
495
|
+
placement?: V2Placement;
|
|
496
|
+
};
|
|
497
|
+
/** the section family it is a variant of (its label): every member of a V2 block's variant family names the block */
|
|
498
|
+
sectionType?: string;
|
|
455
499
|
}
|
|
456
500
|
export declare const LINK_RELS: readonly ["nofollow", "sponsored", "ugc"];
|
|
457
501
|
export type LinkRel = (typeof LINK_RELS)[number];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
-
import { DEPTH_MAX, HELP_CAPS, TAB_MAX as TAB_MAX$1
|
|
2
|
+
import { DEPTH_MAX, HELP_CAPS, TAB_MAX as TAB_MAX$1 } from "@bettercms-ai/types";
|
|
3
3
|
//#region src/adapters/prod-types.ts
|
|
4
4
|
/**
|
|
5
5
|
* PROD SHAPES — the production schema language and stored values, typed locally. The caps and key rules the backend
|
|
@@ -71,8 +71,8 @@ var prod_types_exports = /* @__PURE__ */ __exportAll({
|
|
|
71
71
|
});
|
|
72
72
|
/** Field levels one schema may nest (a top-level field, or a prop, is level 1). */
|
|
73
73
|
var MAX_FIELD_DEPTH = DEPTH_MAX;
|
|
74
|
-
/** Tabs (`kind: "tab"` fieldsets) a model screen holds
|
|
75
|
-
var TAB_MAX =
|
|
74
|
+
/** Tabs (`kind: "tab"` fieldsets) a model screen holds (types' TAB_MAX: no render cap, the dock folds the rest into More), and a tab name's length. */
|
|
75
|
+
var TAB_MAX = TAB_MAX$1;
|
|
76
76
|
var TAB_NAME_MAX = 40;
|
|
77
77
|
var FIELDSETS_MAX = 30;
|
|
78
78
|
var FIELDSET_NAME_MAX = 60;
|
|
@@ -326,6 +326,7 @@ var DOCK_ICON_NAMES = [
|
|
|
326
326
|
"file-certificate",
|
|
327
327
|
"file-code",
|
|
328
328
|
"file-download",
|
|
329
|
+
"file-settings",
|
|
329
330
|
"file-text",
|
|
330
331
|
"filter",
|
|
331
332
|
"flag",
|