nebula-cms 0.1.3 → 0.1.5

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 (381) hide show
  1. package/README.md +3 -1
  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/{src → dist}/client/Admin.svelte +20 -11
  6. package/dist/client/Admin.svelte.d.ts +11 -0
  7. package/dist/client/Admin.svelte.d.ts.map +1 -0
  8. package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
  9. package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
  10. package/dist/client/components/DraftChip.svelte.d.ts +10 -0
  11. package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
  12. package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
  13. package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
  14. package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
  15. package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
  16. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
  17. package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
  18. package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +1 -2
  19. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
  20. package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
  21. package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
  22. package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
  23. package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
  24. package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
  25. package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
  26. package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
  27. package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
  28. package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
  29. package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
  30. package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
  31. package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
  32. package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
  33. package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
  34. package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
  35. package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
  36. package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
  37. package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
  38. package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
  39. package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
  40. package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
  41. package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
  42. package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
  43. package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
  44. package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
  45. package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
  46. package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
  47. package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
  48. package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
  49. package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
  50. package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
  51. package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +2 -4
  52. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
  53. package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
  54. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
  55. package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
  56. package/dist/client/css/icons.css +29 -0
  57. package/dist/client/index.d.ts +2 -0
  58. package/dist/client/index.d.ts.map +1 -0
  59. package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
  60. package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
  61. package/dist/client/js/drafts/merge.svelte.js +106 -0
  62. package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
  63. package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
  64. package/dist/client/js/drafts/ops.svelte.js +182 -0
  65. package/dist/client/js/drafts/storage.d.ts +45 -0
  66. package/dist/client/js/drafts/storage.d.ts.map +1 -0
  67. package/dist/client/js/drafts/storage.js +76 -0
  68. package/dist/client/js/drafts/workers/diff.d.ts +2 -0
  69. package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
  70. package/dist/client/js/drafts/workers/diff.js +20 -0
  71. package/dist/client/js/editor/editor.svelte.d.ts +124 -0
  72. package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
  73. package/dist/client/js/editor/editor.svelte.js +294 -0
  74. package/dist/client/js/editor/languages.d.ts +11 -0
  75. package/dist/client/js/editor/languages.d.ts.map +1 -0
  76. package/dist/client/js/editor/languages.js +93 -0
  77. package/dist/client/js/editor/link-wrap.d.ts +6 -0
  78. package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
  79. package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
  80. package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
  81. package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
  82. package/dist/client/js/editor/markdown-shortcuts.js +219 -0
  83. package/dist/client/js/handlers/admin.d.ts +64 -0
  84. package/dist/client/js/handlers/admin.d.ts.map +1 -0
  85. package/dist/client/js/handlers/admin.js +186 -0
  86. package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
  87. package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
  88. package/dist/client/js/state/dialogs.svelte.js +28 -0
  89. package/dist/client/js/state/router.svelte.d.ts +44 -0
  90. package/dist/client/js/state/router.svelte.d.ts.map +1 -0
  91. package/dist/client/js/state/router.svelte.js +141 -0
  92. package/dist/client/js/state/schema.svelte.d.ts +51 -0
  93. package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
  94. package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
  95. package/dist/client/js/state/state.svelte.d.ts +68 -0
  96. package/dist/client/js/state/state.svelte.d.ts.map +1 -0
  97. package/dist/client/js/state/state.svelte.js +300 -0
  98. package/dist/client/js/state/theme.svelte.d.ts +24 -0
  99. package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
  100. package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
  101. package/dist/client/js/storage/adapter.d.ts +130 -0
  102. package/dist/client/js/storage/adapter.d.ts.map +1 -0
  103. package/dist/client/js/storage/adapter.js +5 -0
  104. package/dist/client/js/storage/client.d.ts +72 -0
  105. package/dist/client/js/storage/client.d.ts.map +1 -0
  106. package/dist/client/js/storage/client.js +121 -0
  107. package/dist/client/js/storage/db.d.ts +8 -0
  108. package/dist/client/js/storage/db.d.ts.map +1 -0
  109. package/dist/client/js/storage/db.js +35 -0
  110. package/dist/client/js/storage/fsa.d.ts +51 -0
  111. package/dist/client/js/storage/fsa.d.ts.map +1 -0
  112. package/dist/client/js/storage/fsa.js +91 -0
  113. package/dist/client/js/storage/github.d.ts +62 -0
  114. package/dist/client/js/storage/github.d.ts.map +1 -0
  115. package/dist/client/js/storage/github.js +216 -0
  116. package/dist/client/js/storage/storage.d.ts +32 -0
  117. package/dist/client/js/storage/storage.d.ts.map +1 -0
  118. package/dist/client/js/storage/storage.js +68 -0
  119. package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
  120. package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
  121. package/dist/client/js/storage/workers/frontmatter.js +253 -0
  122. package/dist/client/js/storage/workers/storage.d.ts +2 -0
  123. package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
  124. package/dist/client/js/storage/workers/storage.js +167 -0
  125. package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
  126. package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
  127. package/dist/client/js/storage/workers/toml-parser.js +75 -0
  128. package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
  129. package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
  130. package/dist/client/js/storage/workers/yaml-parser.js +100 -0
  131. package/dist/client/js/utils/file-types.d.ts +58 -0
  132. package/dist/client/js/utils/file-types.d.ts.map +1 -0
  133. package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
  134. package/dist/client/js/utils/format.d.ts +8 -0
  135. package/dist/client/js/utils/format.d.ts.map +1 -0
  136. package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
  137. package/dist/client/js/utils/frontmatter.d.ts +12 -0
  138. package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
  139. package/dist/client/js/utils/frontmatter.js +29 -0
  140. package/dist/client/js/utils/schema-utils.d.ts +110 -0
  141. package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
  142. package/dist/client/js/utils/schema-utils.js +242 -0
  143. package/dist/client/js/utils/slug.d.ts +8 -0
  144. package/dist/client/js/utils/slug.d.ts.map +1 -0
  145. package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
  146. package/dist/client/js/utils/sort.d.ts +41 -0
  147. package/dist/client/js/utils/sort.d.ts.map +1 -0
  148. package/dist/client/js/utils/sort.js +65 -0
  149. package/dist/client/js/utils/stable-stringify.d.ts +8 -0
  150. package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
  151. package/dist/client/js/utils/stable-stringify.js +23 -0
  152. package/dist/client/js/utils/url-utils.d.ts +11 -0
  153. package/dist/client/js/utils/url-utils.d.ts.map +1 -0
  154. package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
  155. package/dist/client/types/browser-apis.d.ts +39 -0
  156. package/dist/types.d.ts +22 -0
  157. package/dist/types.d.ts.map +1 -0
  158. package/dist/types.js +1 -0
  159. package/package.json +13 -3
  160. package/.github/workflows/ci.yml +0 -27
  161. package/.github/workflows/publish.yml +0 -34
  162. package/.mcp.json +0 -12
  163. package/.prettierignore +0 -5
  164. package/.prettierrc.cjs +0 -22
  165. package/AGENTS.md +0 -183
  166. package/playground/astro.config.mjs +0 -7
  167. package/playground/node_modules/.bin/astro +0 -21
  168. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
  169. package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
  170. package/playground/node_modules/.vite/deps/_metadata.json +0 -184
  171. package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
  172. package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
  173. package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
  174. package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
  175. package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
  176. package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
  177. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
  178. package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
  179. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
  180. package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
  181. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
  182. package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
  183. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
  184. package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
  185. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
  186. package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
  187. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
  188. package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
  189. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
  190. package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
  191. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
  192. package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
  193. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
  194. package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
  195. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
  196. package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
  197. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
  198. package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
  199. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
  200. package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
  201. package/playground/node_modules/.vite/deps/package.json +0 -3
  202. package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
  203. package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
  204. package/playground/node_modules/.vite/deps/svelte.js +0 -55
  205. package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
  206. package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
  207. package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
  208. package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
  209. package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
  210. package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
  211. package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
  212. package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
  213. package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
  214. package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
  215. package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
  216. package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
  217. package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
  218. package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
  219. package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
  220. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
  221. package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
  222. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
  223. package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
  224. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
  225. package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
  226. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
  227. package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
  228. package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
  229. package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
  230. package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
  231. package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
  232. package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
  233. package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
  234. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
  235. package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
  236. package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
  237. package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
  238. package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
  239. package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
  240. package/playground/package.json +0 -16
  241. package/playground/pnpm-lock.yaml +0 -3167
  242. package/playground/src/content/authors/jane-doe.json +0 -8
  243. package/playground/src/content/config/build.toml +0 -2
  244. package/playground/src/content/courses/web-fundamentals.json +0 -29
  245. package/playground/src/content/docs/advanced.mdx +0 -6
  246. package/playground/src/content/docs/intro.md +0 -6
  247. package/playground/src/content/guides/getting-started.mdx +0 -6
  248. package/playground/src/content/posts/hello-world.md +0 -7
  249. package/playground/src/content/products/t-shirt.json +0 -16
  250. package/playground/src/content/recipes/pancakes.mdoc +0 -8
  251. package/playground/src/content/settings/site.yml +0 -2
  252. package/playground/src/content.config.ts +0 -198
  253. package/playground/src/env.d.ts +0 -1
  254. package/playground/src/pages/index.astro +0 -11
  255. package/playground/src/pages/nebula.astro +0 -14
  256. package/pnpm-workspace.yaml +0 -2
  257. package/scripts/subset-icons.mjs +0 -178
  258. package/src/astro/index.ts +0 -295
  259. package/src/client/js/drafts/merge.svelte.ts +0 -121
  260. package/src/client/js/drafts/ops.svelte.ts +0 -227
  261. package/src/client/js/drafts/storage.ts +0 -108
  262. package/src/client/js/drafts/workers/diff.ts +0 -40
  263. package/src/client/js/editor/editor.svelte.ts +0 -343
  264. package/src/client/js/editor/languages.ts +0 -98
  265. package/src/client/js/editor/markdown-shortcuts.ts +0 -261
  266. package/src/client/js/handlers/admin.ts +0 -246
  267. package/src/client/js/state/dialogs.svelte.ts +0 -35
  268. package/src/client/js/state/router.svelte.ts +0 -156
  269. package/src/client/js/state/state.svelte.ts +0 -334
  270. package/src/client/js/storage/adapter.ts +0 -102
  271. package/src/client/js/storage/client.ts +0 -150
  272. package/src/client/js/storage/db.ts +0 -36
  273. package/src/client/js/storage/fsa.ts +0 -110
  274. package/src/client/js/storage/github.ts +0 -297
  275. package/src/client/js/storage/storage.ts +0 -83
  276. package/src/client/js/storage/workers/frontmatter.ts +0 -320
  277. package/src/client/js/storage/workers/storage.ts +0 -177
  278. package/src/client/js/storage/workers/toml-parser.ts +0 -106
  279. package/src/client/js/storage/workers/yaml-parser.ts +0 -132
  280. package/src/client/js/utils/frontmatter.ts +0 -38
  281. package/src/client/js/utils/schema-utils.ts +0 -295
  282. package/src/client/js/utils/sort.ts +0 -84
  283. package/src/client/js/utils/stable-stringify.ts +0 -27
  284. package/src/types.ts +0 -25
  285. package/svelte.config.js +0 -4
  286. package/tests/astro/build.test.ts +0 -63
  287. package/tests/astro/index.test.ts +0 -689
  288. package/tests/client/components/Admin.test.ts +0 -446
  289. package/tests/client/components/BackendPicker.test.ts +0 -239
  290. package/tests/client/components/DraftChip.test.ts +0 -53
  291. package/tests/client/components/MetadataForm.test.ts +0 -164
  292. package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
  293. package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
  294. package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
  295. package/tests/client/components/editor/EditorPane.test.ts +0 -100
  296. package/tests/client/components/editor/EditorTabs.test.ts +0 -253
  297. package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
  298. package/tests/client/components/editor/fixtures.ts +0 -31
  299. package/tests/client/components/fields/ArrayField.test.ts +0 -197
  300. package/tests/client/components/fields/BooleanField.test.ts +0 -206
  301. package/tests/client/components/fields/DateField.test.ts +0 -210
  302. package/tests/client/components/fields/EnumField.test.ts +0 -246
  303. package/tests/client/components/fields/NumberField.test.ts +0 -240
  304. package/tests/client/components/fields/ObjectField.test.ts +0 -157
  305. package/tests/client/components/fields/SchemaField.test.ts +0 -190
  306. package/tests/client/components/fields/StringField.test.ts +0 -223
  307. package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
  308. package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
  309. package/tests/client/components/sidebar/sort-mock.ts +0 -23
  310. package/tests/client/js/drafts/fixtures.ts +0 -22
  311. package/tests/client/js/drafts/merge.test.ts +0 -282
  312. package/tests/client/js/drafts/ops.test.ts +0 -658
  313. package/tests/client/js/drafts/storage.test.ts +0 -200
  314. package/tests/client/js/drafts/workers/diff.test.ts +0 -165
  315. package/tests/client/js/editor/editor.test.ts +0 -616
  316. package/tests/client/js/editor/link-wrap.test.ts +0 -225
  317. package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
  318. package/tests/client/js/handlers/admin.test.ts +0 -467
  319. package/tests/client/js/state/router.test.ts +0 -619
  320. package/tests/client/js/state/schema.test.ts +0 -266
  321. package/tests/client/js/state/state.test.ts +0 -328
  322. package/tests/client/js/storage/adapter.test.ts +0 -115
  323. package/tests/client/js/storage/client.test.ts +0 -250
  324. package/tests/client/js/storage/db.test.ts +0 -59
  325. package/tests/client/js/storage/fsa.test.ts +0 -284
  326. package/tests/client/js/storage/github.test.ts +0 -349
  327. package/tests/client/js/storage/mock-port.ts +0 -95
  328. package/tests/client/js/storage/storage.test.ts +0 -77
  329. package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
  330. package/tests/client/js/storage/workers/storage.test.ts +0 -299
  331. package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
  332. package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
  333. package/tests/client/js/utils/file-types.test.ts +0 -268
  334. package/tests/client/js/utils/frontmatter.test.ts +0 -87
  335. package/tests/client/js/utils/schema-utils.test.ts +0 -318
  336. package/tests/client/js/utils/slug.test.ts +0 -58
  337. package/tests/client/js/utils/sort.test.ts +0 -276
  338. package/tests/client/js/utils/stable-stringify.test.ts +0 -68
  339. package/tests/client/js/utils/url-utils.test.ts +0 -70
  340. package/tests/e2e/backend-connection.test.ts +0 -301
  341. package/tests/e2e/draft-lifecycle.test.ts +0 -388
  342. package/tests/e2e/editing.test.ts +0 -355
  343. package/tests/e2e/github-adapter.test.ts +0 -330
  344. package/tests/e2e/helpers/mock-adapter.ts +0 -166
  345. package/tests/e2e/helpers/test-app.ts +0 -155
  346. package/tests/e2e/navigation.test.ts +0 -358
  347. package/tests/e2e/publishing.test.ts +0 -345
  348. package/tests/e2e/unsaved-changes.test.ts +0 -317
  349. package/tests/setup.ts +0 -2
  350. package/tests/stubs/codemirror.ts +0 -197
  351. package/tsconfig.json +0 -19
  352. package/vitest.config.ts +0 -178
  353. /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
  354. /package/{src → dist}/client/components/DraftChip.svelte +0 -0
  355. /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
  356. /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
  357. /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
  358. /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
  359. /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
  360. /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
  361. /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
  362. /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
  363. /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
  364. /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
  365. /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
  366. /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
  367. /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
  368. /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
  369. /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
  370. /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
  371. /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
  372. /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
  373. /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
  374. /package/{src → dist}/client/css/a11y.css +0 -0
  375. /package/{src → dist}/client/css/btn.css +0 -0
  376. /package/{src → dist}/client/css/dialog.css +0 -0
  377. /package/{src → dist}/client/css/field-input.css +0 -0
  378. /package/{src → dist}/client/css/reset.css +0 -0
  379. /package/{src → dist}/client/css/theme.css +0 -0
  380. /package/{src/client/index.ts → dist/client/index.js} +0 -0
  381. /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
- });