@velastack/cms 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. package/README.md +140 -28
  2. package/dist/backend/index.d.ts +1 -1
  3. package/dist/backend/routes/seed.d.ts +6 -0
  4. package/dist/backend/routes/seed.js +96 -0
  5. package/dist/backend/routes/table.js +3 -0
  6. package/dist/backend/store/queries.d.ts +6 -1
  7. package/dist/backend/store/queries.js +89 -0
  8. package/dist/cms.js +13 -16
  9. package/dist/components/admin-bar/admin-bar-internal.svelte +4 -4
  10. package/dist/components/admin-bar/admin-bar.css +1 -1
  11. package/dist/components/admin-bar/field-input.svelte +239 -0
  12. package/dist/components/admin-bar/field-input.svelte.d.ts +30 -0
  13. package/dist/components/admin-bar/fields-form.svelte +218 -0
  14. package/dist/components/admin-bar/fields-form.svelte.d.ts +16 -0
  15. package/dist/components/admin-bar/icons/pencil.svelte +19 -0
  16. package/dist/components/admin-bar/icons/pencil.svelte.d.ts +3 -0
  17. package/dist/components/admin-bar/keyboard-shortcuts-dialog.svelte +1 -1
  18. package/dist/components/admin-bar/link-input.svelte +102 -0
  19. package/dist/components/admin-bar/link-input.svelte.d.ts +9 -0
  20. package/dist/components/admin-bar/list-field.svelte +93 -0
  21. package/dist/components/admin-bar/list-field.svelte.d.ts +20 -0
  22. package/dist/components/admin-bar/locales-panel.svelte +42 -1
  23. package/dist/components/admin-bar/page-config.d.ts +8 -1
  24. package/dist/components/admin-bar/page-config.js +15 -2
  25. package/dist/components/admin-bar/page-options.d.ts +24 -0
  26. package/dist/components/admin-bar/page-options.js +53 -0
  27. package/dist/components/admin-bar/reorder-buttons.svelte +52 -0
  28. package/dist/components/admin-bar/reorder-buttons.svelte.d.ts +10 -0
  29. package/dist/components/admin-bar/rich-text-input.svelte +92 -0
  30. package/dist/components/admin-bar/rich-text-input.svelte.d.ts +9 -0
  31. package/dist/components/admin-bar/seo-panel.svelte +144 -189
  32. package/dist/components/admin-bar/site-options-panel.svelte +115 -0
  33. package/dist/components/admin-bar/site-options-panel.svelte.d.ts +7 -0
  34. package/dist/components/admin-bar/structured-editor-popover.svelte +259 -0
  35. package/dist/components/admin-bar/structured-editor-popover.svelte.d.ts +48 -0
  36. package/dist/components/admin-bar/ui/checkbox/checkbox.svelte +36 -0
  37. package/dist/components/admin-bar/ui/checkbox/checkbox.svelte.d.ts +5 -0
  38. package/dist/components/admin-bar/ui/checkbox/index.d.ts +2 -0
  39. package/dist/components/admin-bar/ui/checkbox/index.js +2 -0
  40. package/dist/components/admin-bar/ui/popover/index.d.ts +5 -0
  41. package/dist/components/admin-bar/ui/popover/index.js +7 -0
  42. package/dist/components/admin-bar/ui/popover/popover-close.svelte +7 -0
  43. package/dist/components/admin-bar/ui/popover/popover-close.svelte.d.ts +4 -0
  44. package/dist/components/admin-bar/ui/popover/popover-content.svelte +35 -0
  45. package/dist/components/admin-bar/ui/popover/popover-content.svelte.d.ts +10 -0
  46. package/dist/components/admin-bar/ui/popover/popover-trigger.svelte +7 -0
  47. package/dist/components/admin-bar/ui/popover/popover-trigger.svelte.d.ts +4 -0
  48. package/dist/components/admin-bar/ui/popover/popover.svelte +7 -0
  49. package/dist/components/admin-bar/ui/popover/popover.svelte.d.ts +3 -0
  50. package/dist/components/admin-bar/ui/sheet/index.d.ts +10 -0
  51. package/dist/components/admin-bar/ui/sheet/index.js +12 -0
  52. package/dist/components/admin-bar/ui/sheet/sheet-close.svelte +7 -0
  53. package/dist/components/admin-bar/ui/sheet/sheet-close.svelte.d.ts +4 -0
  54. package/dist/components/admin-bar/ui/sheet/sheet-content.svelte +64 -0
  55. package/dist/components/admin-bar/ui/sheet/sheet-content.svelte.d.ts +17 -0
  56. package/dist/components/admin-bar/ui/sheet/sheet-description.svelte +17 -0
  57. package/dist/components/admin-bar/ui/sheet/sheet-description.svelte.d.ts +4 -0
  58. package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte +23 -0
  59. package/dist/components/admin-bar/ui/sheet/sheet-footer.svelte.d.ts +5 -0
  60. package/dist/components/admin-bar/ui/sheet/sheet-header.svelte +20 -0
  61. package/dist/components/admin-bar/ui/sheet/sheet-header.svelte.d.ts +5 -0
  62. package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte +20 -0
  63. package/dist/components/admin-bar/ui/sheet/sheet-overlay.svelte.d.ts +4 -0
  64. package/dist/components/admin-bar/ui/sheet/sheet-title.svelte +17 -0
  65. package/dist/components/admin-bar/ui/sheet/sheet-title.svelte.d.ts +4 -0
  66. package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte +7 -0
  67. package/dist/components/admin-bar/ui/sheet/sheet-trigger.svelte.d.ts +4 -0
  68. package/dist/components/admin-bar/ui/sheet/sheet.svelte +7 -0
  69. package/dist/components/admin-bar/ui/sheet/sheet.svelte.d.ts +3 -0
  70. package/dist/components/admin-bar/ui/switch/index.d.ts +2 -0
  71. package/dist/components/admin-bar/ui/switch/index.js +2 -0
  72. package/dist/components/admin-bar/ui/switch/switch.svelte +27 -0
  73. package/dist/components/admin-bar/ui/switch/switch.svelte.d.ts +5 -0
  74. package/dist/components/admin-bar/ui/tabs/index.d.ts +5 -0
  75. package/dist/components/admin-bar/ui/tabs/index.js +7 -0
  76. package/dist/components/admin-bar/ui/tabs/tabs-content.svelte +17 -0
  77. package/dist/components/admin-bar/ui/tabs/tabs-content.svelte.d.ts +4 -0
  78. package/dist/components/admin-bar/ui/tabs/tabs-list.svelte +16 -0
  79. package/dist/components/admin-bar/ui/tabs/tabs-list.svelte.d.ts +4 -0
  80. package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte +20 -0
  81. package/dist/components/admin-bar/ui/tabs/tabs-trigger.svelte.d.ts +4 -0
  82. package/dist/components/admin-bar/ui/tabs/tabs.svelte +19 -0
  83. package/dist/components/admin-bar/ui/tabs/tabs.svelte.d.ts +4 -0
  84. package/dist/components/cms/cms-boolean.svelte +9 -18
  85. package/dist/components/cms/cms-boolean.svelte.d.ts +1 -0
  86. package/dist/components/cms/cms-collection.svelte +46 -0
  87. package/dist/components/cms/cms-collection.svelte.d.ts +25 -0
  88. package/dist/components/cms/cms-contact.svelte +26 -0
  89. package/dist/components/cms/cms-contact.svelte.d.ts +14 -0
  90. package/dist/components/cms/cms-date-time-editable.svelte +3 -3
  91. package/dist/components/cms/cms-date-time.svelte +17 -17
  92. package/dist/components/cms/cms-date-time.svelte.d.ts +2 -1
  93. package/dist/components/cms/cms-faq.svelte +22 -0
  94. package/dist/components/cms/cms-faq.svelte.d.ts +15 -0
  95. package/dist/components/cms/cms-gallery.svelte +31 -0
  96. package/dist/components/cms/cms-gallery.svelte.d.ts +16 -0
  97. package/dist/components/cms/cms-hours-editable.svelte +160 -0
  98. package/dist/components/cms/cms-hours-editable.svelte.d.ts +17 -0
  99. package/dist/components/cms/cms-hours.svelte +42 -0
  100. package/dist/components/cms/cms-hours.svelte.d.ts +19 -0
  101. package/dist/components/cms/cms-image-editable.svelte +13 -4
  102. package/dist/components/cms/cms-image.svelte +19 -34
  103. package/dist/components/cms/cms-image.svelte.d.ts +7 -8
  104. package/dist/components/cms/cms-link-editable.svelte +4 -60
  105. package/dist/components/cms/cms-link.svelte +15 -28
  106. package/dist/components/cms/cms-link.svelte.d.ts +4 -9
  107. package/dist/components/cms/cms-logos.svelte +29 -0
  108. package/dist/components/cms/cms-logos.svelte.d.ts +16 -0
  109. package/dist/components/cms/cms-markdown.svelte +15 -16
  110. package/dist/components/cms/cms-markdown.svelte.d.ts +1 -0
  111. package/dist/components/cms/cms-nav.svelte +22 -0
  112. package/dist/components/cms/cms-nav.svelte.d.ts +15 -0
  113. package/dist/components/cms/cms-number-editable.svelte +4 -2
  114. package/dist/components/cms/cms-number.svelte +18 -17
  115. package/dist/components/cms/cms-number.svelte.d.ts +2 -1
  116. package/dist/components/cms/cms-pricing.svelte +36 -0
  117. package/dist/components/cms/cms-pricing.svelte.d.ts +14 -0
  118. package/dist/components/cms/cms-rich-text.svelte +17 -22
  119. package/dist/components/cms/cms-rich-text.svelte.d.ts +1 -0
  120. package/dist/components/cms/cms-schedule.svelte +35 -0
  121. package/dist/components/cms/cms-schedule.svelte.d.ts +16 -0
  122. package/dist/components/cms/cms-social-links.svelte +35 -0
  123. package/dist/components/cms/cms-social-links.svelte.d.ts +15 -0
  124. package/dist/components/cms/cms-stats.svelte +31 -0
  125. package/dist/components/cms/cms-stats.svelte.d.ts +16 -0
  126. package/dist/components/cms/cms-steps.svelte +31 -0
  127. package/dist/components/cms/cms-steps.svelte.d.ts +16 -0
  128. package/dist/components/cms/cms-store.svelte.d.ts +14 -0
  129. package/dist/components/cms/cms-store.svelte.js +39 -2
  130. package/dist/components/cms/cms-structured-editable.svelte +29 -0
  131. package/dist/components/cms/{cms-repeater-editable.svelte.d.ts → cms-structured-editable.svelte.d.ts} +12 -8
  132. package/dist/components/cms/cms-structured.svelte +50 -0
  133. package/dist/components/cms/cms-structured.svelte.d.ts +49 -0
  134. package/dist/components/cms/cms-team.svelte +22 -0
  135. package/dist/components/cms/cms-team.svelte.d.ts +15 -0
  136. package/dist/components/cms/cms-testimonials.svelte +36 -0
  137. package/dist/components/cms/cms-testimonials.svelte.d.ts +15 -0
  138. package/dist/components/cms/cms-text.svelte +23 -30
  139. package/dist/components/cms/cms-text.svelte.d.ts +3 -1
  140. package/dist/components/cms/cms-timeline.svelte +35 -0
  141. package/dist/components/cms/cms-timeline.svelte.d.ts +16 -0
  142. package/dist/components/cms/index.d.ts +15 -1
  143. package/dist/components/cms/index.js +15 -1
  144. package/dist/components/cms/scope.d.ts +25 -5
  145. package/dist/components/cms/use-cms-field.svelte.d.ts +54 -0
  146. package/dist/components/cms/use-cms-field.svelte.js +83 -0
  147. package/dist/core/metadata.d.ts +44 -0
  148. package/dist/core/metadata.js +59 -0
  149. package/dist/core/shapes/collection.d.ts +60 -0
  150. package/dist/core/shapes/collection.js +194 -0
  151. package/dist/core/shapes/contact.d.ts +70 -0
  152. package/dist/core/shapes/contact.js +214 -0
  153. package/dist/core/shapes/faq.d.ts +12 -0
  154. package/dist/core/shapes/faq.js +48 -0
  155. package/dist/core/shapes/form.d.ts +46 -0
  156. package/dist/core/shapes/form.js +7 -0
  157. package/dist/core/shapes/hours.d.ts +91 -0
  158. package/dist/core/shapes/hours.js +325 -0
  159. package/dist/core/shapes/image.d.ts +15 -0
  160. package/dist/core/shapes/image.js +27 -0
  161. package/dist/core/shapes/index.d.ts +31 -0
  162. package/dist/core/shapes/index.js +13 -0
  163. package/dist/core/shapes/link.d.ts +15 -0
  164. package/dist/core/shapes/link.js +42 -0
  165. package/dist/core/shapes/nav.d.ts +21 -0
  166. package/dist/core/shapes/nav.js +71 -0
  167. package/dist/core/shapes/presets.d.ts +65 -0
  168. package/dist/core/shapes/presets.js +152 -0
  169. package/dist/core/shapes/pricing.d.ts +40 -0
  170. package/dist/core/shapes/pricing.js +115 -0
  171. package/dist/core/shapes/social-links.d.ts +19 -0
  172. package/dist/core/shapes/social-links.js +83 -0
  173. package/dist/core/shapes/team.d.ts +22 -0
  174. package/dist/core/shapes/team.js +70 -0
  175. package/dist/core/shapes/testimonials.d.ts +24 -0
  176. package/dist/core/shapes/testimonials.js +86 -0
  177. package/dist/core/structured-registry.d.ts +10 -0
  178. package/dist/core/structured-registry.js +6 -0
  179. package/dist/core/structured.d.ts +131 -0
  180. package/dist/core/structured.js +235 -0
  181. package/dist/core/wire.d.ts +31 -1
  182. package/dist/editor.d.ts +26 -0
  183. package/dist/editor.js +25 -0
  184. package/dist/index.d.ts +26 -4
  185. package/dist/index.js +31 -2
  186. package/dist/server/load-cms.js +1 -0
  187. package/package.json +12 -3
  188. package/plugin/README.md +22 -2
  189. package/plugin/dist/build.d.ts +74 -0
  190. package/plugin/dist/build.js +210 -0
  191. package/plugin/dist/build.js.map +1 -0
  192. package/plugin/dist/index.d.ts +10 -1
  193. package/plugin/dist/index.js +63 -4
  194. package/plugin/dist/index.js.map +1 -1
  195. package/plugin/dist/manifest.d.ts +31 -0
  196. package/plugin/dist/manifest.js +146 -41
  197. package/plugin/dist/manifest.js.map +1 -1
  198. package/plugin/dist/parse-svelte.d.ts +15 -0
  199. package/plugin/dist/parse-svelte.js +26 -16
  200. package/plugin/dist/parse-svelte.js.map +1 -1
  201. package/dist/components/admin-bar/site-settings-panel.svelte +0 -225
  202. package/dist/components/admin-bar/site-settings-panel.svelte.d.ts +0 -7
  203. package/dist/components/cms/cms-repeater-editable.svelte +0 -137
  204. package/dist/components/cms/cms-repeater.svelte +0 -55
  205. package/dist/components/cms/cms-repeater.svelte.d.ts +0 -30
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Structured values: the shapes behind the editor components (hours, team,
3
+ * testimonials, …) and the two rules every one of them follows.
4
+ *
5
+ * 1. **Versioned shapes, whole-object writes.** A structured value is a plain
6
+ * object with a `v` version number. Components never patch a leaf inside
7
+ * it; they read the whole value through the schema's `normalize`, change
8
+ * it, and write the whole object back. A field the editor clears is
9
+ * written as `null`, never dropped: the draft and release trees deep-merge
10
+ * over the published tree, and a missing key merges to "unchanged" while
11
+ * `null` merges to "cleared". That is what stops a cleared field from
12
+ * reverting to the template's demo copy.
13
+ *
14
+ * 2. **Localisation is a string overlay, never a second structure.** The
15
+ * default locale stores the full value at `<name>`. Every other locale
16
+ * stores only strings, at `<name>.$t.<id>.<field>`, keyed by the stable
17
+ * `id` of the list item they translate (the reserved id `_` addresses the
18
+ * value's own root fields). Because objects deep-merge across locales,
19
+ * reading `es` yields the default structure plus the `es` overlay, and
20
+ * {@link applyTranslations} folds the strings in. Structure edits happen in
21
+ * the default locale only, so the item list can never fork between
22
+ * languages.
23
+ */
24
+ import { type Tree } from './path.js';
25
+ /** Branch under a structured value that holds a locale's string overlay. */
26
+ export declare const TRANSLATIONS_KEY = "$t";
27
+ /** Overlay id that addresses the structured value's root fields. */
28
+ export declare const ROOT_ID = "_";
29
+ /** `{ [itemId]: { [field]: string } }` — what a non-default locale stores. */
30
+ export type TranslationOverlay = Record<string, Record<string, string>>;
31
+ export type StructuredItem = {
32
+ id: string;
33
+ } & Record<string, unknown>;
34
+ /**
35
+ * One list of items inside a structured value. `key` names the array field,
36
+ * `translatable` the item fields holding translatable text (a string, or an
37
+ * array of strings, translated per index as `<field>.<i>`), and `items` any
38
+ * lists nested inside each item (a collection item's `details`, a pricing
39
+ * tier's `features`). Every item at every level carries a stable `id`.
40
+ */
41
+ export type ItemsSpec = {
42
+ key: string;
43
+ translatable: readonly string[];
44
+ items?: ItemsSpec | readonly ItemsSpec[];
45
+ };
46
+ export type StructuredSchema<T extends Tree> = {
47
+ /** Exported component name, e.g. `'CmsHours'`. Recorded in the manifest. */
48
+ component: string;
49
+ /** Current shape version, written as `v` on every value. */
50
+ version: number;
51
+ /** Root-level fields holding translatable strings, e.g. `['note']`. A
52
+ * dotted path addresses a nested field (`'labels.closed'`). */
53
+ translatable: readonly string[];
54
+ /** The value's item lists, when it has any, and the item fields that translate. */
55
+ items?: ItemsSpec | readonly ItemsSpec[];
56
+ /**
57
+ * Turn anything the store may hold — an older version, a hand-seeded
58
+ * partial object, garbage — into the current shape. Must never throw.
59
+ */
60
+ normalize: (raw: unknown) => T;
61
+ /** The cleared value: what an editor gets after "Clear". */
62
+ empty: () => T;
63
+ };
64
+ export type Structured<T extends Tree> = StructuredSchema<T> & {
65
+ /**
66
+ * Resolve a stored value the way a display component reads it:
67
+ * `undefined` (nothing stored) → the fallback; `null` (cleared) → empty;
68
+ * anything else → normalized, with the locale overlay applied.
69
+ */
70
+ read: (raw: unknown, fallback: T | undefined) => T;
71
+ };
72
+ export declare const isPlainObject: (v: unknown) => v is Tree;
73
+ export declare const defineStructured: <T extends Tree>(schema: StructuredSchema<T>) => Structured<T>;
74
+ /** Path of one translated string: `<name>.$t.<id>.<field>`. */
75
+ export declare const translationPath: (name: string, id: string, field: string) => string;
76
+ /** The `$t` branch of a stored value, when it is one. A dotted field
77
+ * (`labels.closed`, `tags.0`) is stored nested by the path-based writes and
78
+ * read back flat here. */
79
+ export declare const extractTranslations: (raw: unknown) => TranslationOverlay | undefined;
80
+ /** A copy of `raw` without its `$t` branch, so a whole-object write in the
81
+ * default locale never carries another locale's strings. */
82
+ export declare const stripTranslations: (raw: unknown) => unknown;
83
+ /** `set` that copies along the path instead of mutating shared branches. */
84
+ export declare const setIn: (obj: Tree, path: string, value: unknown) => Tree;
85
+ /**
86
+ * Fold a locale's string overlay into a normalized default-locale value.
87
+ * Root fields come from the `_` id; item fields from the item's own `id`,
88
+ * at every nesting level. Only non-empty strings override, so an
89
+ * untranslated field shows the default-locale text rather than a blank.
90
+ */
91
+ export declare const applyTranslations: <T extends Tree>(value: T, overlay: TranslationOverlay | undefined, schema: Pick<StructuredSchema<T>, "translatable" | "items">) => T;
92
+ export type TranslatableField = {
93
+ /** Overlay id: an item id, or `_` for a root field. */
94
+ id: string;
95
+ /** Field path within the object the id addresses (`note`, `labels.closed`, `tags.0`). */
96
+ field: string;
97
+ /** The default-locale string being translated. */
98
+ source: string;
99
+ };
100
+ /**
101
+ * Every translatable, non-empty string in a normalized default-locale value.
102
+ * Drives the popover's translation tab and the Locales panel's counts.
103
+ */
104
+ export declare const translatableFields: <T extends Tree>(value: T, schema: Pick<StructuredSchema<T>, "translatable" | "items">) => TranslatableField[];
105
+ /** How many translatable strings a locale's overlay still lacks. */
106
+ export declare const countTranslations: <T extends Tree>(value: T, overlay: TranslationOverlay | undefined, schema: Pick<StructuredSchema<T>, "translatable" | "items">) => {
107
+ total: number;
108
+ missing: number;
109
+ };
110
+ export declare const asString: (v: unknown, fallback?: string) => string;
111
+ export declare const asNumber: (v: unknown, fallback?: number | null) => number | null;
112
+ export declare const asBoolean: (v: unknown, fallback?: boolean) => boolean;
113
+ /**
114
+ * Normalize a list of items, guaranteeing every item carries a string `id`.
115
+ * An item without one (a hand-written seed, say) gets a deterministic
116
+ * `item-<index>` so reads stay stable until the editor next writes the list,
117
+ * at which point the ids are persisted.
118
+ */
119
+ export declare const asItems: <I extends StructuredItem>(raw: unknown, normalizeItem: (item: Tree, index: number) => Omit<I, "id">) => I[];
120
+ /** A short random id for a newly added item. Stable once written. */
121
+ export declare const newItemId: () => string;
122
+ /** Read a structured value's overlay for one locale out of a whole scope tree. */
123
+ export declare const overlayAt: (tree: Tree | undefined, name: string) => TranslationOverlay | undefined;
124
+ /** A list of strings; anything else is dropped. */
125
+ export declare const asStrings: (v: unknown) => string[];
126
+ /** A nullable string: `null` (cleared) stays `null`; anything else is a string. */
127
+ export declare const asOptionalString: (v: unknown) => string | null;
128
+ /** One of a fixed set of strings, else the fallback. */
129
+ export declare const asEnum: <E extends string>(v: unknown, values: readonly E[], fallback: E) => E;
130
+ /** Move an item within a list, returning a new list. */
131
+ export declare const moveItem: <I>(list: readonly I[], from: number, to: number) => I[];
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Structured values: the shapes behind the editor components (hours, team,
3
+ * testimonials, …) and the two rules every one of them follows.
4
+ *
5
+ * 1. **Versioned shapes, whole-object writes.** A structured value is a plain
6
+ * object with a `v` version number. Components never patch a leaf inside
7
+ * it; they read the whole value through the schema's `normalize`, change
8
+ * it, and write the whole object back. A field the editor clears is
9
+ * written as `null`, never dropped: the draft and release trees deep-merge
10
+ * over the published tree, and a missing key merges to "unchanged" while
11
+ * `null` merges to "cleared". That is what stops a cleared field from
12
+ * reverting to the template's demo copy.
13
+ *
14
+ * 2. **Localisation is a string overlay, never a second structure.** The
15
+ * default locale stores the full value at `<name>`. Every other locale
16
+ * stores only strings, at `<name>.$t.<id>.<field>`, keyed by the stable
17
+ * `id` of the list item they translate (the reserved id `_` addresses the
18
+ * value's own root fields). Because objects deep-merge across locales,
19
+ * reading `es` yields the default structure plus the `es` overlay, and
20
+ * {@link applyTranslations} folds the strings in. Structure edits happen in
21
+ * the default locale only, so the item list can never fork between
22
+ * languages.
23
+ */
24
+ import { get, leafPaths } from './path.js';
25
+ /** Branch under a structured value that holds a locale's string overlay. */
26
+ export const TRANSLATIONS_KEY = '$t';
27
+ /** Overlay id that addresses the structured value's root fields. */
28
+ export const ROOT_ID = '_';
29
+ export const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
30
+ export const defineStructured = (schema) => ({
31
+ ...schema,
32
+ read: (raw, fallback) => {
33
+ if (raw === undefined)
34
+ return fallback === undefined ? schema.empty() : schema.normalize(fallback);
35
+ if (raw === null)
36
+ return schema.empty();
37
+ const overlay = extractTranslations(raw);
38
+ const value = schema.normalize(stripTranslations(raw));
39
+ return overlay ? applyTranslations(value, overlay, schema) : value;
40
+ }
41
+ });
42
+ /** Path of one translated string: `<name>.$t.<id>.<field>`. */
43
+ export const translationPath = (name, id, field) => `${name}.${TRANSLATIONS_KEY}.${id}.${field}`;
44
+ /** The `$t` branch of a stored value, when it is one. A dotted field
45
+ * (`labels.closed`, `tags.0`) is stored nested by the path-based writes and
46
+ * read back flat here. */
47
+ export const extractTranslations = (raw) => {
48
+ if (!isPlainObject(raw))
49
+ return undefined;
50
+ const t = raw[TRANSLATIONS_KEY];
51
+ if (!isPlainObject(t))
52
+ return undefined;
53
+ const out = {};
54
+ for (const [id, fields] of Object.entries(t)) {
55
+ if (!isPlainObject(fields))
56
+ continue;
57
+ const strings = {};
58
+ for (const path of leafPaths(fields)) {
59
+ const v = get(fields, path);
60
+ if (typeof v === 'string')
61
+ strings[path] = v;
62
+ }
63
+ out[id] = strings;
64
+ }
65
+ return out;
66
+ };
67
+ /** A copy of `raw` without its `$t` branch, so a whole-object write in the
68
+ * default locale never carries another locale's strings. */
69
+ export const stripTranslations = (raw) => {
70
+ if (!isPlainObject(raw) || !(TRANSLATIONS_KEY in raw))
71
+ return raw;
72
+ const { [TRANSLATIONS_KEY]: _omit, ...rest } = raw;
73
+ return rest;
74
+ };
75
+ const overlayString = (overlay, id, field) => {
76
+ const v = overlay[id]?.[field];
77
+ return typeof v === 'string' && v !== '' ? v : undefined;
78
+ };
79
+ const specList = (items) => items === undefined ? [] : Array.isArray(items) ? items : [items];
80
+ const isStringArray = (v) => Array.isArray(v) && v.every((s) => typeof s === 'string');
81
+ /** `set` that copies along the path instead of mutating shared branches. */
82
+ export const setIn = (obj, path, value) => {
83
+ const [head, ...rest] = path.split('.');
84
+ const out = { ...obj };
85
+ if (rest.length === 0) {
86
+ out[head] = value;
87
+ return out;
88
+ }
89
+ const child = obj[head];
90
+ out[head] = setIn(isPlainObject(child) ? child : {}, rest.join('.'), value);
91
+ return out;
92
+ };
93
+ /**
94
+ * Fold a locale's string overlay into one object (the root value or an item)
95
+ * and, recursively, into the item lists it holds.
96
+ */
97
+ const applyTo = (obj, id, translatable, items, overlay) => {
98
+ let out = { ...obj };
99
+ for (const field of translatable) {
100
+ const cur = get(obj, field);
101
+ if (typeof cur === 'string') {
102
+ const s = overlayString(overlay, id, field);
103
+ if (s !== undefined)
104
+ out = setIn(out, field, s);
105
+ }
106
+ else if (isStringArray(cur)) {
107
+ let changed = false;
108
+ const next = cur.map((v, i) => {
109
+ const s = overlayString(overlay, id, `${field}.${i}`);
110
+ if (s === undefined)
111
+ return v;
112
+ changed = true;
113
+ return s;
114
+ });
115
+ if (changed)
116
+ out = setIn(out, field, next);
117
+ }
118
+ }
119
+ for (const spec of specList(items)) {
120
+ const list = obj[spec.key];
121
+ if (!Array.isArray(list))
122
+ continue;
123
+ out[spec.key] = list.map((item) => isPlainObject(item) && typeof item.id === 'string'
124
+ ? applyTo(item, item.id, spec.translatable, spec.items, overlay)
125
+ : item);
126
+ }
127
+ return out;
128
+ };
129
+ /**
130
+ * Fold a locale's string overlay into a normalized default-locale value.
131
+ * Root fields come from the `_` id; item fields from the item's own `id`,
132
+ * at every nesting level. Only non-empty strings override, so an
133
+ * untranslated field shows the default-locale text rather than a blank.
134
+ */
135
+ export const applyTranslations = (value, overlay, schema) => {
136
+ if (!overlay)
137
+ return value;
138
+ return applyTo(value, ROOT_ID, schema.translatable, schema.items, overlay);
139
+ };
140
+ const collectFrom = (obj, id, translatable, items, out) => {
141
+ for (const field of translatable) {
142
+ const cur = get(obj, field);
143
+ if (typeof cur === 'string') {
144
+ if (cur !== '')
145
+ out.push({ id, field, source: cur });
146
+ }
147
+ else if (isStringArray(cur)) {
148
+ cur.forEach((s, i) => {
149
+ if (s !== '')
150
+ out.push({ id, field: `${field}.${i}`, source: s });
151
+ });
152
+ }
153
+ }
154
+ for (const spec of specList(items)) {
155
+ const list = obj[spec.key];
156
+ if (!Array.isArray(list))
157
+ continue;
158
+ for (const item of list) {
159
+ if (!isPlainObject(item) || typeof item.id !== 'string')
160
+ continue;
161
+ collectFrom(item, item.id, spec.translatable, spec.items, out);
162
+ }
163
+ }
164
+ };
165
+ /**
166
+ * Every translatable, non-empty string in a normalized default-locale value.
167
+ * Drives the popover's translation tab and the Locales panel's counts.
168
+ */
169
+ export const translatableFields = (value, schema) => {
170
+ const out = [];
171
+ collectFrom(value, ROOT_ID, schema.translatable, schema.items, out);
172
+ return out;
173
+ };
174
+ /** How many translatable strings a locale's overlay still lacks. */
175
+ export const countTranslations = (value, overlay, schema) => {
176
+ const fields = translatableFields(value, schema);
177
+ let missing = 0;
178
+ for (const f of fields) {
179
+ if (!overlay || overlayString(overlay, f.id, f.field) === undefined)
180
+ missing += 1;
181
+ }
182
+ return { total: fields.length, missing };
183
+ };
184
+ // ---------------------------------------------------------------------------
185
+ // Normalizer building blocks
186
+ // ---------------------------------------------------------------------------
187
+ export const asString = (v, fallback = '') => typeof v === 'string' ? v : fallback;
188
+ export const asNumber = (v, fallback = null) => typeof v === 'number' && Number.isFinite(v) ? v : fallback;
189
+ export const asBoolean = (v, fallback = false) => typeof v === 'boolean' ? v : fallback;
190
+ /**
191
+ * Normalize a list of items, guaranteeing every item carries a string `id`.
192
+ * An item without one (a hand-written seed, say) gets a deterministic
193
+ * `item-<index>` so reads stay stable until the editor next writes the list,
194
+ * at which point the ids are persisted.
195
+ */
196
+ export const asItems = (raw, normalizeItem) => {
197
+ if (!Array.isArray(raw))
198
+ return [];
199
+ const out = [];
200
+ raw.forEach((entry, index) => {
201
+ if (!isPlainObject(entry))
202
+ return;
203
+ const id = typeof entry.id === 'string' && entry.id !== '' ? entry.id : `item-${index}`;
204
+ out.push({ id, ...normalizeItem(entry, index) });
205
+ });
206
+ return out;
207
+ };
208
+ /** A short random id for a newly added item. Stable once written. */
209
+ export const newItemId = () => {
210
+ const bytes = new Uint8Array(6);
211
+ if (typeof crypto !== 'undefined' && crypto.getRandomValues)
212
+ crypto.getRandomValues(bytes);
213
+ else
214
+ for (let i = 0; i < bytes.length; i++)
215
+ bytes[i] = Math.floor(Math.random() * 256);
216
+ let out = '';
217
+ for (const b of bytes)
218
+ out += b.toString(16).padStart(2, '0');
219
+ return out;
220
+ };
221
+ /** Read a structured value's overlay for one locale out of a whole scope tree. */
222
+ export const overlayAt = (tree, name) => extractTranslations(get(tree, name));
223
+ /** A list of strings; anything else is dropped. */
224
+ export const asStrings = (v) => Array.isArray(v) ? v.filter((s) => typeof s === 'string') : [];
225
+ /** A nullable string: `null` (cleared) stays `null`; anything else is a string. */
226
+ export const asOptionalString = (v) => typeof v === 'string' && v !== '' ? v : null;
227
+ /** One of a fixed set of strings, else the fallback. */
228
+ export const asEnum = (v, values, fallback) => typeof v === 'string' && values.includes(v) ? v : fallback;
229
+ /** Move an item within a list, returning a new list. */
230
+ export const moveItem = (list, from, to) => {
231
+ const next = [...list];
232
+ const [item] = next.splice(from, 1);
233
+ next.splice(to, 0, item);
234
+ return next;
235
+ };
@@ -11,7 +11,7 @@
11
11
  * `published_at` stay snake_case because that is what the JSON carries.
12
12
  */
13
13
  import type { Tree } from './path.js';
14
- import type { PageDeleteOutcome } from './page-entry.js';
14
+ import type { PageDeleteOutcome, PageEntry } from './page-entry.js';
15
15
  export type { PageDeleteOutcome };
16
16
  /** One pending change in a release, without the bookkeeping the store adds. */
17
17
  export type ReleaseItemCore = {
@@ -182,6 +182,36 @@ export type CmsDeployState = {
182
182
  };
183
183
  latest?: CmsDeployRun | null;
184
184
  };
185
+ /**
186
+ * A project's published content in one object: `layouts` and `pages` keyed
187
+ * `[locale][routeId]`, plus the non-localised `site` tree. `POST /seed`
188
+ * accepts it and `GET /export` returns it, so a template's published
189
+ * `content/` can seed a fresh project and a project can be dumped back into
190
+ * the same shape. A page keyed by route id may be a bare tree (a static page,
191
+ * `params: {}`) or a full `PageEntry[]`.
192
+ */
193
+ export type CmsSeed = {
194
+ layouts?: Record<string, Record<string, Tree>>;
195
+ pages?: Record<string, Record<string, Tree | PageEntry[]>>;
196
+ site?: Tree;
197
+ };
198
+ export type SeedRequest = CmsSeed & {
199
+ /** Overwrite a project that already has published rows. */
200
+ force?: boolean;
201
+ };
202
+ export type SeedSummary = {
203
+ locales: string[];
204
+ layouts: number;
205
+ pages: number;
206
+ site: boolean;
207
+ };
208
+ export type SeedResponse = {
209
+ ok: true;
210
+ seeded: SeedSummary;
211
+ } | {
212
+ ok: false;
213
+ reason: 'already-seeded';
214
+ };
185
215
  export type DocsResponse = {
186
216
  contents: Tree;
187
217
  } | PageDeleteOutcome;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * `@velastack/cms/editor` — the editing chrome a structured component's
3
+ * editable sibling imports lazily, so none of it reaches the public bundle:
4
+ *
5
+ * ```svelte
6
+ * {#await import('@velastack/cms/editor') then { StructuredEditorPopover, FieldInput, ReorderButtons }}
7
+ * ```
8
+ */
9
+ export { default as StructuredEditorPopover } from './components/admin-bar/structured-editor-popover.svelte';
10
+ export { default as FieldInput } from './components/admin-bar/field-input.svelte';
11
+ export type { FieldInputType } from './components/admin-bar/field-input.svelte';
12
+ export { default as ReorderButtons } from './components/admin-bar/reorder-buttons.svelte';
13
+ export { default as FieldsForm } from './components/admin-bar/fields-form.svelte';
14
+ export { default as ListField } from './components/admin-bar/list-field.svelte';
15
+ export { default as LinkInput } from './components/admin-bar/link-input.svelte';
16
+ export { default as RichTextInput } from './components/admin-bar/rich-text-input.svelte';
17
+ export { default as CssRoot } from './components/admin-bar/css-root.svelte';
18
+ export { Button } from './components/admin-bar/ui/button/index.js';
19
+ export { Input } from './components/admin-bar/ui/input/index.js';
20
+ export { Textarea } from './components/admin-bar/ui/textarea/index.js';
21
+ export { Switch } from './components/admin-bar/ui/switch/index.js';
22
+ export { Checkbox } from './components/admin-bar/ui/checkbox/index.js';
23
+ export * as Popover from './components/admin-bar/ui/popover/index.js';
24
+ export * as Tabs from './components/admin-bar/ui/tabs/index.js';
25
+ export * as Sheet from './components/admin-bar/ui/sheet/index.js';
26
+ export { default as CmsMediaPicker } from './components/cms/cms-media-picker.svelte';
package/dist/editor.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@velastack/cms/editor` — the editing chrome a structured component's
3
+ * editable sibling imports lazily, so none of it reaches the public bundle:
4
+ *
5
+ * ```svelte
6
+ * {#await import('@velastack/cms/editor') then { StructuredEditorPopover, FieldInput, ReorderButtons }}
7
+ * ```
8
+ */
9
+ export { default as StructuredEditorPopover } from './components/admin-bar/structured-editor-popover.svelte';
10
+ export { default as FieldInput } from './components/admin-bar/field-input.svelte';
11
+ export { default as ReorderButtons } from './components/admin-bar/reorder-buttons.svelte';
12
+ export { default as FieldsForm } from './components/admin-bar/fields-form.svelte';
13
+ export { default as ListField } from './components/admin-bar/list-field.svelte';
14
+ export { default as LinkInput } from './components/admin-bar/link-input.svelte';
15
+ export { default as RichTextInput } from './components/admin-bar/rich-text-input.svelte';
16
+ export { default as CssRoot } from './components/admin-bar/css-root.svelte';
17
+ export { Button } from './components/admin-bar/ui/button/index.js';
18
+ export { Input } from './components/admin-bar/ui/input/index.js';
19
+ export { Textarea } from './components/admin-bar/ui/textarea/index.js';
20
+ export { Switch } from './components/admin-bar/ui/switch/index.js';
21
+ export { Checkbox } from './components/admin-bar/ui/checkbox/index.js';
22
+ export * as Popover from './components/admin-bar/ui/popover/index.js';
23
+ export * as Tabs from './components/admin-bar/ui/tabs/index.js';
24
+ export * as Sheet from './components/admin-bar/ui/sheet/index.js';
25
+ export { default as CmsMediaPicker } from './components/cms/cms-media-picker.svelte';
package/dist/index.d.ts CHANGED
@@ -14,14 +14,36 @@ export type { CmsNumberProps } from './components/cms/cms-number.svelte';
14
14
  export { default as CmsDateTime } from './components/cms/cms-date-time.svelte';
15
15
  export type { CmsDateTimeMode, CmsDateTimeProps } from './components/cms/cms-date-time.svelte';
16
16
  export { default as CmsLink } from './components/cms/cms-link.svelte';
17
- export type { CmsLinkValue, CmsLinkRenderProps } from './components/cms/cms-link.svelte';
18
- export { default as CmsRepeater } from './components/cms/cms-repeater.svelte';
17
+ export type { CmsLinkRenderProps } from './components/cms/cms-link.svelte';
19
18
  export { default as CmsEntries } from './components/cms/cms-entries.svelte';
19
+ export { default as CmsNav } from './components/cms/cms-nav.svelte';
20
+ export { default as CmsHours } from './components/cms/cms-hours.svelte';
21
+ export { default as CmsContact } from './components/cms/cms-contact.svelte';
22
+ export { default as CmsSocialLinks } from './components/cms/cms-social-links.svelte';
23
+ export { default as CmsCollection } from './components/cms/cms-collection.svelte';
24
+ export { default as CmsTeam } from './components/cms/cms-team.svelte';
25
+ export { default as CmsTestimonials } from './components/cms/cms-testimonials.svelte';
26
+ export { default as CmsPricing } from './components/cms/cms-pricing.svelte';
27
+ export { default as CmsFaq } from './components/cms/cms-faq.svelte';
28
+ export { default as CmsStats } from './components/cms/cms-stats.svelte';
29
+ export { default as CmsSteps } from './components/cms/cms-steps.svelte';
30
+ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
31
+ export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
32
+ export { default as CmsLogos } from './components/cms/cms-logos.svelte';
33
+ export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
34
+ export * from './core/shapes/index.js';
20
35
  export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
21
- export { definePage } from './components/admin-bar/page-config.js';
36
+ export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
37
+ export type { CmsField, CmsFieldInput } from './components/cms/use-cms-field.svelte.js';
38
+ export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asEnum, asItems, asNumber, asOptionalString, asString, asStrings, moveItem, newItemId, setIn, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
39
+ export type { ItemsSpec, Structured, StructuredItem, StructuredSchema, TranslatableField, TranslationOverlay } from './core/structured.js';
40
+ export { registerStructured, getStructured } from './core/structured-registry.js';
41
+ export { toMetaTags } from './core/metadata.js';
42
+ export type { MetaTagsOptions, MetaTagsOutput } from './core/metadata.js';
43
+ export { definePage, DEFAULT_METADATA_SCHEMA, metadataSchemaFor } from './components/admin-bar/page-config.js';
22
44
  export type { CmsPageConfig, CmsCreatablePageConfig, CmsStaticPageConfig, CmsPageField, CmsPageMetadataSchema, MetadataFieldSchema, MetadataPrimitive } from './components/admin-bar/page-config.js';
23
45
  export { cmsStore } from './components/cms/cms-store.svelte.js';
24
46
  export { cms } from './components/cms/cms-store.svelte.js';
25
47
  export { CMS_SCOPE, getCmsScope, getCmsValue, getPageScope } from './components/cms/scope.js';
26
48
  export { installCmsScope } from './components/cms/install-scope.svelte.js';
27
- export type { CmsEntry, CmsManifest, CmsManifestRoute, CmsManifestScope, CmsPayload, CmsScope, CmsScopeEntry } from './components/cms/scope.js';
49
+ export type { CmsEntry, CmsManifest, CmsManifestRoute, CmsManifestScope, CmsManifestUsage, CmsPayload, CmsScope, CmsScopeEntry, SiteFieldSchema, SiteFieldType, SiteSchema } from './components/cms/scope.js';
package/dist/index.js CHANGED
@@ -13,12 +13,41 @@ export { default as CmsBoolean } from './components/cms/cms-boolean.svelte';
13
13
  export { default as CmsNumber } from './components/cms/cms-number.svelte';
14
14
  export { default as CmsDateTime } from './components/cms/cms-date-time.svelte';
15
15
  export { default as CmsLink } from './components/cms/cms-link.svelte';
16
- export { default as CmsRepeater } from './components/cms/cms-repeater.svelte';
17
16
  export { default as CmsEntries } from './components/cms/cms-entries.svelte';
17
+ // Structured editor components. Each reads one versioned value, opens a
18
+ // popover editor in edit mode and hands the typed view to `children`.
19
+ // Their value types, normalizers and JSON-LD helpers come from the shapes.
20
+ export { default as CmsNav } from './components/cms/cms-nav.svelte';
21
+ export { default as CmsHours } from './components/cms/cms-hours.svelte';
22
+ export { default as CmsContact } from './components/cms/cms-contact.svelte';
23
+ export { default as CmsSocialLinks } from './components/cms/cms-social-links.svelte';
24
+ export { default as CmsCollection } from './components/cms/cms-collection.svelte';
25
+ export { default as CmsTeam } from './components/cms/cms-team.svelte';
26
+ export { default as CmsTestimonials } from './components/cms/cms-testimonials.svelte';
27
+ export { default as CmsPricing } from './components/cms/cms-pricing.svelte';
28
+ export { default as CmsFaq } from './components/cms/cms-faq.svelte';
29
+ // List presets: fixed item shapes; a new shape is a new preset here.
30
+ export { default as CmsStats } from './components/cms/cms-stats.svelte';
31
+ export { default as CmsSteps } from './components/cms/cms-steps.svelte';
32
+ export { default as CmsTimeline } from './components/cms/cms-timeline.svelte';
33
+ export { default as CmsGallery } from './components/cms/cms-gallery.svelte';
34
+ export { default as CmsLogos } from './components/cms/cms-logos.svelte';
35
+ export { default as CmsSchedule } from './components/cms/cms-schedule.svelte';
36
+ export * from './core/shapes/index.js';
18
37
  // Admin bar (sync wrapper; admin-bar-internal + seo-panel are async chunks)
19
38
  export { default as AdminBar } from './components/admin-bar/admin-bar.svelte';
39
+ // Building blocks for structured components: the field hook, the value
40
+ // rules (versioned shapes, `$t` overlay) and the registry the Locales panel
41
+ // reads. The editor popover and field inputs are admin-bar chunks, imported
42
+ // lazily by editable siblings:
43
+ // `import('@velastack/cms/editor')`
44
+ export { useCmsField, resolveScopeRef, ROOT_SCOPE_ID } from './components/cms/use-cms-field.svelte.js';
45
+ export { defineStructured, applyTranslations, countTranslations, extractTranslations, stripTranslations, translatableFields, translationPath, asBoolean, asEnum, asItems, asNumber, asOptionalString, asString, asStrings, moveItem, newItemId, setIn, ROOT_ID, TRANSLATIONS_KEY } from './core/structured.js';
46
+ export { registerStructured, getStructured } from './core/structured-registry.js';
47
+ // Page metadata → svelte-meta-tags props.
48
+ export { toMetaTags } from './core/metadata.js';
20
49
  // Per-route page configuration — declare in `page.cms.ts` next to `+page.svelte`.
21
- export { definePage } from './components/admin-bar/page-config.js';
50
+ export { definePage, DEFAULT_METADATA_SCHEMA, metadataSchemaFor } from './components/admin-bar/page-config.js';
22
51
  // Edit-mode store
23
52
  export { cmsStore } from './components/cms/cms-store.svelte.js';
24
53
  // Merged CMS read view (server payload + client overlay), shaped like CmsPayload
@@ -61,6 +61,7 @@ export const resolveCmsPayload = async (args) => {
61
61
  routeId: scope.routeId,
62
62
  params: scopeParams,
63
63
  fields: scope.fields,
64
+ ...(scope.usages ? { usages: scope.usages } : {}),
64
65
  locale: forLocale
65
66
  };
66
67
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@velastack/cms",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A scope-aware CMS for SvelteKit, for static or dynamic sites.",
5
5
  "keywords": [
6
6
  "svelte",
@@ -57,6 +57,11 @@
57
57
  "svelte": "./dist/index.js",
58
58
  "default": "./dist/index.js"
59
59
  },
60
+ "./editor": {
61
+ "types": "./dist/editor.d.ts",
62
+ "svelte": "./dist/editor.js",
63
+ "default": "./dist/editor.js"
64
+ },
60
65
  "./server": {
61
66
  "types": "./dist/server/index.d.ts",
62
67
  "default": "./dist/server/index.js"
@@ -72,6 +77,10 @@
72
77
  "./adapter": {
73
78
  "types": "./plugin/dist/adapter.d.ts",
74
79
  "default": "./plugin/dist/adapter.js"
80
+ },
81
+ "./build": {
82
+ "types": "./plugin/dist/build.d.ts",
83
+ "default": "./plugin/dist/build.js"
75
84
  }
76
85
  },
77
86
  "peerDependencies": {
@@ -110,7 +119,7 @@
110
119
  "tw-animate-css": "^1.4.0",
111
120
  "typescript": "^6.0.3",
112
121
  "vite": "^8.2.2",
113
- "vitest": "^4.1.11"
122
+ "vitest": "^5.0.1"
114
123
  },
115
124
  "dependencies": {
116
125
  "@sveltejs/adapter-static": "^3.0.10",
@@ -119,7 +128,7 @@
119
128
  "@tiptap/pm": "^3.30.6",
120
129
  "@tiptap/starter-kit": "^3.30.6",
121
130
  "bits-ui": "^2.19.0",
122
- "devalue": "^5.9.2",
131
+ "devalue": "^6.0.0",
123
132
  "tailwind-merge": "^3.6.0"
124
133
  }
125
134
  }