@bettercms-ai/kit 2.0.0-next.7 → 2.0.0-next.8
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prod-types.js","names":[],"sources":["../../src/adapters/prod-types.ts"],"sourcesContent":["/**\n * PROD SHAPES — the production schema language and stored values, typed locally. The caps and key rules the backend\n * and the kit must agree on come from @bettercms-ai/types (W1a, a peer of the kit since W1c): one home per constant.\n *\n * Mirrors the backend at F1 + F1.x-props + link objects + tags / rating / code (72ea2b68d), then reference props\n * (73a120c50), validation parity (dd021ceba), option meta (ab15fe893), V2 chrome keys and types (98e48b9e3, 324580b2e),\n * the V2 icon vocabulary (e126bf33d) and the localized flag (279a10490) — origin/dev 81f385ff3:\n * packages/types/src/content-model.ts, component.ts, link-value.ts, button-look.ts, dock-icon-names.ts, video-value.ts\n * src/lib/validation/schemas/content.ts (baseField, fieldUiSchema, chromeUiProblems, fieldsets, MAX_FIELD_DEPTH)\n * src/lib/validation/schemas/components.ts (componentPropDefSchema, subFieldSchema, propUiSchema, propValidationHardening)\n * src/lib/content/validation-parity.ts, src/lib/content/dock-vocabulary.ts (TAB_MAX, TAB_NAME_MAX)\n * When the backend widens, widen these and the adapters together; the round-trip test then shrinks the lossy ledger.\n */\nimport { DEPTH_MAX, HELP_CAPS, TAB_MAX as SHARED_TAB_MAX, type KitCategory, type V2Placement, type V2Shape } from \"@bettercms-ai/types\";\n\n/* ── Limits the backend enforces ─────────────────────────────────────── */\n/** Field levels one schema may nest (a top-level field, or a prop, is level 1). */\nexport const MAX_FIELD_DEPTH = DEPTH_MAX;\n/** 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. */\nexport const TAB_MAX = SHARED_TAB_MAX;\nexport const TAB_NAME_MAX = 40;\nexport const FIELDSETS_MAX = 30;\nexport const FIELDSET_NAME_MAX = 60;\nexport const FIELDSET_DESCRIPTION_MAX = HELP_CAPS.fieldset;\nexport const FIELD_HELP_MAX = HELP_CAPS.field;\nexport const PROP_HELP_MAX = HELP_CAPS.prop;\nexport const PROP_PLACEHOLDER_MAX = HELP_CAPS.placeholder;\nexport const HELP_MORE_MAX = HELP_CAPS.helpMore;\nexport const LOCK_REASON_MAX = 200;\nexport const ITEM_LABEL_MAX = 24;\nexport const PREVIEW_FALLBACK_MAX = 60;\nexport const PROP_GROUP_LABEL_MAX = 32;\nexport const PATTERN_MAX = 200;\n/** A tags value's tag length (TAG_VALUE_MAX_LENGTH), a code value's length (CODE_MAX_LENGTH), a rating's max with none set. */\nexport const TAG_VALUE_MAX_LENGTH = 64;\nexport const CODE_MAX_LENGTH = 100_000;\nexport const RATING_DEFAULT_MAX = 5;\n/** ratingConfigSchema: config.max a whole 1–10; (max − min) / step whole and at most this many stops. */\nexport const RATING_MAX = 10;\nexport const RATING_MAX_STOPS = 100;\n/** tagsConfigSchema: config.min 0–500, config.max 1–500. */\nexport const TAGS_ITEMS_MAX = 500;\n/** A choice option's description (OPTION_DESCRIPTION_MAX): one line. */\nexport const OPTION_DESCRIPTION_MAX = HELP_CAPS.option;\n/** config.patternMessage (PATTERN_MESSAGE_MAX), config.unit on a number (NUMBER_UNIT_MAX). */\nexport const PATTERN_MESSAGE_MAX = 200;\nexport const NUMBER_UNIT_MAX = 24;\n/** fieldUiSchema: ui.rows 1–40, ui.placeholder 1–120, ui.itemTitle 1–60, ui.synced.from 1–64. */\nexport const UI_ROWS_MAX = 40;\nexport const UI_PLACEHOLDER_MAX = HELP_CAPS.placeholder;\nexport const ITEM_TITLE_MAX = 60;\nexport const SYNCED_FROM_MAX = 64;\n/** ui.itemName is an identifier: a letter, then letters, digits or _ (≤ 64). */\nexport const ITEM_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\n/** propUiSchema group: description 1–200, order 0–99. */\nexport const PROP_GROUP_DESCRIPTION_MAX = HELP_CAPS.propGroup;\nexport const PROP_GROUP_ORDER_MAX = 99;\n/** propUiSchema tab: at most 8 distinct tabs per component, an id 1–40, a label ≤ TAB_NAME_MAX trimmed, order 0–99. */\nexport const PROP_TAB_MAX = SHARED_TAB_MAX;\nexport const PROP_TAB_ID_MAX = 40;\n/** numberScaleSchema: token value 1–32, label 1–24, title 1–60, css 1–60; 1–50 steps; at most 20 presets. */\nexport const SCALE_TOKEN_VALUE_MAX = 32;\nexport const SCALE_LABEL_MAX = 24;\nexport const SCALE_TITLE_MAX = 60;\nexport const SCALE_CSS_MAX = 60;\nexport const SCALE_STEPS_MAX = 50;\nexport const SCALE_PRESETS_MAX = 20;\n/** The video block's transcript cap (VIDEO_TRANSCRIPT_MAX). */\nexport const VIDEO_TRANSCRIPT_MAX = 100_000;\n\n/* ── Icons: the closed dock vocabulary (packages/types/src/dock-icon-names.ts at 282655543: 473 names, Sanity's set\n * plus every distinct V2 corpus icon but `map`; copied verbatim from that generated file, regenerate from it) ── */\nexport const DOCK_ICON_NAMES = [\n \"abc\", \"access-denied\", \"activity\", \"add\", \"add-circle\", \"add-comment\", \"add-document\", \"add-user\", \"address-book\",\n \"adjustments\", \"adjustments-horizontal\", \"alarm\", \"alert-triangle\", \"align-left\", \"api\", \"app-window\", \"apple\",\n \"apps\", \"archive\", \"arrow-back-up\", \"arrow-down\", \"arrow-guide\", \"arrow-left\", \"arrow-right\", \"arrow-top-right\",\n \"arrow-up\", \"arrows-diagonal\", \"arrows-down\", \"arrows-exchange\", \"arrows-horizontal\", \"arrows-left-right\",\n \"arrows-shuffle\", \"arrows-sort\", \"article\", \"aspect-ratio\", \"asterisk\", \"award\", \"axis-x\", \"backpack\", \"badge\",\n \"badge-cc\", \"badges\", \"bar-chart\", \"basket\", \"bell\", \"bill\", \"binary-document\", \"block-content\", \"block-element\",\n \"blockquote\", \"bold\", \"bolt\", \"book\", \"book-2\", \"bookmark\", \"bookmark-filled\", \"books\", \"bottle\", \"box\",\n \"brand-dribbble\", \"brand-facebook\", \"brand-instagram\", \"brand-linkedin\", \"brand-pinterest\", \"brand-tiktok\",\n \"brand-x\", \"brand-youtube\", \"briefcase\", \"broadcast\", \"browser\", \"bug\", \"building\", \"building-skyscraper\",\n \"building-store\", \"bulb-filled\", \"bulb-outline\", \"calculator\", \"calendar\", \"calendar-dollar\", \"calendar-event\",\n \"calendar-exclamation\", \"calendar-off\", \"calendar-plus\", \"calendar-star\", \"calendar-time\", \"camera\", \"campfire\",\n \"carousel-horizontal\", \"case\", \"cash\", \"category\", \"category-2\", \"chart-area\", \"chart-arrows\", \"chart-bar\",\n \"chart-bar-popular\", \"chart-donut\", \"chart-dots\", \"chart-line\", \"chart-upward\", \"check\", \"checkbox\", \"checklist\",\n \"checkmark\", \"checkmark-circle\", \"chef-hat\", \"chevron-down\", \"chevron-left\", \"chevron-right\", \"chevron-up\",\n \"chevrons-right\", \"circle\", \"circle-check\", \"circle-dot\", \"circle-number-1\", \"circle-plus\", \"click\", \"clipboard\",\n \"clipboard-image\", \"clipboard-text\", \"clock\", \"clock-check\", \"clock-hour-4\", \"close\", \"close-circle\", \"code\",\n \"code-block\", \"cog\", \"collapse\", \"color-wheel\", \"columns\", \"columns-2\", \"columns-3\", \"comment\", \"component\",\n \"compose\", \"compose-sparkles\", \"confetti\", \"controls\", \"cookie\", \"copy\", \"credit-card\", \"crop\", \"cube\",\n \"currency-dollar\", \"dashboard\", \"database\", \"database-search\", \"desktop\", \"device-desktop\", \"device-mobile\",\n \"device-tablet\", \"devices\", \"diamond\", \"discount\", \"document\", \"document-pdf\", \"document-remove\", \"document-sheet\",\n \"document-text\", \"document-video\", \"document-word\", \"document-zip\", \"documents\", \"door-exit\", \"dot\",\n \"double-chevron-down\", \"double-chevron-left\", \"double-chevron-right\", \"double-chevron-up\", \"double-quote\",\n \"download\", \"drag-handle\", \"drop\", \"droplet\", \"droplet-half\", \"earth-americas\", \"earth-globe\", \"edit\", \"egg\",\n \"ellipsis-horizontal\", \"ellipsis-vertical\", \"empty\", \"enter\", \"enter-right\", \"envelope\", \"equal\", \"error-filled\",\n \"error-outline\", \"error-screen\", \"expand\", \"eye-closed\", \"eye-off\", \"eye-open\", \"face-happy\", \"face-indifferent\",\n \"face-sad\", \"feather\", \"feedback\", \"file\", \"file-certificate\", \"file-code\", \"file-download\", \"file-settings\", \"file-text\", \"filter\",\n \"flag\", \"flame\", \"flask\", \"focus-2\", \"folder\", \"folders\", \"forms\", \"frame\", \"generate\", \"gift\", \"git-branch\",\n \"github\", \"grid-dots\", \"groq\", \"hammer\", \"hand-finger\", \"hanger\", \"hash\", \"headphones\", \"headset\", \"heart\",\n \"heart-filled\", \"heart-handshake\", \"help-circle\", \"highlight\", \"history\", \"home\", \"hourglass\", \"ice-cream\",\n \"id-badge\", \"id-badge-2\", \"image\", \"image-remove\", \"images\", \"inbox\", \"infinity\", \"info-circle\", \"info-filled\",\n \"info-outline\", \"info-square-rounded\", \"inline\", \"inline-element\", \"input-search\", \"insert-above\", \"insert-below\",\n \"italic\", \"joystick\", \"json\", \"language\", \"launch\", \"layout\", \"layout-align-center\", \"layout-board\",\n \"layout-board-split\", \"layout-bottombar\", \"layout-bottombar-expand\", \"layout-cards\", \"layout-columns\",\n \"layout-dashboard\", \"layout-grid\", \"layout-list\", \"layout-navbar\", \"layout-rows\", \"layout-sidebar\",\n \"layout-sidebar-right\", \"leaf\", \"leave\", \"lemon\", \"library-photo\", \"lifebuoy\", \"link\", \"link-removed\", \"linkedin\",\n \"list\", \"list-check\", \"list-details\", \"list-numbers\", \"list-tree\", \"lock\", \"logo-js\", \"logo-ts\", \"mail\", \"map-2\",\n \"map-pin\", \"map-pins\", \"marker\", \"marker-removed\", \"master-detail\", \"math-function\", \"meat\", \"menu\", \"message\",\n \"message-2\", \"message-check\", \"message-circle\", \"message-circle-2\", \"message-plus\", \"message-star\", \"microphone\",\n \"microphone-slash\", \"mobile-device\", \"mood-empty\", \"moon\", \"movie\", \"news\", \"notes\", \"number\", \"ok-hand\", \"olist\",\n \"overage\", \"package\", \"packages\", \"palette\", \"panel-left\", \"panel-right\", \"paperclip\", \"pause\", \"pencil\", \"phone\",\n \"photo\", \"photo-scan\", \"photo-search\", \"pig-money\", \"pin\", \"pin-filled\", \"pin-removed\", \"play\", \"player-play\",\n \"player-track-next\", \"playlist-add\", \"plug\", \"plug-connected\", \"plus-minus\", \"point\", \"presentation\", \"printer\",\n \"progress-50\", \"progress-75\", \"projects\", \"publish\", \"qrcode\", \"quote\", \"read-only\", \"receipt\", \"recycle\", \"redo\",\n \"refresh\", \"remove\", \"remove-circle\", \"repeat\", \"reset\", \"restore\", \"retrieve\", \"retry\", \"revert\", \"robot\",\n \"rocket\", \"rosette\", \"route\", \"ruler\", \"ruler-measure\", \"salad\", \"scale\", \"schema\", \"school\", \"search\", \"section\",\n \"select\", \"send\", \"server\", \"settings\", \"share\", \"shield-check\", \"shield-lock\", \"shopping-bag\", \"shopping-cart\",\n \"shopping-cart-plus\", \"signature\", \"sitemap\", \"slideshow\", \"sort\", \"sort-descending\", \"spacing-vertical\", \"sparkle\",\n \"sparkles\", \"speakerphone\", \"spinner\", \"split-horizontal\", \"split-vertical\", \"square\", \"stack\", \"stack-2\",\n \"stack-compact\", \"stairs\", \"stairs-up\", \"star\", \"star-filled\", \"stars\", \"stop\", \"strikethrough\", \"string\",\n \"subtask\", \"sun\", \"superscript\", \"sync\", \"table\", \"table-options\", \"tablet-device\", \"tag\", \"tags\", \"target\", \"task\",\n \"template\", \"tent\", \"terminal\", \"terminal-2\", \"text\", \"th-large\", \"th-list\", \"thumb-up\", \"thumbs-down\", \"thumbs-up\",\n \"ticket\", \"tiers\", \"timeline\", \"toggle-arrow-right\", \"toggle-right\", \"token\", \"tool\", \"tools-kitchen-2\", \"transfer\",\n \"translate\", \"trash\", \"trend-upward\", \"triangle-outline\", \"trolley\", \"truck\", \"truck-delivery\", \"truncate\",\n \"twitter\", \"typography\", \"ulist\", \"unarchive\", \"underline\", \"undo\", \"unknown\", \"unlink\", \"unlock\", \"unpublish\",\n \"upload\", \"user\", \"user-circle\", \"user-plus\", \"user-search\", \"user-star\", \"users\", \"users-group\", \"versions\",\n \"video\", \"vocabulary\", \"vs\", \"warning-filled\", \"warning-outline\", \"weight\", \"world\", \"wrench\", \"zoom-question\",\n] as const;\nexport type DockIconName = (typeof DOCK_ICON_NAMES)[number];\nexport const DOCK_ICON_NAME_SET: ReadonlySet<string> = new Set<string>(DOCK_ICON_NAMES);\n\n/* ── Content model fields ────────────────────────────────────────────── */\nexport type ProdFieldType =\n | \"text\" | \"richtext\" | \"document\" | \"image\" | \"boolean\" | \"number\" | \"select\"\n | \"reference\" | \"multi-reference\" | \"multiReference\" | \"array\" | \"date\" | \"datetime\"\n | \"longtext\" | \"slug\" | \"email\" | \"phone\" | \"link\" | \"color\" | \"json\" | \"file\"\n | \"component-ref\" | \"modular\" | \"sections\" | \"location\" | \"button\" | \"multiselect\" | \"icon\" | \"object\"\n | \"tags\" | \"rating\" | \"code\"\n /** V2 video: a URL string or `{ url, poster?, autoplay?, loop?, transcript? }` (VideoValue). */\n | \"video\"\n /** Legacy input only: normalized to `array` + zones on write, still read. */\n | \"group\" | \"repeater\";\n\nexport type OpenMode = \"inline\" | \"drill\" | \"popover\" | \"dialog\";\n/** backend SHOW_IF_OPS (validation/schemas/content.ts). */\nexport type ShowIfOp = \"eq\" | \"neq\" | \"in\" | \"notIn\" | \"gt\" | \"lt\" | \"isSet\" | \"notEmpty\" | \"empty\";\nexport interface ShowIfRule {\n field: string;\n op: ShowIfOp;\n value?: string | string[];\n /** past the siblings (A3, showIfScopeSchema): the parent's level, or the document's top level */\n scope?: \"parent\" | \"root\";\n}\nexport interface ShowIf {\n match: \"all\" | \"any\";\n rules: ShowIfRule[];\n}\n\nexport type UiLayout = \"list\" | \"grid\" | \"table\" | \"rows\" | \"chips\" | \"gallery\";\n/** ui.control (UI_CONTROLS): the leaf controls, the V2 control layouts among them. */\nexport const UI_CONTROLS = [\"segmented\", \"slider\", \"rating\", \"dropdown\", \"radio\", \"cards\", \"grid\", \"switch\", \"checkbox\"] as const;\nexport type UiControl = (typeof UI_CONTROLS)[number];\n/** The V2 control layouts and the types each fits (CONTROL_TYPES); segmented / slider / rating have their own rules. */\nexport const CONTROL_TYPES: Readonly<Partial<Record<UiControl, readonly string[]>>> = {\n dropdown: [\"select\"],\n radio: [\"select\"],\n cards: [\"select\", \"multiselect\"],\n grid: [\"select\"],\n switch: [\"boolean\"],\n checkbox: [\"boolean\", \"multiselect\"],\n};\n/** A row preview: child keys or dotted paths (V2), a literal fallback, and an icon shown while there is no media. */\nexport interface UiPreview {\n title?: string;\n subtitle?: string;\n media?: string;\n fallback?: string;\n icon?: DockIconName;\n}\nexport interface FieldUi {\n collapsed?: boolean;\n preview?: UiPreview;\n layout?: UiLayout;\n reorderable?: boolean;\n control?: UiControl;\n icon?: DockIconName;\n itemLabel?: string;\n /** text / longtext: the lines a text box opens with (1–40). */\n rows?: number;\n /** a field's placeholder when its config is strict (a prop keeps its own `placeholder`). */\n placeholder?: string;\n /** a list's item type: its identifier and its title (V2 `of[0].name` / `title`). */\n itemName?: string;\n itemTitle?: string;\n /** the value comes from a global: an edit applies everywhere. */\n synced?: { from: string };\n /** link / image / button: a plain string or the object. */\n valueShape?: \"string\" | \"object\";\n /** V2 field access (D58): the editor gates, the backend stores. */\n access?: { view?: \"schema.view\"; edit?: \"schema.manage\" };\n /** true = never shown in the form; false ≡ absent. */\n hidden?: boolean;\n /** modular: per block slug, how a row of that block type reads (V2 `of[i].icon` / `preview`). */\n blocks?: Record<string, { icon?: DockIconName; preview?: UiPreview }>;\n input?: { name: string; writes?: string[] };\n}\n\nexport type ScaleUnit = \"px\" | \"%\" | \"rem\" | \"em\" | \"vw\" | \"vh\";\nexport interface ScaleCustomValue {\n value: number;\n unit: ScaleUnit;\n}\nexport interface ScaleTokenStep {\n value: string;\n label: string;\n title?: string;\n px?: number;\n css?: string;\n}\nexport type ScaleBound = number | Partial<Record<ScaleUnit, number>>;\n/**\n * A number's `config.scale` (CPO D41): a stepped range, numeric steps or token steps, units, presets, and the custom\n * escape hatch (a boolean, or its bounds). A token scale stores the token string; a custom value `{ value, unit }`.\n */\nexport interface NumberScale {\n steps: { min: number; max: number; step?: number } | Array<{ value: number; label?: string }> | ScaleTokenStep[];\n units?: ScaleUnit[];\n defaultUnit?: ScaleUnit;\n allowCustom?: boolean | { min?: ScaleBound; max?: ScaleBound; units?: ScaleUnit[]; step?: number };\n presets?: Array<{ label?: string; value: number | string | ScaleCustomValue }>;\n}\n\nexport interface ArrayZoneConfig {\n nonRepeatable?: ContentModelField[];\n repeatable?: { fields: ContentModelField[]; minItems?: number; maxItems?: number };\n}\n\n/** One choice: a bare value, or `{ label, value }` with optional chrome (a dock icon, one line ≤ 140 under the label). */\nexport interface ChoiceOption {\n label: string;\n value: string;\n icon?: DockIconName;\n description?: string;\n}\nexport type SelectOption = string | ChoiceOption;\n\n/**\n * A list of scalars' item settings (backend arrayItemSettingsSchema, an `array` prop's `config.item`): the V2 item field's\n * description, locale chip, required, and a text / rich text item's length (a text item's pattern).\n */\nexport interface ArrayItemSettings {\n helpText?: string;\n localized?: boolean;\n required?: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n patternMessage?: string;\n}\n\n/** The typed keys of a field's `config` bag; the bag itself stays loose on scalar types. */\nexport interface FieldConfig {\n /** primitive array: scalars, or images, links or rich text (each item judged by its own type). */\n itemType?: \"text\" | \"number\" | \"date\" | \"image\" | \"link\" | \"richtext\";\n zones?: ArrayZoneConfig;\n contentModelId?: string;\n polymorphic?: boolean;\n allowedModelSlugs?: string[];\n /** reference / multi-reference (props): the non-entry targets, and a filter over the target's fields. */\n targets?: (\"page\" | \"form\" | \"collection\")[];\n filter?: string;\n min?: number;\n max?: number;\n step?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n /** text / longtext: the message shown when the pattern does not match (≤ 200, needs `pattern`). */\n patternMessage?: string;\n /** number: the error bounds (V2 validation.min / max); they win over min / max. */\n validation?: { min?: number; max?: number };\n /** number: what it counts (\"%\", \"USD\"), 1–24 characters. */\n unit?: string;\n placeholder?: string;\n includeTime?: boolean;\n /** \"site\", an IANA zone name, true (the editor picks a zone) or false (none). */\n timezone?: string | boolean;\n blockSlugs?: string[];\n minItems?: number;\n maxItems?: number;\n allowedSections?: string[];\n componentIds?: string[];\n /** slot (props): the V2 `of.allow` block names it takes, kept as written (backend slotAllowIssues). */\n allow?: string[];\n /** array (props): the item's own settings (backend arrayItemSettingsSchema). */\n item?: ArrayItemSettings;\n scale?: NumberScale;\n /** multiselect: its choices; with `allowCustom` it is free tags. tags: its suggestions. */\n options?: SelectOption[];\n allowCustom?: boolean;\n /** code: the language, and the languages an editor may pick (strict config, with tags' and rating's). */\n language?: string;\n languages?: string[];\n /**\n * text: \"url\" marks a text field the kit edits as a URL (V2 `url`). The backend keeps a text field's config as sent and\n * enforces its minLength / maxLength / pattern, which a link field would not; it reads no other meaning into the key.\n */\n inputType?: \"url\";\n [key: string]: unknown;\n}\n\nexport interface ContentModelField {\n key: string;\n label: string;\n type: ProdFieldType;\n helpText?: string;\n required?: boolean;\n unique?: boolean;\n defaultValue?: unknown;\n /** select: strings or { label, value }. */\n options?: SelectOption[];\n config?: FieldConfig | null;\n /** object (and legacy group / repeater): child fields. */\n fields?: ContentModelField[];\n openMode?: OpenMode;\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n fieldsetId?: string;\n placement?: \"cover\" | \"title\" | \"excerpt\" | \"body\" | \"inline\" | \"panel\";\n showIf?: ShowIf;\n readOnly?: true | \"developer\";\n lockReason?: string;\n /** V2 per-field localization: once any field is marked, a translation owns only its localized values. */\n localized?: boolean;\n ui?: FieldUi;\n searchable?: boolean;\n [key: string]: unknown;\n}\n\nexport type FieldsetKind = \"group\" | \"row\" | \"tab\";\nexport interface Fieldset {\n id: string;\n name: string;\n description?: string;\n kind?: FieldsetKind;\n icon?: DockIconName;\n collapsed?: boolean;\n tab?: string;\n collapsible?: boolean;\n columns?: 1 | 2;\n layout?: \"stack\" | \"breakpoints\";\n}\n\n/** A content model as the adapters read and write it (server-owned ids and timestamps left out). */\nexport interface ProdContentModel {\n name: string;\n slug: string;\n description?: string;\n kind?: \"model\" | \"block\";\n /** the collection's dock icon (content_models.icon, migration 0458; modelIconSchema) */\n icon?: DockIconName;\n fields: ContentModelField[];\n fieldsets?: Fieldset[];\n /** how an entry row reads in the list (content_models.list_preview, widened to V2 PreviewConfig by A3) */\n listPreview?: ComponentPreview | null;\n /** content_models.v2 (A3, contentModelV2Schema): what V2 says that has no native column */\n v2?: { shape?: V2Shape; placement?: V2Placement; category?: KitCategory; variants?: Record<string, { icon?: DockIconName; description?: string }> };\n}\n\n/* ── Component props ─────────────────────────────────────────────────── */\nexport const COMPONENT_PROP_TYPES = [\n \"text\", \"richtext\", \"image\", \"url\", \"boolean\", \"number\", \"select\", \"group\", \"table\", \"slot\", \"form\",\n \"link\", \"button\", \"date\", \"color\", \"file\", \"icon\", \"multiselect\",\n \"object\", \"datetime\", \"slug\", \"email\", \"phone\", \"json\", \"location\", \"tags\", \"rating\", \"code\",\n /** an entry id (or `{ entryId, modelSlug }`) and a list of them, hydrated on delivery; no definition default. */\n \"reference\", \"multi-reference\",\n /** a video value, a list of `config.itemType` values, multi-line text. */\n \"video\", \"array\", \"longtext\",\n] as const;\nexport type ComponentPropType = (typeof COMPONENT_PROP_TYPES)[number];\nexport type ComponentSubFieldType = Exclude<ComponentPropType, \"slot\" | \"form\">;\n\nexport const COMPONENT_CATEGORIES = [\n \"navbar\", \"footer\", \"button\", \"section\", \"slider\", \"tabs\", \"form\", \"custom\", \"hero\", \"content\", \"social-proof\", \"conversion\",\n] as const;\nexport type ComponentCategory = (typeof COMPONENT_CATEGORIES)[number];\n\nexport interface PropGroup {\n id: string;\n label?: string;\n kind?: \"group\" | \"row\";\n icon?: DockIconName;\n collapsed?: boolean;\n /** V2 fieldset keys, on the defining prop: a line under the header (≤ 200), side by side, a static header, a\n * breakpoint switcher, and its place among the groups (0–99; absent = where its first prop sits). */\n description?: string;\n columns?: 1 | 2;\n collapsible?: boolean;\n layout?: \"stack\" | \"breakpoints\";\n order?: number;\n}\n/**\n * A top-level prop's tab. Defined once: the first top-level prop with an id carries its label / icon / order, later\n * props carry `{ id }` only. A nested prop names one as `{ id }` only (tab_nested refuses a definition there; V2 and\n * the dock ignore it); one ui.group's members share one tab.\n */\nexport interface PropTab {\n id: string;\n label?: string;\n icon?: DockIconName;\n /** its place among the tabs (0–99; absent = where its first prop sits). */\n order?: number;\n}\nexport type PropUi = FieldUi & { group?: PropGroup; tab?: PropTab };\n\nexport interface ComponentSubField {\n key: string;\n label: string;\n type: ComponentSubFieldType;\n defaultValue?: unknown;\n required?: boolean;\n /** null = no help on purpose (V2 has no description); absent = the platform's auto-help. */\n helpText?: string | null;\n placeholder?: string;\n /** strings or { label, value } (subFieldOptionSchema). */\n options?: SelectOption[];\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n /** on a table's sub-field: no two rows share it; on a list: no item repeats. */\n unique?: boolean;\n openMode?: OpenMode;\n showIf?: ShowIf;\n /** the prop's ui, inline group included (V2 fieldsets nest). */\n ui?: PropUi;\n readOnly?: true | \"developer\";\n lockReason?: string;\n localized?: boolean;\n fields?: ComponentSubField[];\n config?: { fields?: ComponentSubField[]; options?: SelectOption[] } & Record<string, unknown>;\n}\n\nexport interface ComponentPropDef {\n key: string;\n label: string;\n /**\n * Where the override writes inside the component's blockJson. Binding, not schema: the V2 language has no twin, so\n * fromTypeSchema leaves it unset and the host binds it (componentize lane) before the save.\n */\n target?: { blockId: string; path: string };\n type: ComponentPropType;\n defaultValue?: unknown;\n required?: boolean;\n /** null = no help on purpose (V2 has no description); absent = the platform's auto-help. */\n helpText?: string | null;\n placeholder?: string;\n showIf?: ShowIf;\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n /** a list prop's items (a table's rows) distinct; refused on a top-level scalar (unique_prop_scope). */\n unique?: boolean;\n openMode?: OpenMode;\n readOnly?: true | \"developer\";\n lockReason?: string;\n localized?: boolean;\n config?: {\n componentIds?: string[];\n /** slot: the V2 block names it takes (`textBlock`), resolved to the project's components by the dock. */\n allow?: string[];\n /** array: the item's own settings. */\n item?: ArrayItemSettings;\n multiple?: boolean;\n minItems?: number;\n maxItems?: number;\n options?: SelectOption[];\n fields?: ComponentSubField[];\n min?: number;\n max?: number;\n step?: number;\n /** number: the error bounds (they win over min / max). */\n validation?: { min?: number; max?: number };\n /** text / url / longtext / richtext: length (richtext: its plain text). text / url / longtext: a pattern and its message. */\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n patternMessage?: string;\n /** reference / multi-reference: the target model, or polymorphic with an allowlist of model slugs. */\n contentModelId?: string;\n polymorphic?: boolean;\n allowedModelSlugs?: string[];\n /** reference / multi-reference: page / form / collection targets (backend REF_TARGET_KINDS) and a filter over the target's fields. */\n targets?: (\"page\" | \"form\" | \"collection\")[];\n filter?: string;\n /** array: what the list holds. */\n itemType?: \"text\" | \"number\" | \"date\" | \"image\" | \"link\" | \"richtext\";\n /** table with mode \"types\" (a polymorphic list): its item types, each row's `_type` naming one. */\n itemTypes?: { name: string; label?: string; fields: ComponentSubField[]; icon?: DockIconName; preview?: UiPreview }[];\n } & Record<string, unknown>;\n ui?: PropUi;\n}\n\n/** A component as the adapters read and write it. */\n/**\n * How ONE instance of a component reads as a row in a parent's slot list (backend `components.preview`, migration 0459;\n * types ComponentPreview): V2's document-level `preview`. `title` / `subtitle` / `media` each name a TOP-LEVEL prop key\n * (the API refuses any other: preview_key_unknown), `fallback` is the literal row label (trimmed, 1..120), `icon` a dock\n * icon shown while the row has no media.\n */\nexport type ComponentPreview = UiPreview;\n/** backend componentPreviewSchema: a preview slot names a key of at most this many characters */\nexport const COMPONENT_PREVIEW_KEY_MAX = 64;\n/** backend componentPreviewSchema: the row's literal fallback label, trimmed, at most this many characters */\nexport const COMPONENT_PREVIEW_FALLBACK_MAX = 120;\n\nexport interface ProdComponent {\n name: string;\n slug: string;\n description?: string;\n /** a library id, or since A3 the kit catalogue's own category verbatim (CATEGORY_ORDER) */\n category?: ComponentCategory | KitCategory;\n props: ComponentPropDef[];\n preview?: ComponentPreview | null;\n /** The component's dock icon (backend `components.icon`, dockIconSchema): V2's block icon. */\n icon?: DockIconName | null;\n /** The V2 tabs no top-level prop sits in (backend `components.tabs`, componentTabsSchema): declared, shown by none. */\n tabs?: PropTab[] | null;\n /** components.v2 (A3, componentV2Schema): category and icon are native columns */\n v2?: { shape?: V2Shape; placement?: V2Placement };\n /** the section family it is a variant of (its label): every member of a V2 block's variant family names the block */\n sectionType?: string;\n}\n\n/* ── Stored values ───────────────────────────────────────────────────── */\nexport const LINK_RELS = [\"nofollow\", \"sponsored\", \"ugc\"] as const;\nexport type LinkRel = (typeof LINK_RELS)[number];\nexport const LINK_VALUE_TYPES = [\"url\", \"page\", \"entry\", \"email\", \"phone\", \"anchor\", \"section\", \"file\"] as const;\nexport type LinkValueType = (typeof LINK_VALUE_TYPES)[number];\n/** The F1 link object (link-value.ts). A bare href string stays valid too. */\nexport interface ProdLinkValue {\n type?: LinkValueType;\n href: string;\n title?: string;\n newTab?: boolean;\n target?: \"_self\" | \"_blank\";\n rel?: LinkRel;\n section?: string;\n /** page / entry links: the target's id. */\n ref?: string;\n}\n\nexport const BUTTON_VARIANTS = [\"primary\", \"secondary\", \"tertiary\", \"brand\", \"dark\", \"light\", \"outline\", \"link\"] as const;\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\nexport const BUTTON_SIZES = [\"sm\", \"md\", \"lg\"] as const;\n/** One button as one value (button-look.ts ButtonValue). */\nexport interface ProdButtonValue {\n label: string;\n href: string | ProdLinkValue;\n variant?: ButtonVariant;\n size?: (typeof BUTTON_SIZES)[number];\n arrow?: boolean;\n icon?: DockIconName | null;\n newTab?: boolean;\n rel?: LinkRel;\n /** Stretch across the column (backend button_full_width_invalid refuses a non-boolean a write brings). */\n fullWidth?: boolean;\n}\n\n/** 0–1 fractions of the image cut from each edge. */\nexport interface ProdImageCrop {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n/** The normalized image value (field-value-normalize.ts): other keys are dropped on write. */\nexport interface ProdImageValue {\n id?: string;\n url: string;\n name?: string;\n altText?: string;\n /** 0–1 fractions. */\n focal?: { x: number; y: number };\n crop?: ProdImageCrop;\n}\n\n/** The V2 video value's object form (video-value.ts): exactly the video block's props. A bare URL string is valid too. */\nexport const VIDEO_VALUE_KEYS = [\"url\", \"poster\", \"autoplay\", \"loop\", \"transcript\"] as const;\nexport interface ProdVideoValue {\n url: string;\n poster?: string;\n autoplay?: boolean;\n loop?: boolean;\n transcript?: string;\n}\n\n/** A polymorphic reference value. */\nexport interface ProdPolymorphicRef {\n entryId: string;\n modelSlug: string;\n}\n/**\n * A page, form or collection pick on a prop reference that also takes entries (backend reference-target-config.ts\n * targetRefOf): a bare string there is read as an entry id (acceptsEntries), so a non-entry pick names its kind.\n */\nexport interface ProdTargetRef {\n kind: \"page\" | \"form\" | \"collection\";\n id: string;\n}\n/** What a `form` prop stores. */\nexport interface ProdFormRef {\n formId: string;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"prod-types.js","names":[],"sources":["../../src/adapters/prod-types.ts"],"sourcesContent":["/**\n * PROD SHAPES — the production schema language and stored values, typed locally. The caps and key rules the backend\n * and the kit must agree on come from @bettercms-ai/types (W1a, a peer of the kit since W1c): one home per constant.\n *\n * Mirrors the backend at F1 + F1.x-props + link objects + tags / rating / code (72ea2b68d), then reference props\n * (73a120c50), validation parity (dd021ceba), option meta (ab15fe893), V2 chrome keys and types (98e48b9e3, 324580b2e),\n * the V2 icon vocabulary (e126bf33d) and the localized flag (279a10490) — origin/dev 81f385ff3:\n * packages/types/src/content-model.ts, component.ts, link-value.ts, button-look.ts, dock-icon-names.ts, video-value.ts\n * src/lib/validation/schemas/content.ts (baseField, fieldUiSchema, chromeUiProblems, fieldsets, MAX_FIELD_DEPTH)\n * src/lib/validation/schemas/components.ts (componentPropDefSchema, subFieldSchema, propUiSchema, propValidationHardening)\n * src/lib/content/validation-parity.ts, src/lib/content/dock-vocabulary.ts (TAB_MAX, TAB_NAME_MAX)\n * When the backend widens, widen these and the adapters together; the round-trip test then shrinks the lossy ledger.\n */\nimport { DEPTH_MAX, HELP_CAPS, TAB_MAX as SHARED_TAB_MAX, type KitCategory, type V2Placement, type V2Shape } from \"@bettercms-ai/types\";\n\n/* ── Limits the backend enforces ─────────────────────────────────────── */\n/** Field levels one schema may nest (a top-level field, or a prop, is level 1). */\nexport const MAX_FIELD_DEPTH = DEPTH_MAX;\n/** 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. */\nexport const TAB_MAX = SHARED_TAB_MAX;\nexport const TAB_NAME_MAX = 40;\nexport const FIELDSETS_MAX = 30;\nexport const FIELDSET_NAME_MAX = 60;\nexport const FIELDSET_DESCRIPTION_MAX = HELP_CAPS.fieldset;\nexport const FIELD_HELP_MAX = HELP_CAPS.field;\nexport const PROP_HELP_MAX = HELP_CAPS.prop;\nexport const PROP_PLACEHOLDER_MAX = HELP_CAPS.placeholder;\nexport const HELP_MORE_MAX = HELP_CAPS.helpMore;\nexport const LOCK_REASON_MAX = 200;\nexport const ITEM_LABEL_MAX = 24;\nexport const PREVIEW_FALLBACK_MAX = 60;\nexport const PROP_GROUP_LABEL_MAX = 32;\nexport const PATTERN_MAX = 200;\n/** A tags value's tag length (TAG_VALUE_MAX_LENGTH), a code value's length (CODE_MAX_LENGTH), a rating's max with none set. */\nexport const TAG_VALUE_MAX_LENGTH = 64;\nexport const CODE_MAX_LENGTH = 100_000;\nexport const RATING_DEFAULT_MAX = 5;\n/** ratingConfigSchema: config.max a whole 1–10; (max − min) / step whole and at most this many stops. */\nexport const RATING_MAX = 10;\nexport const RATING_MAX_STOPS = 100;\n/** tagsConfigSchema: config.min 0–500, config.max 1–500. */\nexport const TAGS_ITEMS_MAX = 500;\n/** A choice option's description (OPTION_DESCRIPTION_MAX): one line. */\nexport const OPTION_DESCRIPTION_MAX = HELP_CAPS.option;\n/** config.patternMessage (PATTERN_MESSAGE_MAX), config.unit on a number (NUMBER_UNIT_MAX). */\nexport const PATTERN_MESSAGE_MAX = 200;\nexport const NUMBER_UNIT_MAX = 24;\n/** fieldUiSchema: ui.rows 1–40, ui.placeholder 1–120, ui.itemTitle 1–60, ui.synced.from 1–64. */\nexport const UI_ROWS_MAX = 40;\nexport const UI_PLACEHOLDER_MAX = HELP_CAPS.placeholder;\nexport const ITEM_TITLE_MAX = 60;\nexport const SYNCED_FROM_MAX = 64;\n/** ui.itemName is an identifier: a letter, then letters, digits or _ (≤ 64). */\nexport const ITEM_NAME_RE = /^[A-Za-z][A-Za-z0-9_]{0,63}$/;\n/** propUiSchema group: description 1–200, order 0–99. */\nexport const PROP_GROUP_DESCRIPTION_MAX = HELP_CAPS.propGroup;\nexport const PROP_GROUP_ORDER_MAX = 99;\n/** propUiSchema tab: at most 8 distinct tabs per component, an id 1–40, a label ≤ TAB_NAME_MAX trimmed, order 0–99. */\nexport const PROP_TAB_MAX = SHARED_TAB_MAX;\nexport const PROP_TAB_ID_MAX = 40;\n/** numberScaleSchema: token value 1–32, label 1–24, title 1–60, css 1–60; 1–50 steps; at most 20 presets. */\nexport const SCALE_TOKEN_VALUE_MAX = 32;\nexport const SCALE_LABEL_MAX = 24;\nexport const SCALE_TITLE_MAX = 60;\nexport const SCALE_CSS_MAX = 60;\nexport const SCALE_STEPS_MAX = 50;\nexport const SCALE_PRESETS_MAX = 20;\n/** The video block's transcript cap (VIDEO_TRANSCRIPT_MAX). */\nexport const VIDEO_TRANSCRIPT_MAX = 100_000;\n\n/* ── Icons: the closed dock vocabulary (packages/types/src/dock-icon-names.ts at 282655543: 473 names, Sanity's set\n * plus every distinct V2 corpus icon but `map`; copied verbatim from that generated file, regenerate from it) ── */\nexport const DOCK_ICON_NAMES = [\n \"abc\", \"access-denied\", \"activity\", \"add\", \"add-circle\", \"add-comment\", \"add-document\", \"add-user\", \"address-book\",\n \"adjustments\", \"adjustments-horizontal\", \"alarm\", \"alert-triangle\", \"align-left\", \"api\", \"app-window\", \"apple\",\n \"apps\", \"archive\", \"arrow-back-up\", \"arrow-down\", \"arrow-guide\", \"arrow-left\", \"arrow-right\", \"arrow-top-right\",\n \"arrow-up\", \"arrows-diagonal\", \"arrows-down\", \"arrows-exchange\", \"arrows-horizontal\", \"arrows-left-right\",\n \"arrows-shuffle\", \"arrows-sort\", \"article\", \"aspect-ratio\", \"asterisk\", \"award\", \"axis-x\", \"backpack\", \"badge\",\n \"badge-cc\", \"badges\", \"bar-chart\", \"basket\", \"bell\", \"bill\", \"binary-document\", \"block-content\", \"block-element\",\n \"blockquote\", \"bold\", \"bolt\", \"book\", \"book-2\", \"bookmark\", \"bookmark-filled\", \"books\", \"bottle\", \"box\",\n \"brand-dribbble\", \"brand-facebook\", \"brand-instagram\", \"brand-linkedin\", \"brand-pinterest\", \"brand-tiktok\",\n \"brand-x\", \"brand-youtube\", \"briefcase\", \"broadcast\", \"browser\", \"bug\", \"building\", \"building-skyscraper\",\n \"building-store\", \"bulb-filled\", \"bulb-outline\", \"calculator\", \"calendar\", \"calendar-dollar\", \"calendar-event\",\n \"calendar-exclamation\", \"calendar-off\", \"calendar-plus\", \"calendar-star\", \"calendar-time\", \"camera\", \"campfire\",\n \"carousel-horizontal\", \"case\", \"cash\", \"category\", \"category-2\", \"chart-area\", \"chart-arrows\", \"chart-bar\",\n \"chart-bar-popular\", \"chart-donut\", \"chart-dots\", \"chart-line\", \"chart-upward\", \"check\", \"checkbox\", \"checklist\",\n \"checkmark\", \"checkmark-circle\", \"chef-hat\", \"chevron-down\", \"chevron-left\", \"chevron-right\", \"chevron-up\",\n \"chevrons-right\", \"circle\", \"circle-check\", \"circle-dot\", \"circle-number-1\", \"circle-plus\", \"click\", \"clipboard\",\n \"clipboard-image\", \"clipboard-text\", \"clock\", \"clock-check\", \"clock-hour-4\", \"close\", \"close-circle\", \"code\",\n \"code-block\", \"cog\", \"collapse\", \"color-wheel\", \"columns\", \"columns-2\", \"columns-3\", \"comment\", \"component\",\n \"compose\", \"compose-sparkles\", \"confetti\", \"controls\", \"cookie\", \"copy\", \"credit-card\", \"crop\", \"cube\",\n \"currency-dollar\", \"dashboard\", \"database\", \"database-search\", \"desktop\", \"device-desktop\", \"device-mobile\",\n \"device-tablet\", \"devices\", \"diamond\", \"discount\", \"document\", \"document-pdf\", \"document-remove\", \"document-sheet\",\n \"document-text\", \"document-video\", \"document-word\", \"document-zip\", \"documents\", \"door-exit\", \"dot\",\n \"double-chevron-down\", \"double-chevron-left\", \"double-chevron-right\", \"double-chevron-up\", \"double-quote\",\n \"download\", \"drag-handle\", \"drop\", \"droplet\", \"droplet-half\", \"earth-americas\", \"earth-globe\", \"edit\", \"egg\",\n \"ellipsis-horizontal\", \"ellipsis-vertical\", \"empty\", \"enter\", \"enter-right\", \"envelope\", \"equal\", \"error-filled\",\n \"error-outline\", \"error-screen\", \"expand\", \"eye-closed\", \"eye-off\", \"eye-open\", \"face-happy\", \"face-indifferent\",\n \"face-sad\", \"feather\", \"feedback\", \"file\", \"file-certificate\", \"file-code\", \"file-download\", \"file-settings\", \"file-text\", \"filter\",\n \"flag\", \"flame\", \"flask\", \"focus-2\", \"folder\", \"folders\", \"forms\", \"frame\", \"generate\", \"gift\", \"git-branch\",\n \"github\", \"grid-dots\", \"groq\", \"hammer\", \"hand-finger\", \"hanger\", \"hash\", \"headphones\", \"headset\", \"heart\",\n \"heart-filled\", \"heart-handshake\", \"help-circle\", \"highlight\", \"history\", \"home\", \"hourglass\", \"ice-cream\",\n \"id-badge\", \"id-badge-2\", \"image\", \"image-remove\", \"images\", \"inbox\", \"infinity\", \"info-circle\", \"info-filled\",\n \"info-outline\", \"info-square-rounded\", \"inline\", \"inline-element\", \"input-search\", \"insert-above\", \"insert-below\",\n \"italic\", \"joystick\", \"json\", \"language\", \"launch\", \"layout\", \"layout-align-center\", \"layout-board\",\n \"layout-board-split\", \"layout-bottombar\", \"layout-bottombar-expand\", \"layout-cards\", \"layout-columns\",\n \"layout-dashboard\", \"layout-grid\", \"layout-list\", \"layout-navbar\", \"layout-rows\", \"layout-sidebar\",\n \"layout-sidebar-right\", \"leaf\", \"leave\", \"lemon\", \"library-photo\", \"lifebuoy\", \"link\", \"link-removed\", \"linkedin\",\n \"list\", \"list-check\", \"list-details\", \"list-numbers\", \"list-tree\", \"lock\", \"logo-js\", \"logo-ts\", \"mail\", \"map-2\",\n \"map-pin\", \"map-pins\", \"marker\", \"marker-removed\", \"master-detail\", \"math-function\", \"meat\", \"menu\", \"message\",\n \"message-2\", \"message-check\", \"message-circle\", \"message-circle-2\", \"message-plus\", \"message-star\", \"microphone\",\n \"microphone-slash\", \"mobile-device\", \"mood-empty\", \"moon\", \"movie\", \"news\", \"notes\", \"number\", \"ok-hand\", \"olist\",\n \"overage\", \"package\", \"packages\", \"palette\", \"panel-left\", \"panel-right\", \"paperclip\", \"pause\", \"pencil\", \"phone\",\n \"photo\", \"photo-scan\", \"photo-search\", \"pig-money\", \"pin\", \"pin-filled\", \"pin-removed\", \"play\", \"player-play\",\n \"player-track-next\", \"playlist-add\", \"plug\", \"plug-connected\", \"plus-minus\", \"point\", \"presentation\", \"printer\",\n \"progress-50\", \"progress-75\", \"projects\", \"publish\", \"qrcode\", \"quote\", \"read-only\", \"receipt\", \"recycle\", \"redo\",\n \"refresh\", \"remove\", \"remove-circle\", \"repeat\", \"reset\", \"restore\", \"retrieve\", \"retry\", \"revert\", \"robot\",\n \"rocket\", \"rosette\", \"route\", \"ruler\", \"ruler-measure\", \"salad\", \"scale\", \"schema\", \"school\", \"search\", \"section\",\n \"select\", \"send\", \"server\", \"settings\", \"share\", \"shield-check\", \"shield-lock\", \"shopping-bag\", \"shopping-cart\",\n \"shopping-cart-plus\", \"signature\", \"sitemap\", \"slideshow\", \"sort\", \"sort-descending\", \"spacing-vertical\", \"sparkle\",\n \"sparkles\", \"speakerphone\", \"spinner\", \"split-horizontal\", \"split-vertical\", \"square\", \"stack\", \"stack-2\",\n \"stack-compact\", \"stairs\", \"stairs-up\", \"star\", \"star-filled\", \"stars\", \"stop\", \"strikethrough\", \"string\",\n \"subtask\", \"sun\", \"superscript\", \"sync\", \"table\", \"table-options\", \"tablet-device\", \"tag\", \"tags\", \"target\", \"task\",\n \"template\", \"tent\", \"terminal\", \"terminal-2\", \"text\", \"th-large\", \"th-list\", \"thumb-up\", \"thumbs-down\", \"thumbs-up\",\n \"ticket\", \"tiers\", \"timeline\", \"toggle-arrow-right\", \"toggle-right\", \"token\", \"tool\", \"tools-kitchen-2\", \"transfer\",\n \"translate\", \"trash\", \"trend-upward\", \"triangle-outline\", \"trolley\", \"truck\", \"truck-delivery\", \"truncate\",\n \"twitter\", \"typography\", \"ulist\", \"unarchive\", \"underline\", \"undo\", \"unknown\", \"unlink\", \"unlock\", \"unpublish\",\n \"upload\", \"user\", \"user-circle\", \"user-plus\", \"user-search\", \"user-star\", \"users\", \"users-group\", \"versions\",\n \"video\", \"vocabulary\", \"vs\", \"warning-filled\", \"warning-outline\", \"weight\", \"world\", \"wrench\", \"zoom-question\",\n] as const;\nexport type DockIconName = (typeof DOCK_ICON_NAMES)[number];\nexport const DOCK_ICON_NAME_SET: ReadonlySet<string> = new Set<string>(DOCK_ICON_NAMES);\n\n/* ── Content model fields ────────────────────────────────────────────── */\nexport type ProdFieldType =\n | \"text\" | \"richtext\" | \"document\" | \"image\" | \"boolean\" | \"number\" | \"select\"\n | \"reference\" | \"multi-reference\" | \"multiReference\" | \"array\" | \"date\" | \"datetime\"\n | \"longtext\" | \"slug\" | \"email\" | \"phone\" | \"link\" | \"color\" | \"json\" | \"file\"\n | \"component-ref\" | \"modular\" | \"sections\" | \"location\" | \"button\" | \"multiselect\" | \"icon\" | \"object\"\n | \"tags\" | \"rating\" | \"code\"\n /** V2 video: a URL string or `{ url, poster?, autoplay?, loop?, transcript? }` (VideoValue). */\n | \"video\"\n /** Legacy input only: normalized to `array` + zones on write, still read. */\n | \"group\" | \"repeater\";\n\nexport type OpenMode = \"inline\" | \"drill\" | \"popover\" | \"dialog\";\n/** backend SHOW_IF_OPS (validation/schemas/content.ts). */\nexport type ShowIfOp = \"eq\" | \"neq\" | \"in\" | \"notIn\" | \"gt\" | \"lt\" | \"isSet\" | \"notEmpty\" | \"empty\";\nexport interface ShowIfRule {\n field: string;\n op: ShowIfOp;\n value?: string | string[];\n /** past the siblings (A3, showIfScopeSchema): the parent's level, or the document's top level */\n scope?: \"parent\" | \"root\";\n}\nexport interface ShowIf {\n match: \"all\" | \"any\";\n rules: ShowIfRule[];\n}\n\nexport type UiLayout = \"list\" | \"grid\" | \"table\" | \"rows\" | \"chips\" | \"gallery\";\n/** ui.control (UI_CONTROLS): the leaf controls, the V2 control layouts among them. */\nexport const UI_CONTROLS = [\"segmented\", \"slider\", \"rating\", \"dropdown\", \"radio\", \"cards\", \"grid\", \"switch\", \"checkbox\"] as const;\nexport type UiControl = (typeof UI_CONTROLS)[number];\n/** The V2 control layouts and the types each fits (CONTROL_TYPES); segmented / slider / rating have their own rules. */\nexport const CONTROL_TYPES: Readonly<Partial<Record<UiControl, readonly string[]>>> = {\n dropdown: [\"select\"],\n radio: [\"select\"],\n cards: [\"select\", \"multiselect\"],\n grid: [\"select\"],\n switch: [\"boolean\"],\n checkbox: [\"boolean\", \"multiselect\"],\n};\n/** A row preview: child keys or dotted paths (V2), a literal fallback, and an icon shown while there is no media. */\nexport interface UiPreview {\n title?: string;\n subtitle?: string;\n media?: string;\n fallback?: string;\n icon?: DockIconName;\n}\nexport interface FieldUi {\n collapsed?: boolean;\n preview?: UiPreview;\n layout?: UiLayout;\n reorderable?: boolean;\n control?: UiControl;\n icon?: DockIconName;\n itemLabel?: string;\n /** text / longtext: the lines a text box opens with (1–40). */\n rows?: number;\n /** a field's placeholder when its config is strict (a prop keeps its own `placeholder`). */\n placeholder?: string;\n /** a list's item type: its identifier and its title (V2 `of[0].name` / `title`). */\n itemName?: string;\n itemTitle?: string;\n /** the value comes from a global: an edit applies everywhere. */\n synced?: { from: string };\n /** link / image / button: a plain string or the object. */\n valueShape?: \"string\" | \"object\";\n /** V2 field access (D58): the editor gates, the backend stores. */\n access?: { view?: \"schema.view\"; edit?: \"schema.manage\" };\n /** true = never shown in the form; false ≡ absent. */\n hidden?: boolean;\n /** modular: per block slug, how a row of that block type reads (V2 `of[i].icon` / `preview`). */\n blocks?: Record<string, { icon?: DockIconName; preview?: UiPreview; openMode?: OpenMode }>;\n input?: { name: string; writes?: string[] };\n}\n\nexport type ScaleUnit = \"px\" | \"%\" | \"rem\" | \"em\" | \"vw\" | \"vh\";\nexport interface ScaleCustomValue {\n value: number;\n unit: ScaleUnit;\n}\nexport interface ScaleTokenStep {\n value: string;\n label: string;\n title?: string;\n px?: number;\n css?: string;\n}\nexport type ScaleBound = number | Partial<Record<ScaleUnit, number>>;\n/**\n * A number's `config.scale` (CPO D41): a stepped range, numeric steps or token steps, units, presets, and the custom\n * escape hatch (a boolean, or its bounds). A token scale stores the token string; a custom value `{ value, unit }`.\n */\nexport interface NumberScale {\n steps: { min: number; max: number; step?: number } | Array<{ value: number; label?: string }> | ScaleTokenStep[];\n units?: ScaleUnit[];\n defaultUnit?: ScaleUnit;\n allowCustom?: boolean | { min?: ScaleBound; max?: ScaleBound; units?: ScaleUnit[]; step?: number };\n presets?: Array<{ label?: string; value: number | string | ScaleCustomValue }>;\n}\n\nexport interface ArrayZoneConfig {\n nonRepeatable?: ContentModelField[];\n repeatable?: { fields: ContentModelField[]; minItems?: number; maxItems?: number };\n}\n\n/** One choice: a bare value, or `{ label, value }` with optional chrome (a dock icon, one line ≤ 140 under the label). */\nexport interface ChoiceOption {\n label: string;\n value: string;\n icon?: DockIconName;\n description?: string;\n}\nexport type SelectOption = string | ChoiceOption;\n\n/**\n * A list of scalars' item settings (backend arrayItemSettingsSchema, an `array` prop's `config.item`): the V2 item field's\n * description, locale chip, required, and a text / rich text item's length (a text item's pattern).\n */\nexport interface ArrayItemSettings {\n helpText?: string;\n localized?: boolean;\n required?: boolean;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n patternMessage?: string;\n}\n\n/** The typed keys of a field's `config` bag; the bag itself stays loose on scalar types. */\nexport interface FieldConfig {\n /** primitive array: scalars, or images, links or rich text (each item judged by its own type). */\n itemType?: \"text\" | \"number\" | \"date\" | \"image\" | \"link\" | \"richtext\";\n zones?: ArrayZoneConfig;\n contentModelId?: string;\n polymorphic?: boolean;\n allowedModelSlugs?: string[];\n /** reference / multi-reference (props): the non-entry targets, and a filter over the target's fields. */\n targets?: (\"page\" | \"form\" | \"collection\")[];\n filter?: string;\n min?: number;\n max?: number;\n step?: number;\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n /** text / longtext: the message shown when the pattern does not match (≤ 200, needs `pattern`). */\n patternMessage?: string;\n /** number: the error bounds (V2 validation.min / max); they win over min / max. */\n validation?: { min?: number; max?: number };\n /** number: what it counts (\"%\", \"USD\"), 1–24 characters. */\n unit?: string;\n placeholder?: string;\n includeTime?: boolean;\n /** \"site\", an IANA zone name, true (the editor picks a zone) or false (none). */\n timezone?: string | boolean;\n blockSlugs?: string[];\n minItems?: number;\n maxItems?: number;\n allowedSections?: string[];\n componentIds?: string[];\n /** slot (props): the V2 `of.allow` block names it takes, kept as written (backend slotAllowIssues). */\n allow?: string[];\n /** array (props): the item's own settings (backend arrayItemSettingsSchema). */\n item?: ArrayItemSettings;\n scale?: NumberScale;\n /** multiselect: its choices; with `allowCustom` it is free tags. tags: its suggestions. */\n options?: SelectOption[];\n allowCustom?: boolean;\n /** code: the language, and the languages an editor may pick (strict config, with tags' and rating's). */\n language?: string;\n languages?: string[];\n /**\n * text: \"url\" marks a text field the kit edits as a URL (V2 `url`). The backend keeps a text field's config as sent and\n * enforces its minLength / maxLength / pattern, which a link field would not; it reads no other meaning into the key.\n */\n inputType?: \"url\";\n [key: string]: unknown;\n}\n\nexport interface ContentModelField {\n key: string;\n label: string;\n type: ProdFieldType;\n helpText?: string;\n required?: boolean;\n unique?: boolean;\n defaultValue?: unknown;\n /** select: strings or { label, value }. */\n options?: SelectOption[];\n config?: FieldConfig | null;\n /** object (and legacy group / repeater): child fields. */\n fields?: ContentModelField[];\n openMode?: OpenMode;\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n fieldsetId?: string;\n placement?: \"cover\" | \"title\" | \"excerpt\" | \"body\" | \"inline\" | \"panel\";\n showIf?: ShowIf;\n readOnly?: true | \"developer\";\n lockReason?: string;\n /** V2 per-field localization: once any field is marked, a translation owns only its localized values. */\n localized?: boolean;\n ui?: FieldUi;\n searchable?: boolean;\n [key: string]: unknown;\n}\n\nexport type FieldsetKind = \"group\" | \"row\" | \"tab\";\nexport interface Fieldset {\n id: string;\n name: string;\n description?: string;\n kind?: FieldsetKind;\n icon?: DockIconName;\n collapsed?: boolean;\n tab?: string;\n collapsible?: boolean;\n columns?: 1 | 2;\n layout?: \"stack\" | \"breakpoints\";\n}\n\n/** A content model as the adapters read and write it (server-owned ids and timestamps left out). */\nexport interface ProdContentModel {\n name: string;\n slug: string;\n description?: string;\n kind?: \"model\" | \"block\";\n /** the collection's dock icon (content_models.icon, migration 0458; modelIconSchema) */\n icon?: DockIconName;\n fields: ContentModelField[];\n fieldsets?: Fieldset[];\n /** how an entry row reads in the list (content_models.list_preview, widened to V2 PreviewConfig by A3) */\n listPreview?: ComponentPreview | null;\n /** content_models.v2 (A3, contentModelV2Schema): what V2 says that has no native column */\n v2?: { shape?: V2Shape; placement?: V2Placement; category?: KitCategory; variants?: Record<string, { icon?: DockIconName; description?: string }> };\n}\n\n/* ── Component props ─────────────────────────────────────────────────── */\nexport const COMPONENT_PROP_TYPES = [\n \"text\", \"richtext\", \"image\", \"url\", \"boolean\", \"number\", \"select\", \"group\", \"table\", \"slot\", \"form\",\n \"link\", \"button\", \"date\", \"color\", \"file\", \"icon\", \"multiselect\",\n \"object\", \"datetime\", \"slug\", \"email\", \"phone\", \"json\", \"location\", \"tags\", \"rating\", \"code\",\n /** an entry id (or `{ entryId, modelSlug }`) and a list of them, hydrated on delivery; no definition default. */\n \"reference\", \"multi-reference\",\n /** a video value, a list of `config.itemType` values, multi-line text. */\n \"video\", \"array\", \"longtext\",\n] as const;\nexport type ComponentPropType = (typeof COMPONENT_PROP_TYPES)[number];\nexport type ComponentSubFieldType = Exclude<ComponentPropType, \"slot\" | \"form\">;\n\nexport const COMPONENT_CATEGORIES = [\n \"navbar\", \"footer\", \"button\", \"section\", \"slider\", \"tabs\", \"form\", \"custom\", \"hero\", \"content\", \"social-proof\", \"conversion\",\n] as const;\nexport type ComponentCategory = (typeof COMPONENT_CATEGORIES)[number];\n\nexport interface PropGroup {\n id: string;\n label?: string;\n kind?: \"group\" | \"row\";\n icon?: DockIconName;\n collapsed?: boolean;\n /** V2 fieldset keys, on the defining prop: a line under the header (≤ 200), side by side, a static header, a\n * breakpoint switcher, and its place among the groups (0–99; absent = where its first prop sits). */\n description?: string;\n columns?: 1 | 2;\n collapsible?: boolean;\n layout?: \"stack\" | \"breakpoints\";\n order?: number;\n}\n/**\n * A top-level prop's tab. Defined once: the first top-level prop with an id carries its label / icon / order, later\n * props carry `{ id }` only. A nested prop names one as `{ id }` only (tab_nested refuses a definition there; V2 and\n * the dock ignore it); one ui.group's members share one tab.\n */\nexport interface PropTab {\n id: string;\n label?: string;\n icon?: DockIconName;\n /** its place among the tabs (0–99; absent = where its first prop sits). */\n order?: number;\n}\nexport type PropUi = FieldUi & { group?: PropGroup; tab?: PropTab };\n\nexport interface ComponentSubField {\n key: string;\n label: string;\n type: ComponentSubFieldType;\n defaultValue?: unknown;\n required?: boolean;\n /** null = no help on purpose (V2 has no description); absent = the platform's auto-help. */\n helpText?: string | null;\n placeholder?: string;\n /** strings or { label, value } (subFieldOptionSchema). */\n options?: SelectOption[];\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n /** on a table's sub-field: no two rows share it; on a list: no item repeats. */\n unique?: boolean;\n openMode?: OpenMode;\n showIf?: ShowIf;\n /** the prop's ui, inline group included (V2 fieldsets nest). */\n ui?: PropUi;\n readOnly?: true | \"developer\";\n lockReason?: string;\n localized?: boolean;\n fields?: ComponentSubField[];\n config?: { fields?: ComponentSubField[]; options?: SelectOption[] } & Record<string, unknown>;\n}\n\nexport interface ComponentPropDef {\n key: string;\n label: string;\n /**\n * Where the override writes inside the component's blockJson. Binding, not schema: the V2 language has no twin, so\n * fromTypeSchema leaves it unset and the host binds it (componentize lane) before the save.\n */\n target?: { blockId: string; path: string };\n type: ComponentPropType;\n defaultValue?: unknown;\n required?: boolean;\n /** null = no help on purpose (V2 has no description); absent = the platform's auto-help. */\n helpText?: string | null;\n placeholder?: string;\n showIf?: ShowIf;\n helpMore?: string;\n advanced?: boolean;\n /** the AI-assist sparkle (A3: baseField and a prop's `ai`) */\n ai?: boolean;\n recommendedMax?: number;\n /** a list prop's items (a table's rows) distinct; refused on a top-level scalar (unique_prop_scope). */\n unique?: boolean;\n openMode?: OpenMode;\n readOnly?: true | \"developer\";\n lockReason?: string;\n localized?: boolean;\n config?: {\n componentIds?: string[];\n /** slot: the V2 block names it takes (`textBlock`), resolved to the project's components by the dock. */\n allow?: string[];\n /** array: the item's own settings. */\n item?: ArrayItemSettings;\n multiple?: boolean;\n minItems?: number;\n maxItems?: number;\n options?: SelectOption[];\n fields?: ComponentSubField[];\n min?: number;\n max?: number;\n step?: number;\n /** number: the error bounds (they win over min / max). */\n validation?: { min?: number; max?: number };\n /** text / url / longtext / richtext: length (richtext: its plain text). text / url / longtext: a pattern and its message. */\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n patternMessage?: string;\n /** reference / multi-reference: the target model, or polymorphic with an allowlist of model slugs. */\n contentModelId?: string;\n polymorphic?: boolean;\n allowedModelSlugs?: string[];\n /** reference / multi-reference: page / form / collection targets (backend REF_TARGET_KINDS) and a filter over the target's fields. */\n targets?: (\"page\" | \"form\" | \"collection\")[];\n filter?: string;\n /** array: what the list holds. */\n itemType?: \"text\" | \"number\" | \"date\" | \"image\" | \"link\" | \"richtext\";\n /** table with mode \"types\" (a polymorphic list): its item types, each row's `_type` naming one. */\n itemTypes?: { name: string; label?: string; fields: ComponentSubField[]; icon?: DockIconName; preview?: UiPreview }[];\n } & Record<string, unknown>;\n ui?: PropUi;\n}\n\n/** A component as the adapters read and write it. */\n/**\n * How ONE instance of a component reads as a row in a parent's slot list (backend `components.preview`, migration 0459;\n * types ComponentPreview): V2's document-level `preview`. `title` / `subtitle` / `media` each name a TOP-LEVEL prop key\n * (the API refuses any other: preview_key_unknown), `fallback` is the literal row label (trimmed, 1..120), `icon` a dock\n * icon shown while the row has no media.\n */\nexport type ComponentPreview = UiPreview;\n/** backend componentPreviewSchema: a preview slot names a key of at most this many characters */\nexport const COMPONENT_PREVIEW_KEY_MAX = 64;\n/** backend componentPreviewSchema: the row's literal fallback label, trimmed, at most this many characters */\nexport const COMPONENT_PREVIEW_FALLBACK_MAX = 120;\n\nexport interface ProdComponent {\n name: string;\n slug: string;\n description?: string;\n /** a library id, or since A3 the kit catalogue's own category verbatim (CATEGORY_ORDER) */\n category?: ComponentCategory | KitCategory;\n props: ComponentPropDef[];\n preview?: ComponentPreview | null;\n /** The component's dock icon (backend `components.icon`, dockIconSchema): V2's block icon. */\n icon?: DockIconName | null;\n /** The V2 tabs no top-level prop sits in (backend `components.tabs`, componentTabsSchema): declared, shown by none. */\n tabs?: PropTab[] | null;\n /** components.v2 (A3, componentV2Schema): category and icon are native columns */\n v2?: { shape?: V2Shape; placement?: V2Placement };\n /** the section family it is a variant of (its label): every member of a V2 block's variant family names the block */\n sectionType?: string;\n}\n\n/* ── Stored values ───────────────────────────────────────────────────── */\nexport const LINK_RELS = [\"nofollow\", \"sponsored\", \"ugc\"] as const;\nexport type LinkRel = (typeof LINK_RELS)[number];\nexport const LINK_VALUE_TYPES = [\"url\", \"page\", \"entry\", \"email\", \"phone\", \"anchor\", \"section\", \"file\"] as const;\nexport type LinkValueType = (typeof LINK_VALUE_TYPES)[number];\n/** The F1 link object (link-value.ts). A bare href string stays valid too. */\nexport interface ProdLinkValue {\n type?: LinkValueType;\n href: string;\n title?: string;\n newTab?: boolean;\n target?: \"_self\" | \"_blank\";\n rel?: LinkRel;\n section?: string;\n /** page / entry links: the target's id. */\n ref?: string;\n}\n\nexport const BUTTON_VARIANTS = [\"primary\", \"secondary\", \"tertiary\", \"brand\", \"dark\", \"light\", \"outline\", \"link\"] as const;\nexport type ButtonVariant = (typeof BUTTON_VARIANTS)[number];\nexport const BUTTON_SIZES = [\"sm\", \"md\", \"lg\"] as const;\n/** One button as one value (button-look.ts ButtonValue). */\nexport interface ProdButtonValue {\n label: string;\n href: string | ProdLinkValue;\n variant?: ButtonVariant;\n size?: (typeof BUTTON_SIZES)[number];\n arrow?: boolean;\n icon?: DockIconName | null;\n newTab?: boolean;\n rel?: LinkRel;\n /** Stretch across the column (backend button_full_width_invalid refuses a non-boolean a write brings). */\n fullWidth?: boolean;\n}\n\n/** 0–1 fractions of the image cut from each edge. */\nexport interface ProdImageCrop {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n/** The normalized image value (field-value-normalize.ts): other keys are dropped on write. */\nexport interface ProdImageValue {\n id?: string;\n url: string;\n name?: string;\n altText?: string;\n /** 0–1 fractions. */\n focal?: { x: number; y: number };\n crop?: ProdImageCrop;\n}\n\n/** The V2 video value's object form (video-value.ts): exactly the video block's props. A bare URL string is valid too. */\nexport const VIDEO_VALUE_KEYS = [\"url\", \"poster\", \"autoplay\", \"loop\", \"transcript\"] as const;\nexport interface ProdVideoValue {\n url: string;\n poster?: string;\n autoplay?: boolean;\n loop?: boolean;\n transcript?: string;\n}\n\n/** A polymorphic reference value. */\nexport interface ProdPolymorphicRef {\n entryId: string;\n modelSlug: string;\n}\n/**\n * A page, form or collection pick on a prop reference that also takes entries (backend reference-target-config.ts\n * targetRefOf): a bare string there is read as an entry id (acceptsEntries), so a non-entry pick names its kind.\n */\nexport interface ProdTargetRef {\n kind: \"page\" | \"form\" | \"collection\";\n id: string;\n}\n/** What a `form` prop stores. */\nexport interface ProdFormRef {\n formId: string;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiBA,IAAa,kBAAkB;;AAE/B,IAAa,UAAU;AACvB,IAAa,eAAe;AAC5B,IAAa,gBAAgB;AAC7B,IAAa,oBAAoB;AACjC,IAAa,2BAA2B,UAAU;AAClD,IAAa,iBAAiB,UAAU;AACxC,IAAa,gBAAgB,UAAU;AACvC,IAAa,uBAAuB,UAAU;AAC9C,IAAa,gBAAgB,UAAU;AACvC,IAAa,kBAAkB;AAC/B,IAAa,iBAAiB;AAC9B,IAAa,uBAAuB;AACpC,IAAa,uBAAuB;AACpC,IAAa,cAAc;;AAE3B,IAAa,uBAAuB;AACpC,IAAa,kBAAkB;AAC/B,IAAa,qBAAqB;;AAElC,IAAa,aAAa;AAC1B,IAAa,mBAAmB;;AAEhC,IAAa,iBAAiB;;AAE9B,IAAa,yBAAyB,UAAU;;AAEhD,IAAa,sBAAsB;AACnC,IAAa,kBAAkB;;AAE/B,IAAa,cAAc;AAC3B,IAAa,qBAAqB,UAAU;AAC5C,IAAa,iBAAiB;AAC9B,IAAa,kBAAkB;;AAE/B,IAAa,eAAe;;AAE5B,IAAa,6BAA6B,UAAU;AACpD,IAAa,uBAAuB;;AAEpC,IAAa,eAAe;AAC5B,IAAa,kBAAkB;;AAE/B,IAAa,wBAAwB;AACrC,IAAa,kBAAkB;AAC/B,IAAa,kBAAkB;AAC/B,IAAa,gBAAgB;AAC7B,IAAa,kBAAkB;AAC/B,IAAa,oBAAoB;;AAEjC,IAAa,uBAAuB;AAIpC,IAAa,kBAAkB;CAC7B;CAAO;CAAiB;CAAY;CAAO;CAAc;CAAe;CAAgB;CAAY;CACpG;CAAe;CAA0B;CAAS;CAAkB;CAAc;CAAO;CAAc;CACvG;CAAQ;CAAW;CAAiB;CAAc;CAAe;CAAc;CAAe;CAC9F;CAAY;CAAmB;CAAe;CAAmB;CAAqB;CACtF;CAAkB;CAAe;CAAW;CAAgB;CAAY;CAAS;CAAU;CAAY;CACvG;CAAY;CAAU;CAAa;CAAU;CAAQ;CAAQ;CAAmB;CAAiB;CACjG;CAAc;CAAQ;CAAQ;CAAQ;CAAU;CAAY;CAAmB;CAAS;CAAU;CAClG;CAAkB;CAAkB;CAAmB;CAAkB;CAAmB;CAC5F;CAAW;CAAiB;CAAa;CAAa;CAAW;CAAO;CAAY;CACpF;CAAkB;CAAe;CAAgB;CAAc;CAAY;CAAmB;CAC9F;CAAwB;CAAgB;CAAiB;CAAiB;CAAiB;CAAU;CACrG;CAAuB;CAAQ;CAAQ;CAAY;CAAc;CAAc;CAAgB;CAC/F;CAAqB;CAAe;CAAc;CAAc;CAAgB;CAAS;CAAY;CACrG;CAAa;CAAoB;CAAY;CAAgB;CAAgB;CAAiB;CAC9F;CAAkB;CAAU;CAAgB;CAAc;CAAmB;CAAe;CAAS;CACrG;CAAmB;CAAkB;CAAS;CAAe;CAAgB;CAAS;CAAgB;CACtG;CAAc;CAAO;CAAY;CAAe;CAAW;CAAa;CAAa;CAAW;CAChG;CAAW;CAAoB;CAAY;CAAY;CAAU;CAAQ;CAAe;CAAQ;CAChG;CAAmB;CAAa;CAAY;CAAmB;CAAW;CAAkB;CAC5F;CAAiB;CAAW;CAAW;CAAY;CAAY;CAAgB;CAAmB;CAClG;CAAiB;CAAkB;CAAiB;CAAgB;CAAa;CAAa;CAC9F;CAAuB;CAAuB;CAAwB;CAAqB;CAC3F;CAAY;CAAe;CAAQ;CAAW;CAAgB;CAAkB;CAAe;CAAQ;CACvG;CAAuB;CAAqB;CAAS;CAAS;CAAe;CAAY;CAAS;CAClG;CAAiB;CAAgB;CAAU;CAAc;CAAW;CAAY;CAAc;CAC9F;CAAY;CAAW;CAAY;CAAQ;CAAoB;CAAa;CAAiB;CAAiB;CAAa;CAC3H;CAAQ;CAAS;CAAS;CAAW;CAAU;CAAW;CAAS;CAAS;CAAY;CAAQ;CAChG;CAAU;CAAa;CAAQ;CAAU;CAAe;CAAU;CAAQ;CAAc;CAAW;CACnG;CAAgB;CAAmB;CAAe;CAAa;CAAW;CAAQ;CAAa;CAC/F;CAAY;CAAc;CAAS;CAAgB;CAAU;CAAS;CAAY;CAAe;CACjG;CAAgB;CAAuB;CAAU;CAAkB;CAAgB;CAAgB;CACnG;CAAU;CAAY;CAAQ;CAAY;CAAU;CAAU;CAAuB;CACrF;CAAsB;CAAoB;CAA2B;CAAgB;CACrF;CAAoB;CAAe;CAAe;CAAiB;CAAe;CAClF;CAAwB;CAAQ;CAAS;CAAS;CAAiB;CAAY;CAAQ;CAAgB;CACvG;CAAQ;CAAc;CAAgB;CAAgB;CAAa;CAAQ;CAAW;CAAW;CAAQ;CACzG;CAAW;CAAY;CAAU;CAAkB;CAAiB;CAAiB;CAAQ;CAAQ;CACrG;CAAa;CAAiB;CAAkB;CAAoB;CAAgB;CAAgB;CACpG;CAAoB;CAAiB;CAAc;CAAQ;CAAS;CAAQ;CAAS;CAAU;CAAW;CAC1G;CAAW;CAAW;CAAY;CAAW;CAAc;CAAe;CAAa;CAAS;CAAU;CAC1G;CAAS;CAAc;CAAgB;CAAa;CAAO;CAAc;CAAe;CAAQ;CAChG;CAAqB;CAAgB;CAAQ;CAAkB;CAAc;CAAS;CAAgB;CACtG;CAAe;CAAe;CAAY;CAAW;CAAU;CAAS;CAAa;CAAW;CAAW;CAC3G;CAAW;CAAU;CAAiB;CAAU;CAAS;CAAW;CAAY;CAAS;CAAU;CACnG;CAAU;CAAW;CAAS;CAAS;CAAiB;CAAS;CAAS;CAAU;CAAU;CAAU;CACxG;CAAU;CAAQ;CAAU;CAAY;CAAS;CAAgB;CAAe;CAAgB;CAChG;CAAsB;CAAa;CAAW;CAAa;CAAQ;CAAmB;CAAoB;CAC1G;CAAY;CAAgB;CAAW;CAAoB;CAAkB;CAAU;CAAS;CAChG;CAAiB;CAAU;CAAa;CAAQ;CAAe;CAAS;CAAQ;CAAiB;CACjG;CAAW;CAAO;CAAe;CAAQ;CAAS;CAAiB;CAAiB;CAAO;CAAQ;CAAU;CAC7G;CAAY;CAAQ;CAAY;CAAc;CAAQ;CAAY;CAAW;CAAY;CAAe;CACxG;CAAU;CAAS;CAAY;CAAsB;CAAgB;CAAS;CAAQ;CAAmB;CACzG;CAAa;CAAS;CAAgB;CAAoB;CAAW;CAAS;CAAkB;CAChG;CAAW;CAAc;CAAS;CAAa;CAAa;CAAQ;CAAW;CAAU;CAAU;CACnG;CAAU;CAAQ;CAAe;CAAa;CAAe;CAAa;CAAS;CAAe;CAClG;CAAS;CAAc;CAAM;CAAkB;CAAmB;CAAU;CAAS;CAAU;AACjG;AAEA,IAAa,qBAA0C,IAAI,IAAY,eAAe;;AA+BtF,IAAa,cAAc;CAAC;CAAa;CAAU;CAAU;CAAY;CAAS;CAAS;CAAQ;CAAU;AAAU;;AAGvH,IAAa,gBAAyE;CACpF,UAAU,CAAC,QAAQ;CACnB,OAAO,CAAC,QAAQ;CAChB,OAAO,CAAC,UAAU,aAAa;CAC/B,MAAM,CAAC,QAAQ;CACf,QAAQ,CAAC,SAAS;CAClB,UAAU,CAAC,WAAW,aAAa;AACrC;AA2MA,IAAa,uBAAuB;CAClC;CAAQ;CAAY;CAAS;CAAO;CAAW;CAAU;CAAU;CAAS;CAAS;CAAQ;CAC7F;CAAQ;CAAU;CAAQ;CAAS;CAAQ;CAAQ;CACnD;CAAU;CAAY;CAAQ;CAAS;CAAS;CAAQ;CAAY;CAAQ;CAAU;CAEtF;CAAa;CAEb;CAAS;CAAS;AACpB;AAIA,IAAa,uBAAuB;CAClC;CAAU;CAAU;CAAU;CAAW;CAAU;CAAQ;CAAQ;CAAU;CAAQ;CAAW;CAAgB;AAClH;;AAmIA,IAAa,4BAA4B;;AAEzC,IAAa,iCAAiC;AAqB9C,IAAa,YAAY;CAAC;CAAY;CAAa;AAAK;AAExD,IAAa,mBAAmB;CAAC;CAAO;CAAQ;CAAS;CAAS;CAAS;CAAU;CAAW;AAAM;AAetG,IAAa,kBAAkB;CAAC;CAAW;CAAa;CAAY;CAAS;CAAQ;CAAS;CAAW;AAAM;AAE/G,IAAa,eAAe;CAAC;CAAM;CAAM;AAAI;;AAkC7C,IAAa,mBAAmB;CAAC;CAAO;CAAU;CAAY;CAAQ;AAAY"}
|
package/dist/adapters/schema.js
CHANGED
|
@@ -386,8 +386,10 @@ function shapeOf(f, lv, ctx, top, nest) {
|
|
|
386
386
|
* `layout`, `page`, a forbidden key or a leading `_`; or one off KEY_FORMAT), renamed. V2 authoring follows the API
|
|
387
387
|
* lane, so `id` is a field key like any other (only the derive lane, which mints keys, reserves it).
|
|
388
388
|
*/
|
|
389
|
-
|
|
390
|
-
|
|
389
|
+
/** The address-prefix keys a model may hold below its top level (019141ffc): they are no address there. */
|
|
390
|
+
var NESTED_OK_KEYS = /* @__PURE__ */ new Set(["layout", "page"]);
|
|
391
|
+
function modelKeyOf(name, nested = false) {
|
|
392
|
+
if (KEY_FORMAT.test(name) && (!isReservedKey(name) || nested && NESTED_OK_KEYS.has(name))) return name;
|
|
391
393
|
let k = name.replace(/^_+/, "").replace(/[^A-Za-z0-9_-]/g, "");
|
|
392
394
|
if (!/^[A-Za-z]/.test(k)) k = `f${k}`;
|
|
393
395
|
return isReservedKey(k) ? `${k}Field` : k;
|
|
@@ -566,8 +568,8 @@ function convertField(f, at, lv, ctx, siblings, top, inTable = false, inItem = f
|
|
|
566
568
|
if (twins) {
|
|
567
569
|
node.key = `${f.name}_${twins + 1}`;
|
|
568
570
|
lose(ctx, `${at}/name`, "duplicate name");
|
|
569
|
-
} else if (!props && modelKeyOf(f.name) !== f.name) {
|
|
570
|
-
node.key = modelKeyOf(f.name);
|
|
571
|
+
} else if (!props && modelKeyOf(f.name, lv > 1) !== f.name) {
|
|
572
|
+
node.key = modelKeyOf(f.name, lv > 1);
|
|
571
573
|
lose(ctx, `${at}/name`, "reserved name");
|
|
572
574
|
}
|
|
573
575
|
const nesting = shape.kind === "object" || shape.kind === "zones" || shape.kind === "types";
|
|
@@ -831,10 +833,24 @@ function placeInSet(f, node, at, ctx, top, siblings) {
|
|
|
831
833
|
}
|
|
832
834
|
return;
|
|
833
835
|
}
|
|
836
|
+
if (f.fieldset !== void 0 && !ctx.sets.has(f.fieldset) && !ctx.tabs.has(f.fieldset) && !ctx.droppedTabs.has(f.fieldset) && f.fieldset.length <= 60) {
|
|
837
|
+
const derived = ctx.derivedSets ??= [];
|
|
838
|
+
if (derived.length + ctx.sets.size + ctx.tabs.size < 30) {
|
|
839
|
+
derived.push({
|
|
840
|
+
id: f.fieldset,
|
|
841
|
+
name: f.fieldset,
|
|
842
|
+
kind: "group"
|
|
843
|
+
});
|
|
844
|
+
ctx.sets.set(f.fieldset, void 0);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
834
847
|
const set = f.fieldset !== void 0 && ctx.sets.has(f.fieldset) ? f.fieldset : void 0;
|
|
835
848
|
if (f.fieldset !== void 0 && !set) lose(ctx, `${at}/fieldset`, "fieldset collision");
|
|
836
849
|
if (!top) {
|
|
837
|
-
if (f.group !== void 0)
|
|
850
|
+
if (f.group !== void 0) {
|
|
851
|
+
if (ctx.tabs.has(f.group)) node.ui.tab = { id: f.group };
|
|
852
|
+
else lose(ctx, `${at}/group`, "group (nested)");
|
|
853
|
+
}
|
|
838
854
|
if (set) node.fieldsetId = set;
|
|
839
855
|
return;
|
|
840
856
|
}
|
|
@@ -1619,28 +1635,8 @@ function blockModelFor(item, at, ctx, list) {
|
|
|
1619
1635
|
if (item[k] === void 0 || k === "type" || k === "title") continue;
|
|
1620
1636
|
if (k === "name") {
|
|
1621
1637
|
if (camel(slug) !== item.name) lose(ctx, p, "item identity");
|
|
1622
|
-
} else if (k === "fields") {
|
|
1623
|
-
|
|
1624
|
-
let shifted = false;
|
|
1625
|
-
fields.forEach((c, j) => {
|
|
1626
|
-
const fp = `${p}/${j}`;
|
|
1627
|
-
const mark = ctx.lossy.length;
|
|
1628
|
-
const markCat = ctx.catalog.length;
|
|
1629
|
-
const node = convertField(c, fp, 1, sub, fields, false);
|
|
1630
|
-
if (BLOCK_FIELD_TYPES.has(node.type)) {
|
|
1631
|
-
kept.push(emitField(node));
|
|
1632
|
-
if (dropped && !shifted) {
|
|
1633
|
-
shifted = true;
|
|
1634
|
-
lose(ctx, p, "block model field");
|
|
1635
|
-
}
|
|
1636
|
-
return;
|
|
1637
|
-
}
|
|
1638
|
-
ctx.lossy.length = mark;
|
|
1639
|
-
ctx.catalog.length = markCat;
|
|
1640
|
-
dropped = true;
|
|
1641
|
-
lose(ctx, fp, "block model field");
|
|
1642
|
-
});
|
|
1643
|
-
} else if (k === "openMode") cat(ctx, p, "item openMode");
|
|
1638
|
+
} else if (k === "fields") fields.forEach((c, j) => kept.push(emitField(convertField(c, `${p}/${j}`, 1, sub, fields, false))));
|
|
1639
|
+
else if (k === "openMode") chrome.openMode = item.openMode;
|
|
1644
1640
|
else if (k === "icon") {
|
|
1645
1641
|
const dock = dockIconOf(item.icon);
|
|
1646
1642
|
if (dock) chrome.icon = dock;
|
|
@@ -1648,11 +1644,17 @@ function blockModelFor(item, at, ctx, list) {
|
|
|
1648
1644
|
} else if (k === "preview") chrome.preview = previewToProd(item.preview, fields, p, ctx);
|
|
1649
1645
|
else lose(ctx, p, "item identity");
|
|
1650
1646
|
}
|
|
1651
|
-
const model = {
|
|
1647
|
+
const model = kept.every(blockRenders) ? {
|
|
1652
1648
|
name: item.title,
|
|
1653
1649
|
slug,
|
|
1654
1650
|
kind: "block",
|
|
1655
1651
|
fields: kept
|
|
1652
|
+
} : {
|
|
1653
|
+
name: item.title,
|
|
1654
|
+
slug,
|
|
1655
|
+
kind: "model",
|
|
1656
|
+
fields: kept,
|
|
1657
|
+
v2: { shape: "block" }
|
|
1656
1658
|
};
|
|
1657
1659
|
const prev = ctx.blockModels.get(slug);
|
|
1658
1660
|
const keep = () => {
|
|
@@ -2105,6 +2107,11 @@ function fromTypeSchema(v2, target) {
|
|
|
2105
2107
|
const preview = componentPreviewToProd(v2.preview, new Set(model.fields.map((x) => x.key)), v2.fields, "/preview", ctx);
|
|
2106
2108
|
if (Object.keys(preview).length) model.listPreview = preview;
|
|
2107
2109
|
}
|
|
2110
|
+
if (ctx.derivedSets) {
|
|
2111
|
+
const declared = fieldsets.filter((x) => x.kind !== "tab").length;
|
|
2112
|
+
ctx.derivedSets.forEach((_, i) => cat(ctx, `/fieldsets/${declared + i}`, "fieldset (derived)"));
|
|
2113
|
+
fieldsets.push(...ctx.derivedSets);
|
|
2114
|
+
}
|
|
2108
2115
|
if (fieldsets.length) model.fieldsets = fieldsets;
|
|
2109
2116
|
return {
|
|
2110
2117
|
schema: model,
|
|
@@ -2647,7 +2654,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
|
|
|
2647
2654
|
} else loss("fieldsetId", "fieldset", `No fieldset "${n.fieldsetId}" exists on this model; the membership is dropped.`);
|
|
2648
2655
|
}
|
|
2649
2656
|
if (lane === "props" && n.ui.group) f.fieldset = n.ui.group.id;
|
|
2650
|
-
if (lane === "props" && n.ui.tab) f.group = n.ui.tab.id;
|
|
2657
|
+
if ((lane === "props" || !top) && n.ui.tab) f.group = n.ui.tab.id;
|
|
2651
2658
|
for (const k of [
|
|
2652
2659
|
"target",
|
|
2653
2660
|
"stale",
|
|
@@ -2671,6 +2678,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
|
|
|
2671
2678
|
]);
|
|
2672
2679
|
const use = (...k) => k.forEach((x) => used.add(x));
|
|
2673
2680
|
const useUi = (...k) => k.forEach((x) => usedUi.add(x));
|
|
2681
|
+
if (!top && lane === "fields") useUi("tab");
|
|
2674
2682
|
if (lane === "props") useUi("group");
|
|
2675
2683
|
if (lane === "props") useUi("tab");
|
|
2676
2684
|
if (RICH_TYPES.has(n.type) && cfg.references !== void 0) {
|
|
@@ -2893,6 +2901,7 @@ function readToV2(n, at, siblings, ctx, top, lane) {
|
|
|
2893
2901
|
};
|
|
2894
2902
|
if (chrome?.icon) item.icon = tablerIconOf(chrome.icon);
|
|
2895
2903
|
if (chrome?.preview) item.preview = previewFromProd(chrome.preview, kids);
|
|
2904
|
+
if (chrome?.openMode) item.openMode = chrome.openMode;
|
|
2896
2905
|
return item;
|
|
2897
2906
|
});
|
|
2898
2907
|
const missing = slugs.filter((s) => !ctx.blockModels.has(s));
|