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