nebula-cms 0.1.0 → 0.1.4

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 (386) hide show
  1. package/README.md +2 -0
  2. package/dist/astro/index.d.ts +43 -0
  3. package/dist/astro/index.d.ts.map +1 -0
  4. package/dist/astro/index.js +223 -0
  5. package/dist/client/Admin.svelte.d.ts +11 -0
  6. package/dist/client/Admin.svelte.d.ts.map +1 -0
  7. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  9. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  11. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  13. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  15. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  17. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  18. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  19. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  20. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  22. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  24. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  28. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  29. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  30. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  32. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  34. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  38. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  40. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  42. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  50. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  51. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  52. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  53. package/dist/client/css/icons.css +29 -0
  54. package/dist/client/index.d.ts +2 -0
  55. package/dist/client/index.d.ts.map +1 -0
  56. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  57. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  58. package/dist/client/js/drafts/merge.svelte.js +106 -0
  59. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  60. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/ops.svelte.js +182 -0
  62. package/dist/client/js/drafts/storage.d.ts +45 -0
  63. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  64. package/dist/client/js/drafts/storage.js +76 -0
  65. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  66. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  67. package/dist/client/js/drafts/workers/diff.js +20 -0
  68. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  69. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  70. package/dist/client/js/editor/editor.svelte.js +294 -0
  71. package/dist/client/js/editor/languages.d.ts +11 -0
  72. package/dist/client/js/editor/languages.d.ts.map +1 -0
  73. package/dist/client/js/editor/languages.js +93 -0
  74. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  75. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  76. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  77. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  78. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  79. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  80. package/dist/client/js/handlers/admin.d.ts +64 -0
  81. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  82. package/dist/client/js/handlers/admin.js +186 -0
  83. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  84. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  85. package/dist/client/js/state/dialogs.svelte.js +28 -0
  86. package/dist/client/js/state/router.svelte.d.ts +44 -0
  87. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/router.svelte.js +135 -0
  89. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  90. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  91. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  92. package/dist/client/js/state/state.svelte.d.ts +68 -0
  93. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  94. package/dist/client/js/state/state.svelte.js +291 -0
  95. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  96. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  97. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  98. package/dist/client/js/storage/adapter.d.ts +130 -0
  99. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  100. package/dist/client/js/storage/adapter.js +5 -0
  101. package/dist/client/js/storage/client.d.ts +72 -0
  102. package/dist/client/js/storage/client.d.ts.map +1 -0
  103. package/dist/client/js/storage/client.js +121 -0
  104. package/dist/client/js/storage/db.d.ts +8 -0
  105. package/dist/client/js/storage/db.d.ts.map +1 -0
  106. package/dist/client/js/storage/db.js +35 -0
  107. package/dist/client/js/storage/fsa.d.ts +51 -0
  108. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  109. package/dist/client/js/storage/fsa.js +91 -0
  110. package/dist/client/js/storage/github.d.ts +62 -0
  111. package/dist/client/js/storage/github.d.ts.map +1 -0
  112. package/dist/client/js/storage/github.js +216 -0
  113. package/dist/client/js/storage/storage.d.ts +32 -0
  114. package/dist/client/js/storage/storage.d.ts.map +1 -0
  115. package/dist/client/js/storage/storage.js +68 -0
  116. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  117. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  118. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  119. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  120. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/storage.js +167 -0
  122. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  123. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  125. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  128. package/dist/client/js/utils/file-types.d.ts +58 -0
  129. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  130. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  131. package/dist/client/js/utils/format.d.ts +8 -0
  132. package/dist/client/js/utils/format.d.ts.map +1 -0
  133. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  134. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  135. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  136. package/dist/client/js/utils/frontmatter.js +29 -0
  137. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  138. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  139. package/dist/client/js/utils/schema-utils.js +242 -0
  140. package/dist/client/js/utils/slug.d.ts +8 -0
  141. package/dist/client/js/utils/slug.d.ts.map +1 -0
  142. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  143. package/dist/client/js/utils/sort.d.ts +41 -0
  144. package/dist/client/js/utils/sort.d.ts.map +1 -0
  145. package/dist/client/js/utils/sort.js +65 -0
  146. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  147. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  148. package/dist/client/js/utils/stable-stringify.js +23 -0
  149. package/dist/client/js/utils/url-utils.d.ts +11 -0
  150. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  151. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  152. package/dist/types.d.ts +22 -0
  153. package/dist/types.d.ts.map +1 -0
  154. package/dist/types.js +1 -0
  155. package/package.json +8 -1
  156. package/.claude/settings.local.json +0 -42
  157. package/.github/workflows/ci.yml +0 -31
  158. package/.mcp.json +0 -12
  159. package/.prettierignore +0 -5
  160. package/.prettierrc.cjs +0 -22
  161. package/AGENTS.md +0 -183
  162. package/playground/.claude/settings.local.json +0 -5
  163. package/playground/astro.config.mjs +0 -7
  164. package/playground/node_modules/.bin/astro +0 -21
  165. package/playground/node_modules/.bin/rollup +0 -21
  166. package/playground/node_modules/.bin/tsc +0 -21
  167. package/playground/node_modules/.bin/tsserver +0 -21
  168. package/playground/node_modules/.bin/vite +0 -21
  169. package/playground/node_modules/.vite/_svelte_metadata.json +0 -1
  170. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -80
  171. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  172. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  173. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  174. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  176. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  178. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  180. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js +0 -1005
  182. package/playground/node_modules/.vite/deps/chunk-ALJIOON6.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  184. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  186. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  188. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js +0 -204
  190. package/playground/node_modules/.vite/deps/chunk-G3C2FXJT.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js +0 -27
  192. package/playground/node_modules/.vite/deps/chunk-GKDKFWC5.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js +0 -4376
  194. package/playground/node_modules/.vite/deps/chunk-HNCLEOC5.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js +0 -688
  196. package/playground/node_modules/.vite/deps/chunk-JICYXBFU.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js +0 -5099
  198. package/playground/node_modules/.vite/deps/chunk-KCUTL6DD.js.map +0 -7
  199. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  200. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  201. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  202. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  203. package/playground/node_modules/.vite/deps/package.json +0 -3
  204. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  205. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  207. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  209. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  211. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  213. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  215. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  217. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  219. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  221. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  223. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  225. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  227. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  229. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  231. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  233. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  235. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  236. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  237. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  238. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  239. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  240. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  241. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  242. package/playground/package.json +0 -16
  243. package/playground/pnpm-lock.yaml +0 -3167
  244. package/playground/src/content/authors/jane-doe.json +0 -8
  245. package/playground/src/content/config/build.toml +0 -2
  246. package/playground/src/content/courses/web-fundamentals.json +0 -29
  247. package/playground/src/content/docs/advanced.mdx +0 -6
  248. package/playground/src/content/docs/intro.md +0 -6
  249. package/playground/src/content/guides/getting-started.mdx +0 -6
  250. package/playground/src/content/posts/hello-world.md +0 -7
  251. package/playground/src/content/products/t-shirt.json +0 -16
  252. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  253. package/playground/src/content/settings/site.yml +0 -2
  254. package/playground/src/content.config.ts +0 -198
  255. package/playground/src/env.d.ts +0 -1
  256. package/playground/src/pages/index.astro +0 -11
  257. package/playground/src/pages/nebula.astro +0 -14
  258. package/pnpm-workspace.yaml +0 -2
  259. package/scripts/subset-icons.mjs +0 -178
  260. package/src/astro/index.ts +0 -295
  261. package/src/client/js/drafts/merge.svelte.ts +0 -121
  262. package/src/client/js/drafts/ops.svelte.ts +0 -227
  263. package/src/client/js/drafts/storage.ts +0 -108
  264. package/src/client/js/drafts/workers/diff.ts +0 -40
  265. package/src/client/js/editor/editor.svelte.ts +0 -343
  266. package/src/client/js/editor/languages.ts +0 -98
  267. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  268. package/src/client/js/handlers/admin.ts +0 -246
  269. package/src/client/js/state/dialogs.svelte.ts +0 -35
  270. package/src/client/js/state/router.svelte.ts +0 -156
  271. package/src/client/js/state/state.svelte.ts +0 -334
  272. package/src/client/js/storage/adapter.ts +0 -102
  273. package/src/client/js/storage/client.ts +0 -150
  274. package/src/client/js/storage/db.ts +0 -36
  275. package/src/client/js/storage/fsa.ts +0 -110
  276. package/src/client/js/storage/github.ts +0 -297
  277. package/src/client/js/storage/storage.ts +0 -83
  278. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  279. package/src/client/js/storage/workers/storage.ts +0 -177
  280. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  281. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  282. package/src/client/js/utils/frontmatter.ts +0 -38
  283. package/src/client/js/utils/schema-utils.ts +0 -295
  284. package/src/client/js/utils/sort.ts +0 -84
  285. package/src/client/js/utils/stable-stringify.ts +0 -27
  286. package/src/types.ts +0 -25
  287. package/svelte.config.js +0 -4
  288. package/tests/astro/build.test.ts +0 -63
  289. package/tests/astro/index.test.ts +0 -689
  290. package/tests/client/components/Admin.test.ts +0 -446
  291. package/tests/client/components/BackendPicker.test.ts +0 -239
  292. package/tests/client/components/DraftChip.test.ts +0 -53
  293. package/tests/client/components/MetadataForm.test.ts +0 -164
  294. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  295. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  296. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  297. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  298. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  299. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  300. package/tests/client/components/editor/fixtures.ts +0 -31
  301. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  302. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  303. package/tests/client/components/fields/DateField.test.ts +0 -210
  304. package/tests/client/components/fields/EnumField.test.ts +0 -246
  305. package/tests/client/components/fields/NumberField.test.ts +0 -240
  306. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  307. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  308. package/tests/client/components/fields/StringField.test.ts +0 -223
  309. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  310. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  311. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  312. package/tests/client/js/drafts/fixtures.ts +0 -22
  313. package/tests/client/js/drafts/merge.test.ts +0 -282
  314. package/tests/client/js/drafts/ops.test.ts +0 -658
  315. package/tests/client/js/drafts/storage.test.ts +0 -200
  316. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  317. package/tests/client/js/editor/editor.test.ts +0 -616
  318. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  319. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  320. package/tests/client/js/handlers/admin.test.ts +0 -467
  321. package/tests/client/js/state/router.test.ts +0 -619
  322. package/tests/client/js/state/schema.test.ts +0 -266
  323. package/tests/client/js/state/state.test.ts +0 -328
  324. package/tests/client/js/storage/adapter.test.ts +0 -115
  325. package/tests/client/js/storage/client.test.ts +0 -250
  326. package/tests/client/js/storage/db.test.ts +0 -59
  327. package/tests/client/js/storage/fsa.test.ts +0 -284
  328. package/tests/client/js/storage/github.test.ts +0 -349
  329. package/tests/client/js/storage/mock-port.ts +0 -95
  330. package/tests/client/js/storage/storage.test.ts +0 -77
  331. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  332. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  333. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  334. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  335. package/tests/client/js/utils/file-types.test.ts +0 -268
  336. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  337. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  338. package/tests/client/js/utils/slug.test.ts +0 -58
  339. package/tests/client/js/utils/sort.test.ts +0 -276
  340. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  341. package/tests/client/js/utils/url-utils.test.ts +0 -70
  342. package/tests/e2e/backend-connection.test.ts +0 -301
  343. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  344. package/tests/e2e/editing.test.ts +0 -355
  345. package/tests/e2e/github-adapter.test.ts +0 -330
  346. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  347. package/tests/e2e/helpers/test-app.ts +0 -155
  348. package/tests/e2e/navigation.test.ts +0 -358
  349. package/tests/e2e/publishing.test.ts +0 -345
  350. package/tests/e2e/unsaved-changes.test.ts +0 -317
  351. package/tests/setup.ts +0 -2
  352. package/tests/stubs/codemirror.ts +0 -197
  353. package/tsconfig.json +0 -19
  354. package/vitest.config.ts +0 -178
  355. /package/{src → dist}/client/Admin.svelte +0 -0
  356. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  357. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  358. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  359. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  360. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  361. /package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +0 -0
  362. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  363. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  364. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  365. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  366. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  371. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  372. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  373. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  374. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  375. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  376. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  377. /package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +0 -0
  378. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  379. /package/{src → dist}/client/css/a11y.css +0 -0
  380. /package/{src → dist}/client/css/btn.css +0 -0
  381. /package/{src → dist}/client/css/dialog.css +0 -0
  382. /package/{src → dist}/client/css/field-input.css +0 -0
  383. /package/{src → dist}/client/css/reset.css +0 -0
  384. /package/{src → dist}/client/css/theme.css +0 -0
  385. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  386. /package/{src → dist}/virtual.d.ts +0 -0
@@ -1,268 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- getExtensionsForSchema,
4
- hasBodyEditor,
5
- getFileCategory,
6
- getDataFormat,
7
- stripExtension,
8
- getDefaultExtension,
9
- } from '../../../../src/client/js/utils/file-types';
10
-
11
- //////////////////////////////
12
- // getExtensionsForSchema
13
- //////////////////////////////
14
-
15
- describe('getExtensionsForSchema', () => {
16
- it('returns all extensions for a single type identifier', () => {
17
- expect(getExtensionsForSchema({ files: ['md'] })).toEqual([
18
- '.md',
19
- '.markdown',
20
- ]);
21
- });
22
-
23
- it('returns extensions for multiple type identifiers combined', () => {
24
- expect(getExtensionsForSchema({ files: ['md', 'json'] })).toEqual([
25
- '.md',
26
- '.markdown',
27
- '.json',
28
- ]);
29
- });
30
-
31
- it('returns extensions for all frontmatter types', () => {
32
- expect(getExtensionsForSchema({ files: ['md', 'mdx', 'markdoc'] })).toEqual(
33
- ['.md', '.markdown', '.mdx', '.mdoc', '.markdoc'],
34
- );
35
- });
36
-
37
- it('returns extensions for all data types', () => {
38
- expect(getExtensionsForSchema({ files: ['json', 'yaml', 'toml'] })).toEqual(
39
- ['.json', '.yml', '.yaml', '.toml'],
40
- );
41
- });
42
-
43
- it('returns an empty array when files is empty', () => {
44
- expect(getExtensionsForSchema({ files: [] })).toEqual([]);
45
- });
46
-
47
- it('skips unknown type identifiers without throwing', () => {
48
- expect(getExtensionsForSchema({ files: ['md', 'unknown-type'] })).toEqual([
49
- '.md',
50
- '.markdown',
51
- ]);
52
- });
53
-
54
- it('returns an empty array when files property is missing', () => {
55
- expect(getExtensionsForSchema({})).toEqual([]);
56
- });
57
- });
58
-
59
- //////////////////////////////
60
- // hasBodyEditor
61
- //////////////////////////////
62
-
63
- describe('hasBodyEditor', () => {
64
- it('returns true for .md files', () => {
65
- expect(hasBodyEditor('post.md')).toBe(true);
66
- });
67
-
68
- it('returns true for .markdown files', () => {
69
- expect(hasBodyEditor('post.markdown')).toBe(true);
70
- });
71
-
72
- it('returns true for .mdx files', () => {
73
- expect(hasBodyEditor('component.mdx')).toBe(true);
74
- });
75
-
76
- it('returns true for .mdoc files', () => {
77
- expect(hasBodyEditor('doc.mdoc')).toBe(true);
78
- });
79
-
80
- it('returns true for .markdoc files', () => {
81
- expect(hasBodyEditor('doc.markdoc')).toBe(true);
82
- });
83
-
84
- it('returns false for .json files', () => {
85
- expect(hasBodyEditor('data.json')).toBe(false);
86
- });
87
-
88
- it('returns false for .yml files', () => {
89
- expect(hasBodyEditor('config.yml')).toBe(false);
90
- });
91
-
92
- it('returns false for .yaml files', () => {
93
- expect(hasBodyEditor('config.yaml')).toBe(false);
94
- });
95
-
96
- it('returns false for .toml files', () => {
97
- expect(hasBodyEditor('config.toml')).toBe(false);
98
- });
99
-
100
- it('returns false for unrecognised extensions', () => {
101
- expect(hasBodyEditor('file.txt')).toBe(false);
102
- });
103
- });
104
-
105
- //////////////////////////////
106
- // getFileCategory
107
- //////////////////////////////
108
-
109
- describe('getFileCategory', () => {
110
- it('returns frontmatter for .md files', () => {
111
- expect(getFileCategory('post.md')).toBe('frontmatter');
112
- });
113
-
114
- it('returns frontmatter for .markdown files', () => {
115
- expect(getFileCategory('post.markdown')).toBe('frontmatter');
116
- });
117
-
118
- it('returns frontmatter for .mdx files', () => {
119
- expect(getFileCategory('component.mdx')).toBe('frontmatter');
120
- });
121
-
122
- it('returns frontmatter for .mdoc files', () => {
123
- expect(getFileCategory('doc.mdoc')).toBe('frontmatter');
124
- });
125
-
126
- it('returns frontmatter for .markdoc files', () => {
127
- expect(getFileCategory('doc.markdoc')).toBe('frontmatter');
128
- });
129
-
130
- it('returns data for .json files', () => {
131
- expect(getFileCategory('data.json')).toBe('data');
132
- });
133
-
134
- it('returns data for .yml files', () => {
135
- expect(getFileCategory('config.yml')).toBe('data');
136
- });
137
-
138
- it('returns data for .yaml files', () => {
139
- expect(getFileCategory('config.yaml')).toBe('data');
140
- });
141
-
142
- it('returns data for .toml files', () => {
143
- expect(getFileCategory('config.toml')).toBe('data');
144
- });
145
-
146
- it('returns null for unrecognised extensions', () => {
147
- expect(getFileCategory('file.txt')).toBe(null);
148
- });
149
- });
150
-
151
- //////////////////////////////
152
- // getDataFormat
153
- //////////////////////////////
154
-
155
- describe('getDataFormat', () => {
156
- it('returns json for .json files', () => {
157
- expect(getDataFormat('data.json')).toBe('json');
158
- });
159
-
160
- it('returns yaml for .yml files', () => {
161
- expect(getDataFormat('config.yml')).toBe('yaml');
162
- });
163
-
164
- it('returns yaml for .yaml files', () => {
165
- expect(getDataFormat('config.yaml')).toBe('yaml');
166
- });
167
-
168
- it('returns toml for .toml files', () => {
169
- expect(getDataFormat('config.toml')).toBe('toml');
170
- });
171
-
172
- it('returns null for frontmatter files', () => {
173
- expect(getDataFormat('post.md')).toBe(null);
174
- });
175
-
176
- it('returns null for .mdx files', () => {
177
- expect(getDataFormat('component.mdx')).toBe(null);
178
- });
179
-
180
- it('returns null for unrecognised extensions', () => {
181
- expect(getDataFormat('file.txt')).toBe(null);
182
- });
183
- });
184
-
185
- //////////////////////////////
186
- // stripExtension
187
- //////////////////////////////
188
-
189
- describe('stripExtension', () => {
190
- it('strips .md extension', () => {
191
- expect(stripExtension('my-post.md')).toBe('my-post');
192
- });
193
-
194
- it('strips .markdown extension', () => {
195
- expect(stripExtension('my-post.markdown')).toBe('my-post');
196
- });
197
-
198
- it('strips .mdx extension', () => {
199
- expect(stripExtension('component.mdx')).toBe('component');
200
- });
201
-
202
- it('strips .mdoc extension', () => {
203
- expect(stripExtension('doc.mdoc')).toBe('doc');
204
- });
205
-
206
- it('strips .markdoc extension', () => {
207
- expect(stripExtension('doc.markdoc')).toBe('doc');
208
- });
209
-
210
- it('strips .json extension', () => {
211
- expect(stripExtension('data.json')).toBe('data');
212
- });
213
-
214
- it('strips .yml extension', () => {
215
- expect(stripExtension('config.yml')).toBe('config');
216
- });
217
-
218
- it('strips .yaml extension', () => {
219
- expect(stripExtension('config.yaml')).toBe('config');
220
- });
221
-
222
- it('strips .toml extension', () => {
223
- expect(stripExtension('config.toml')).toBe('config');
224
- });
225
-
226
- it('returns filename unchanged for unrecognised extensions', () => {
227
- expect(stripExtension('file.txt')).toBe('file.txt');
228
- });
229
-
230
- it('only strips the last known extension in a double-extension filename', () => {
231
- // Filenames with multiple dots: strip only the recognised trailing extension
232
- expect(stripExtension('my.post.md')).toBe('my.post');
233
- });
234
- });
235
-
236
- //////////////////////////////
237
- // getDefaultExtension
238
- //////////////////////////////
239
-
240
- describe('getDefaultExtension', () => {
241
- it('returns .md as the default extension for md', () => {
242
- expect(getDefaultExtension('md')).toBe('.md');
243
- });
244
-
245
- it('returns .mdx as the default extension for mdx', () => {
246
- expect(getDefaultExtension('mdx')).toBe('.mdx');
247
- });
248
-
249
- it('returns .mdoc as the default extension for markdoc', () => {
250
- expect(getDefaultExtension('markdoc')).toBe('.mdoc');
251
- });
252
-
253
- it('returns .json as the default extension for json', () => {
254
- expect(getDefaultExtension('json')).toBe('.json');
255
- });
256
-
257
- it('returns .yml as the default extension for yaml', () => {
258
- expect(getDefaultExtension('yaml')).toBe('.yml');
259
- });
260
-
261
- it('returns .toml as the default extension for toml', () => {
262
- expect(getDefaultExtension('toml')).toBe('.toml');
263
- });
264
-
265
- it('returns null for unknown type identifiers', () => {
266
- expect(getDefaultExtension('unknown')).toBe(null);
267
- });
268
- });
@@ -1,87 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { splitFrontmatter } from '../../../../src/client/js/utils/frontmatter';
3
-
4
- describe('splitFrontmatter', () => {
5
- it('splits valid YAML frontmatter and body', () => {
6
- const content = '---\ntitle: Hello\n---\nBody text here.';
7
- const result = splitFrontmatter(content);
8
- // slice(4, closeIndex) stops before the \n that precedes ---
9
- expect(result.rawFrontmatter).toBe('title: Hello');
10
- expect(result.body).toBe('Body text here.');
11
- });
12
-
13
- it('returns an empty body when there is only frontmatter', () => {
14
- const content = '---\ntitle: No Body\n---\n';
15
- const result = splitFrontmatter(content);
16
- expect(result.rawFrontmatter).toBe('title: No Body');
17
- expect(result.body).toBe('');
18
- });
19
-
20
- it('handles frontmatter with no trailing newline (ends with ---)', () => {
21
- // endsWith('\n---') branch: slice(4, length - 4) trims the leading newline of the delimiter
22
- const content = '---\ntitle: EOF\n---';
23
- const result = splitFrontmatter(content);
24
- expect(result.rawFrontmatter).toBe('title: EOF');
25
- expect(result.body).toBe('');
26
- });
27
-
28
- it('returns empty frontmatter and full content when no frontmatter delimiter is present', () => {
29
- const content = 'Just plain body text.';
30
- const result = splitFrontmatter(content);
31
- expect(result.rawFrontmatter).toBe('');
32
- expect(result.body).toBe('Just plain body text.');
33
- });
34
-
35
- it('returns empty frontmatter and full content for an empty string', () => {
36
- const result = splitFrontmatter('');
37
- expect(result.rawFrontmatter).toBe('');
38
- expect(result.body).toBe('');
39
- });
40
-
41
- it('rejects a horizontal rule (----) at the start — not treated as frontmatter', () => {
42
- const content = '----\ntitle: Nope\n---\nBody.';
43
- const result = splitFrontmatter(content);
44
- expect(result.rawFrontmatter).toBe('');
45
- expect(result.body).toBe(content);
46
- });
47
-
48
- it('strips a leading BOM character before processing', () => {
49
- const content = '\uFEFF---\ntitle: BOM\n---\nBody.';
50
- const result = splitFrontmatter(content);
51
- expect(result.rawFrontmatter).toBe('title: BOM');
52
- expect(result.body).toBe('Body.');
53
- });
54
-
55
- it('normalises CRLF line endings before processing', () => {
56
- const content = '---\r\ntitle: CRLF\r\n---\r\nBody.';
57
- const result = splitFrontmatter(content);
58
- expect(result.rawFrontmatter).toBe('title: CRLF');
59
- expect(result.body).toBe('Body.');
60
- });
61
-
62
- it('returns empty frontmatter when the closing delimiter is missing', () => {
63
- // No closing --- so the entire content is treated as body
64
- const content = '---\ntitle: Unclosed';
65
- const result = splitFrontmatter(content);
66
- expect(result.rawFrontmatter).toBe('');
67
- expect(result.body).toBe(content);
68
- });
69
-
70
- it('treats content starting with --- but missing a newline as a non-match', () => {
71
- // "---" without an immediate newline is not a valid opening delimiter
72
- const content = '--- title: Inline';
73
- const result = splitFrontmatter(content);
74
- expect(result.rawFrontmatter).toBe('');
75
- expect(result.body).toBe(content);
76
- });
77
-
78
- it('handles multiline frontmatter with body correctly', () => {
79
- const content =
80
- '---\ntitle: Multi\nauthor: Jane\ntags:\n - a\n - b\n---\n# Heading\n\nParagraph.';
81
- const result = splitFrontmatter(content);
82
- expect(result.rawFrontmatter).toBe(
83
- 'title: Multi\nauthor: Jane\ntags:\n - a\n - b',
84
- );
85
- expect(result.body).toBe('# Heading\n\nParagraph.');
86
- });
87
- });
@@ -1,318 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- resolveFieldType,
4
- createDefaultValue,
5
- extractTabs,
6
- getFieldsForTab,
7
- getByPath,
8
- setByPath,
9
- } from '../../../../src/client/js/utils/schema-utils';
10
-
11
- //////////////////////////////
12
- // resolveFieldType
13
- //////////////////////////////
14
-
15
- describe('resolveFieldType', () => {
16
- it('resolves type: string to kind: string', () => {
17
- expect(resolveFieldType({ type: 'string' })).toEqual({ kind: 'string' });
18
- });
19
-
20
- it('resolves type: number to kind: number', () => {
21
- expect(resolveFieldType({ type: 'number' })).toEqual({ kind: 'number' });
22
- });
23
-
24
- it('resolves type: integer to kind: number', () => {
25
- expect(resolveFieldType({ type: 'integer' })).toEqual({ kind: 'number' });
26
- });
27
-
28
- it('resolves type: boolean to kind: boolean', () => {
29
- expect(resolveFieldType({ type: 'boolean' })).toEqual({ kind: 'boolean' });
30
- });
31
-
32
- it('resolves type: array to kind: array', () => {
33
- expect(resolveFieldType({ type: 'array' })).toEqual({ kind: 'array' });
34
- });
35
-
36
- it('resolves type: object to kind: object', () => {
37
- expect(resolveFieldType({ type: 'object' })).toEqual({ kind: 'object' });
38
- });
39
-
40
- it('resolves string with format: date-time to kind: date', () => {
41
- expect(resolveFieldType({ type: 'string', format: 'date-time' })).toEqual({
42
- kind: 'date',
43
- });
44
- });
45
-
46
- it('resolves string with enum values to kind: enum with options', () => {
47
- const result = resolveFieldType({ type: 'string', enum: ['a', 'b', 'c'] });
48
- expect(result).toEqual({ kind: 'enum', options: ['a', 'b', 'c'] });
49
- });
50
-
51
- it('resolves unknown type to kind: unknown', () => {
52
- expect(resolveFieldType({ type: 'bogus' })).toEqual({ kind: 'unknown' });
53
- });
54
-
55
- it('resolves schema with no type to kind: unknown', () => {
56
- expect(resolveFieldType({})).toEqual({ kind: 'unknown' });
57
- });
58
-
59
- it('unwraps nullable anyOf and marks nullable: true', () => {
60
- const schema = {
61
- anyOf: [{ type: 'string' }, { type: 'null' }],
62
- };
63
- expect(resolveFieldType(schema)).toEqual({
64
- kind: 'string',
65
- nullable: true,
66
- });
67
- });
68
-
69
- it('unwraps nullable anyOf for date-time and marks nullable: true', () => {
70
- const schema = {
71
- anyOf: [{ type: 'string', format: 'date-time' }, { type: 'null' }],
72
- };
73
- expect(resolveFieldType(schema)).toEqual({ kind: 'date', nullable: true });
74
- });
75
-
76
- it('date-time format takes precedence over enum when both present', () => {
77
- // format is checked before enum in the implementation
78
- const result = resolveFieldType({
79
- type: 'string',
80
- format: 'date-time',
81
- enum: ['x'],
82
- });
83
- expect(result.kind).toBe('date');
84
- });
85
- });
86
-
87
- //////////////////////////////
88
- // createDefaultValue
89
- //////////////////////////////
90
-
91
- describe('createDefaultValue', () => {
92
- it('returns an explicit schema default when present', () => {
93
- expect(createDefaultValue({ type: 'string', default: 'hello' })).toBe(
94
- 'hello',
95
- );
96
- });
97
-
98
- it('returns an explicit default of false (falsy default honoured)', () => {
99
- expect(createDefaultValue({ type: 'boolean', default: false })).toBe(false);
100
- });
101
-
102
- it('returns empty string for type: string', () => {
103
- expect(createDefaultValue({ type: 'string' })).toBe('');
104
- });
105
-
106
- it('returns 0 for type: number', () => {
107
- expect(createDefaultValue({ type: 'number' })).toBe(0);
108
- });
109
-
110
- it('returns 0 for type: integer', () => {
111
- expect(createDefaultValue({ type: 'integer' })).toBe(0);
112
- });
113
-
114
- it('returns false for type: boolean', () => {
115
- expect(createDefaultValue({ type: 'boolean' })).toBe(false);
116
- });
117
-
118
- it('returns [] for type: array', () => {
119
- expect(createDefaultValue({ type: 'array' })).toEqual([]);
120
- });
121
-
122
- it('returns {} for type: object with no properties', () => {
123
- expect(createDefaultValue({ type: 'object' })).toEqual({});
124
- });
125
-
126
- it('recursively builds defaults for object properties', () => {
127
- const schema = {
128
- type: 'object',
129
- properties: {
130
- name: { type: 'string' },
131
- count: { type: 'number' },
132
- active: { type: 'boolean' },
133
- },
134
- };
135
- expect(createDefaultValue(schema)).toEqual({
136
- name: '',
137
- count: 0,
138
- active: false,
139
- });
140
- });
141
-
142
- it('returns null for a nullable anyOf schema', () => {
143
- const schema = { anyOf: [{ type: 'string' }, { type: 'null' }] };
144
- expect(createDefaultValue(schema)).toBeNull();
145
- });
146
-
147
- it('returns null for an unrecognised type', () => {
148
- expect(createDefaultValue({ type: 'bogus' })).toBeNull();
149
- });
150
-
151
- it('returns null for an empty schema', () => {
152
- expect(createDefaultValue({})).toBeNull();
153
- });
154
- });
155
-
156
- //////////////////////////////
157
- // extractTabs
158
- //////////////////////////////
159
-
160
- describe('extractTabs', () => {
161
- it('returns an empty array when the schema has no properties', () => {
162
- expect(extractTabs({})).toEqual([]);
163
- });
164
-
165
- it('returns an empty array when no fields have a tab array', () => {
166
- const schema = { properties: { title: { type: 'string' } } };
167
- expect(extractTabs(schema)).toEqual([]);
168
- });
169
-
170
- it('returns sorted, deduplicated tab names', () => {
171
- const schema = {
172
- properties: {
173
- title: { type: 'string', tab: ['Content'] },
174
- slug: { type: 'string', tab: ['SEO', 'Content'] },
175
- date: { type: 'string', tab: ['Metadata'] },
176
- },
177
- };
178
- expect(extractTabs(schema)).toEqual(['Content', 'Metadata', 'SEO']);
179
- });
180
-
181
- it('deduplicates tab names appearing across multiple fields', () => {
182
- const schema = {
183
- properties: {
184
- a: { tab: ['Shared'] },
185
- b: { tab: ['Shared'] },
186
- },
187
- };
188
- expect(extractTabs(schema)).toEqual(['Shared']);
189
- });
190
-
191
- it('ignores fields whose tab value is not an array', () => {
192
- const schema = {
193
- properties: {
194
- a: { tab: 'not-an-array' },
195
- b: { tab: ['Real'] },
196
- },
197
- };
198
- expect(extractTabs(schema)).toEqual(['Real']);
199
- });
200
- });
201
-
202
- //////////////////////////////
203
- // getFieldsForTab
204
- //////////////////////////////
205
-
206
- describe('getFieldsForTab', () => {
207
- const schema = {
208
- properties: {
209
- title: { type: 'string', tab: ['Content'] },
210
- slug: { type: 'string', tab: ['SEO'] },
211
- date: { type: 'string', tab: ['Metadata', 'Content'] },
212
- $schema: { type: 'string' },
213
- },
214
- };
215
-
216
- it('returns all field names (excluding $schema) when tab is null', () => {
217
- const result = getFieldsForTab(schema, null);
218
- expect(result).toContain('title');
219
- expect(result).toContain('slug');
220
- expect(result).toContain('date');
221
- expect(result).not.toContain('$schema');
222
- });
223
-
224
- it('returns only fields belonging to the given tab', () => {
225
- expect(getFieldsForTab(schema, 'Content')).toEqual(['title', 'date']);
226
- });
227
-
228
- it('returns the correct subset for SEO tab', () => {
229
- expect(getFieldsForTab(schema, 'SEO')).toEqual(['slug']);
230
- });
231
-
232
- it('returns an empty array for a tab with no matching fields', () => {
233
- expect(getFieldsForTab(schema, 'NonExistent')).toEqual([]);
234
- });
235
-
236
- it('returns an empty array when the schema has no properties', () => {
237
- expect(getFieldsForTab({}, 'Content')).toEqual([]);
238
- });
239
- });
240
-
241
- //////////////////////////////
242
- // getByPath
243
- //////////////////////////////
244
-
245
- describe('getByPath', () => {
246
- it('returns the root object for an empty path', () => {
247
- const obj = { a: 1 };
248
- expect(getByPath(obj, [])).toBe(obj);
249
- });
250
-
251
- it('reads a top-level property by key', () => {
252
- expect(getByPath({ name: 'Alice' }, ['name'])).toBe('Alice');
253
- });
254
-
255
- it('traverses nested objects', () => {
256
- const obj = { a: { b: { c: 42 } } };
257
- expect(getByPath(obj, ['a', 'b', 'c'])).toBe(42);
258
- });
259
-
260
- it('reads array elements by numeric index', () => {
261
- const obj = { items: ['x', 'y', 'z'] };
262
- expect(getByPath(obj, ['items', 1])).toBe('y');
263
- });
264
-
265
- it('returns undefined when a segment is missing', () => {
266
- expect(getByPath({ a: 1 }, ['b', 'c'])).toBeUndefined();
267
- });
268
-
269
- it('returns undefined when traversing through null', () => {
270
- expect(getByPath({ a: null }, ['a', 'b'])).toBeUndefined();
271
- });
272
-
273
- it('returns undefined when traversing through undefined', () => {
274
- expect(getByPath({ a: undefined }, ['a', 'b'])).toBeUndefined();
275
- });
276
- });
277
-
278
- //////////////////////////////
279
- // setByPath
280
- //////////////////////////////
281
-
282
- describe('setByPath', () => {
283
- it('sets a top-level property', () => {
284
- const obj: Record<string, unknown> = {};
285
- setByPath(obj, ['name'], 'Alice');
286
- expect(obj['name']).toBe('Alice');
287
- });
288
-
289
- it('sets a deeply nested property, creating intermediates', () => {
290
- const obj: Record<string, unknown> = {};
291
- setByPath(obj, ['a', 'b', 'c'], 99);
292
- expect((obj['a'] as Record<string, unknown>)['b']).toEqual({ c: 99 });
293
- });
294
-
295
- it('overwrites an existing value', () => {
296
- const obj = { score: 1 };
297
- setByPath(obj, ['score'], 100);
298
- expect(obj['score']).toBe(100);
299
- });
300
-
301
- it('sets a value at a numeric array index', () => {
302
- const obj: Record<string, unknown> = { items: ['a', 'b', 'c'] };
303
- setByPath(obj, ['items', 1], 'X');
304
- expect((obj['items'] as string[])[1]).toBe('X');
305
- });
306
-
307
- it('does nothing for an empty path', () => {
308
- const obj = { a: 1 };
309
- setByPath(obj, [], 'should-not-apply');
310
- expect(obj).toEqual({ a: 1 });
311
- });
312
-
313
- it('creates missing intermediate objects', () => {
314
- const obj: Record<string, unknown> = {};
315
- setByPath(obj, ['x', 'y'], 'deep');
316
- expect(obj).toEqual({ x: { y: 'deep' } });
317
- });
318
- });