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,619 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
-
3
- /*
4
- //////////////////////////////
5
- // Router module test strategy
6
- //
7
- // The router module reads location.pathname at import time to set the initial
8
- // $state, and tracks an `initialized` flag at module scope. To test different
9
- // initial routes and to reset that flag between test groups we must use
10
- // vi.resetModules() + a dynamic import for each describe block that needs a
11
- // fresh module instance. Globals (location, navigation, window) are stubbed
12
- // before each dynamic import so the module picks them up on load.
13
- //////////////////////////////
14
- */
15
-
16
- // Hoisted mock for the virtual config module — lets tests override basePath
17
- const { mockBasePath } = vi.hoisted(() => ({
18
- mockBasePath: vi.fn(() => '/admin'),
19
- }));
20
-
21
- vi.mock('virtual:nebula/config', () => ({
22
- default: {
23
- get basePath() {
24
- return mockBasePath();
25
- },
26
- collectionsPath: '/collections',
27
- },
28
- }));
29
-
30
- afterEach(() => {
31
- mockBasePath.mockReturnValue('/admin');
32
- });
33
-
34
- // Minimal NavigateEvent shape used by the navigate listener.
35
- interface FakeNavigateEvent {
36
- destination: { url: string };
37
- hashChange: boolean;
38
- downloadRequest: null | string;
39
- canIntercept: boolean;
40
- intercepted: boolean;
41
- interceptHandler: (() => void) | null;
42
- preventDefault: ReturnType<typeof vi.fn>;
43
- intercept: (opts: { handler: () => void }) => void;
44
- }
45
-
46
- /**
47
- * Builds a fake NavigateEvent for use in navigate listener tests.
48
- * @param {string} url - The destination URL string
49
- * @param {Partial<FakeNavigateEvent>} overrides - Optional field overrides
50
- * @return {FakeNavigateEvent} The constructed fake event
51
- */
52
- function makeFakeNavigateEvent(
53
- url: string,
54
- overrides: Partial<FakeNavigateEvent> = {},
55
- ): FakeNavigateEvent {
56
- const event: FakeNavigateEvent = {
57
- destination: { url },
58
- hashChange: false,
59
- downloadRequest: null,
60
- canIntercept: true,
61
- intercepted: false,
62
- interceptHandler: null,
63
- preventDefault: vi.fn(),
64
- intercept(opts) {
65
- this.intercepted = true;
66
- this.interceptHandler = opts.handler;
67
- },
68
- ...overrides,
69
- };
70
- return event;
71
- }
72
-
73
- /**
74
- * Builds a minimal navigation global stub that captures 'navigate' listeners
75
- * and exposes helpers to fire them.
76
- * @return {{ addEventListener: ReturnType<typeof vi.fn>, navigate: ReturnType<typeof vi.fn>, fire: (e: FakeNavigateEvent) => void }} The stub and fire helper
77
- */
78
- function makeNavigationStub() {
79
- const listeners: Array<(e: FakeNavigateEvent) => void> = [];
80
- const stub = {
81
- addEventListener: vi.fn(
82
- (type: string, cb: (e: FakeNavigateEvent) => void) => {
83
- if (type === 'navigate') listeners.push(cb);
84
- },
85
- ),
86
- navigate: vi.fn(),
87
- // Fires the navigate event on all registered listeners.
88
- fire(event: FakeNavigateEvent) {
89
- for (const l of listeners) l(event);
90
- },
91
- };
92
- return stub;
93
- }
94
-
95
- /*
96
- //////////////////////////////
97
- // Route parsing via route export
98
- //////////////////////////////
99
- */
100
-
101
- describe('route — initial route parsing', () => {
102
- afterEach(() => {
103
- vi.unstubAllGlobals();
104
- vi.resetModules();
105
- });
106
-
107
- it('parses the home route for /admin', async () => {
108
- vi.stubGlobal('location', { pathname: '/admin' });
109
- vi.stubGlobal('navigation', makeNavigationStub());
110
- const { nav } =
111
- await import('../../../../src/client/js/state/router.svelte');
112
- expect(nav.route).toEqual({ view: 'home' });
113
- });
114
-
115
- it('parses the home route for /admin/ (trailing slash)', async () => {
116
- vi.stubGlobal('location', { pathname: '/admin/' });
117
- vi.stubGlobal('navigation', makeNavigationStub());
118
- const { nav } =
119
- await import('../../../../src/client/js/state/router.svelte');
120
- expect(nav.route).toEqual({ view: 'home' });
121
- });
122
-
123
- it('parses the collection route for /admin/posts', async () => {
124
- vi.stubGlobal('location', { pathname: '/admin/posts' });
125
- vi.stubGlobal('navigation', makeNavigationStub());
126
- const { nav } =
127
- await import('../../../../src/client/js/state/router.svelte');
128
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
129
- });
130
-
131
- it('parses the file route for /admin/posts/hello-world', async () => {
132
- vi.stubGlobal('location', { pathname: '/admin/posts/hello-world' });
133
- vi.stubGlobal('navigation', makeNavigationStub());
134
- const { nav } =
135
- await import('../../../../src/client/js/state/router.svelte');
136
- expect(nav.route).toEqual({
137
- view: 'file',
138
- collection: 'posts',
139
- slug: 'hello-world',
140
- });
141
- });
142
-
143
- it('parses the draft route for /admin/posts/draft-abc123', async () => {
144
- vi.stubGlobal('location', { pathname: '/admin/posts/draft-abc123' });
145
- vi.stubGlobal('navigation', makeNavigationStub());
146
- const { nav } =
147
- await import('../../../../src/client/js/state/router.svelte');
148
- expect(nav.route).toEqual({
149
- view: 'draft',
150
- collection: 'posts',
151
- draftId: 'abc123',
152
- });
153
- });
154
- });
155
-
156
- /*
157
- //////////////////////////////
158
- // adminPath()
159
- //////////////////////////////
160
- */
161
-
162
- describe('adminPath', () => {
163
- afterEach(() => {
164
- vi.unstubAllGlobals();
165
- vi.resetModules();
166
- });
167
-
168
- it('returns basePath when called with no segments', async () => {
169
- vi.stubGlobal('location', { pathname: '/admin' });
170
- vi.stubGlobal('navigation', makeNavigationStub());
171
- const { adminPath } =
172
- await import('../../../../src/client/js/state/router.svelte');
173
- expect(adminPath()).toBe('/admin');
174
- });
175
-
176
- it('joins segments under default /admin basePath', async () => {
177
- vi.stubGlobal('location', { pathname: '/admin' });
178
- vi.stubGlobal('navigation', makeNavigationStub());
179
- const { adminPath } =
180
- await import('../../../../src/client/js/state/router.svelte');
181
- expect(adminPath('posts')).toBe('/admin/posts');
182
- expect(adminPath('posts', 'hello-world')).toBe('/admin/posts/hello-world');
183
- });
184
-
185
- it('produces single-slash paths when basePath is /', async () => {
186
- mockBasePath.mockReturnValue('/');
187
- vi.stubGlobal('location', { pathname: '/' });
188
- vi.stubGlobal('navigation', makeNavigationStub());
189
- vi.stubGlobal('window', { addEventListener: vi.fn() });
190
- vi.resetModules();
191
- const { adminPath } =
192
- await import('../../../../src/client/js/state/router.svelte');
193
- expect(adminPath('authors')).toBe('/authors');
194
- expect(adminPath('posts', 'my-post')).toBe('/posts/my-post');
195
- expect(adminPath('posts', 'draft-abc')).toBe('/posts/draft-abc');
196
- });
197
-
198
- it('joins segments under custom basePath', async () => {
199
- mockBasePath.mockReturnValue('/cms');
200
- vi.stubGlobal('location', { pathname: '/cms' });
201
- vi.stubGlobal('navigation', makeNavigationStub());
202
- vi.stubGlobal('window', { addEventListener: vi.fn() });
203
- vi.resetModules();
204
- const { adminPath } =
205
- await import('../../../../src/client/js/state/router.svelte');
206
- expect(adminPath('posts')).toBe('/cms/posts');
207
- expect(adminPath('posts', 'hello')).toBe('/cms/posts/hello');
208
- });
209
-
210
- it('joins segments under nested basePath', async () => {
211
- mockBasePath.mockReturnValue('/app/dashboard');
212
- vi.stubGlobal('location', { pathname: '/app/dashboard' });
213
- vi.stubGlobal('navigation', makeNavigationStub());
214
- vi.stubGlobal('window', { addEventListener: vi.fn() });
215
- vi.resetModules();
216
- const { adminPath } =
217
- await import('../../../../src/client/js/state/router.svelte');
218
- expect(adminPath('posts')).toBe('/app/dashboard/posts');
219
- });
220
- });
221
-
222
- /*
223
- //////////////////////////////
224
- // Root basePath (/) — interception and parsing
225
- //////////////////////////////
226
- */
227
-
228
- describe('initRouter with root basePath (/)', () => {
229
- afterEach(() => {
230
- vi.unstubAllGlobals();
231
- vi.resetModules();
232
- });
233
-
234
- it('parses home route for /', async () => {
235
- mockBasePath.mockReturnValue('/');
236
- vi.stubGlobal('location', { pathname: '/' });
237
- vi.stubGlobal('navigation', makeNavigationStub());
238
- vi.stubGlobal('window', { addEventListener: vi.fn() });
239
- vi.resetModules();
240
- const { initRouter, nav } =
241
- await import('../../../../src/client/js/state/router.svelte');
242
- initRouter();
243
- expect(nav.route).toEqual({ view: 'home' });
244
- });
245
-
246
- it('parses collection route for /authors', async () => {
247
- mockBasePath.mockReturnValue('/');
248
- vi.stubGlobal('location', { pathname: '/authors' });
249
- vi.stubGlobal('navigation', makeNavigationStub());
250
- vi.stubGlobal('window', { addEventListener: vi.fn() });
251
- vi.resetModules();
252
- const { initRouter, nav } =
253
- await import('../../../../src/client/js/state/router.svelte');
254
- initRouter();
255
- expect(nav.route).toEqual({ view: 'collection', collection: 'authors' });
256
- });
257
-
258
- it('intercepts navigations under root basePath', async () => {
259
- mockBasePath.mockReturnValue('/');
260
- const navStub = makeNavigationStub();
261
- vi.stubGlobal('location', { pathname: '/' });
262
- vi.stubGlobal('navigation', navStub);
263
- vi.stubGlobal('window', {
264
- addEventListener: vi.fn(),
265
- confirm: vi.fn(() => true),
266
- });
267
- vi.resetModules();
268
- const { initRouter, nav } =
269
- await import('../../../../src/client/js/state/router.svelte');
270
- initRouter();
271
-
272
- const event = makeFakeNavigateEvent('http://localhost/authors');
273
- navStub.fire(event);
274
- expect(event.intercepted).toBe(true);
275
- event.interceptHandler!();
276
- expect(nav.route).toEqual({ view: 'collection', collection: 'authors' });
277
- });
278
-
279
- it('adminPath returns / when basePath is root and no segments given', async () => {
280
- mockBasePath.mockReturnValue('/');
281
- vi.stubGlobal('location', { pathname: '/' });
282
- vi.stubGlobal('navigation', makeNavigationStub());
283
- vi.stubGlobal('window', { addEventListener: vi.fn() });
284
- vi.resetModules();
285
- const { adminPath } =
286
- await import('../../../../src/client/js/state/router.svelte');
287
- expect(adminPath()).toBe('/');
288
- });
289
- });
290
-
291
- /*
292
- //////////////////////////////
293
- // Custom basePath route parsing
294
- //////////////////////////////
295
- */
296
-
297
- describe('initRouter with custom basePath', () => {
298
- afterEach(() => {
299
- vi.unstubAllGlobals();
300
- vi.resetModules();
301
- });
302
-
303
- it('parses home route with custom basePath', async () => {
304
- mockBasePath.mockReturnValue('/cms');
305
- vi.stubGlobal('location', { pathname: '/cms' });
306
- vi.stubGlobal('navigation', makeNavigationStub());
307
- vi.stubGlobal('window', { addEventListener: vi.fn() });
308
- vi.resetModules();
309
- const { initRouter, nav } =
310
- await import('../../../../src/client/js/state/router.svelte');
311
- initRouter();
312
- expect(nav.route).toEqual({ view: 'home' });
313
- });
314
-
315
- it('parses collection route with custom basePath', async () => {
316
- mockBasePath.mockReturnValue('/cms');
317
- vi.stubGlobal('location', { pathname: '/cms/posts' });
318
- vi.stubGlobal('navigation', makeNavigationStub());
319
- vi.stubGlobal('window', { addEventListener: vi.fn() });
320
- vi.resetModules();
321
- const { initRouter, nav } =
322
- await import('../../../../src/client/js/state/router.svelte');
323
- initRouter();
324
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
325
- });
326
-
327
- it('parses file route with custom basePath', async () => {
328
- mockBasePath.mockReturnValue('/cms');
329
- vi.stubGlobal('location', { pathname: '/cms/posts/hello-world' });
330
- vi.stubGlobal('navigation', makeNavigationStub());
331
- vi.stubGlobal('window', { addEventListener: vi.fn() });
332
- vi.resetModules();
333
- const { initRouter, nav } =
334
- await import('../../../../src/client/js/state/router.svelte');
335
- initRouter();
336
- expect(nav.route).toEqual({
337
- view: 'file',
338
- collection: 'posts',
339
- slug: 'hello-world',
340
- });
341
- });
342
-
343
- it('parses draft route with custom basePath', async () => {
344
- mockBasePath.mockReturnValue('/cms');
345
- vi.stubGlobal('location', { pathname: '/cms/posts/draft-abc123' });
346
- vi.stubGlobal('navigation', makeNavigationStub());
347
- vi.stubGlobal('window', { addEventListener: vi.fn() });
348
- vi.resetModules();
349
- const { initRouter, nav } =
350
- await import('../../../../src/client/js/state/router.svelte');
351
- initRouter();
352
- expect(nav.route).toEqual({
353
- view: 'draft',
354
- collection: 'posts',
355
- draftId: 'abc123',
356
- });
357
- });
358
-
359
- it('intercepts navigations under custom basePath', async () => {
360
- mockBasePath.mockReturnValue('/cms');
361
- const navStub = makeNavigationStub();
362
- vi.stubGlobal('location', { pathname: '/cms' });
363
- vi.stubGlobal('navigation', navStub);
364
- vi.stubGlobal('window', {
365
- addEventListener: vi.fn(),
366
- confirm: vi.fn(() => true),
367
- });
368
- vi.resetModules();
369
- const { initRouter, nav } =
370
- await import('../../../../src/client/js/state/router.svelte');
371
- initRouter();
372
-
373
- const event = makeFakeNavigateEvent('http://localhost/cms/posts');
374
- navStub.fire(event);
375
- expect(event.intercepted).toBe(true);
376
- event.interceptHandler!();
377
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
378
- });
379
-
380
- it('does not intercept navigations outside custom basePath', async () => {
381
- mockBasePath.mockReturnValue('/cms');
382
- const navStub = makeNavigationStub();
383
- vi.stubGlobal('location', { pathname: '/cms' });
384
- vi.stubGlobal('navigation', navStub);
385
- vi.stubGlobal('window', { addEventListener: vi.fn() });
386
- vi.resetModules();
387
- const { initRouter, nav } =
388
- await import('../../../../src/client/js/state/router.svelte');
389
- initRouter();
390
-
391
- const event = makeFakeNavigateEvent('http://localhost/admin/posts');
392
- navStub.fire(event);
393
- expect(event.intercepted).toBe(false);
394
- expect(nav.route).toEqual({ view: 'home' });
395
- });
396
-
397
- it('handles nested basePath like /app/dashboard', async () => {
398
- mockBasePath.mockReturnValue('/app/dashboard');
399
- vi.stubGlobal('location', { pathname: '/app/dashboard/posts' });
400
- vi.stubGlobal('navigation', makeNavigationStub());
401
- vi.stubGlobal('window', { addEventListener: vi.fn() });
402
- vi.resetModules();
403
- const { initRouter, nav } =
404
- await import('../../../../src/client/js/state/router.svelte');
405
- initRouter();
406
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
407
- });
408
- });
409
-
410
- /*
411
- //////////////////////////////
412
- // navigate()
413
- //////////////////////////////
414
- */
415
-
416
- describe('navigate()', () => {
417
- afterEach(() => {
418
- vi.unstubAllGlobals();
419
- vi.resetModules();
420
- });
421
-
422
- it('calls navigation.navigate with the given path', async () => {
423
- const navStub = makeNavigationStub();
424
- vi.stubGlobal('location', { pathname: '/admin' });
425
- vi.stubGlobal('navigation', navStub);
426
- const { navigate } =
427
- await import('../../../../src/client/js/state/router.svelte');
428
- navigate('/admin/posts');
429
- expect(navStub.navigate).toHaveBeenCalledWith('/admin/posts');
430
- });
431
- });
432
-
433
- /*
434
- //////////////////////////////
435
- // initRouter — Navigation API listener registration
436
- //////////////////////////////
437
- */
438
-
439
- describe('initRouter — navigate listener', () => {
440
- afterEach(() => {
441
- vi.unstubAllGlobals();
442
- vi.resetModules();
443
- });
444
-
445
- it('intercepts /admin navigations and updates reactive route state', async () => {
446
- const navStub = makeNavigationStub();
447
- vi.stubGlobal('location', { pathname: '/admin' });
448
- vi.stubGlobal('navigation', navStub);
449
- vi.stubGlobal('window', {
450
- addEventListener: vi.fn(),
451
- confirm: vi.fn(() => true),
452
- });
453
- const { initRouter, nav } =
454
- await import('../../../../src/client/js/state/router.svelte');
455
- initRouter();
456
-
457
- const event = makeFakeNavigateEvent('http://localhost/admin/posts');
458
- navStub.fire(event);
459
- // handler must have been called to update route
460
- expect(event.intercepted).toBe(true);
461
- event.interceptHandler!();
462
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
463
- });
464
-
465
- it('does not intercept navigations outside /admin', async () => {
466
- const navStub = makeNavigationStub();
467
- vi.stubGlobal('location', { pathname: '/admin' });
468
- vi.stubGlobal('navigation', navStub);
469
- vi.stubGlobal('window', { addEventListener: vi.fn() });
470
- const { initRouter, nav } =
471
- await import('../../../../src/client/js/state/router.svelte');
472
- initRouter();
473
-
474
- const event = makeFakeNavigateEvent('http://localhost/other-page');
475
- navStub.fire(event);
476
- expect(event.intercepted).toBe(false);
477
- // route should remain at home
478
- expect(nav.route).toEqual({ view: 'home' });
479
- });
480
-
481
- it('does not intercept hash-change events', async () => {
482
- const navStub = makeNavigationStub();
483
- vi.stubGlobal('location', { pathname: '/admin' });
484
- vi.stubGlobal('navigation', navStub);
485
- vi.stubGlobal('window', { addEventListener: vi.fn() });
486
- const { initRouter } =
487
- await import('../../../../src/client/js/state/router.svelte');
488
- initRouter();
489
-
490
- const event = makeFakeNavigateEvent('http://localhost/admin#section', {
491
- hashChange: true,
492
- });
493
- navStub.fire(event);
494
- expect(event.intercepted).toBe(false);
495
- });
496
-
497
- it('does not intercept when canIntercept is false', async () => {
498
- const navStub = makeNavigationStub();
499
- vi.stubGlobal('location', { pathname: '/admin' });
500
- vi.stubGlobal('navigation', navStub);
501
- vi.stubGlobal('window', { addEventListener: vi.fn() });
502
- const { initRouter } =
503
- await import('../../../../src/client/js/state/router.svelte');
504
- initRouter();
505
-
506
- const event = makeFakeNavigateEvent('http://localhost/admin/posts', {
507
- canIntercept: false,
508
- });
509
- navStub.fire(event);
510
- expect(event.intercepted).toBe(false);
511
- });
512
- });
513
-
514
- /*
515
- //////////////////////////////
516
- // initRouter — idempotence
517
- //////////////////////////////
518
- */
519
-
520
- describe('initRouter — idempotent registration', () => {
521
- afterEach(() => {
522
- vi.unstubAllGlobals();
523
- vi.resetModules();
524
- });
525
-
526
- it('only registers one navigate listener even when called multiple times', async () => {
527
- const navStub = makeNavigationStub();
528
- vi.stubGlobal('location', { pathname: '/admin' });
529
- vi.stubGlobal('navigation', navStub);
530
- vi.stubGlobal('window', { addEventListener: vi.fn() });
531
- const { initRouter } =
532
- await import('../../../../src/client/js/state/router.svelte');
533
-
534
- initRouter();
535
- initRouter();
536
- initRouter();
537
-
538
- // addEventListener('navigate', ...) must have been called exactly once
539
- const navigateCalls = navStub.addEventListener.mock.calls.filter(
540
- ([type]: [string]) => type === 'navigate',
541
- );
542
- expect(navigateCalls).toHaveLength(1);
543
- });
544
- });
545
-
546
- /*
547
- //////////////////////////////
548
- // registerDirtyChecker — navigation interception
549
- //////////////////////////////
550
- */
551
-
552
- describe('registerDirtyChecker', () => {
553
- afterEach(() => {
554
- vi.unstubAllGlobals();
555
- vi.resetModules();
556
- });
557
-
558
- it('blocks navigation when dirty and user cancels confirm', async () => {
559
- const navStub = makeNavigationStub();
560
- const confirmSpy = vi.fn(() => false); // user clicks "Cancel"
561
- vi.stubGlobal('location', { pathname: '/admin' });
562
- vi.stubGlobal('navigation', navStub);
563
- vi.stubGlobal('confirm', confirmSpy);
564
- vi.stubGlobal('window', { addEventListener: vi.fn() });
565
- const { initRouter, registerDirtyChecker, nav } =
566
- await import('../../../../src/client/js/state/router.svelte');
567
- initRouter();
568
- registerDirtyChecker(() => true);
569
-
570
- const event = makeFakeNavigateEvent('http://localhost/admin/posts');
571
- navStub.fire(event);
572
-
573
- expect(confirmSpy).toHaveBeenCalled();
574
- expect(event.preventDefault).toHaveBeenCalled();
575
- expect(event.intercepted).toBe(false);
576
- // Route must NOT have changed
577
- expect(nav.route).toEqual({ view: 'home' });
578
- });
579
-
580
- it('allows navigation when dirty but user confirms', async () => {
581
- const navStub = makeNavigationStub();
582
- const confirmSpy = vi.fn(() => true); // user clicks "OK"
583
- vi.stubGlobal('location', { pathname: '/admin' });
584
- vi.stubGlobal('navigation', navStub);
585
- vi.stubGlobal('confirm', confirmSpy);
586
- vi.stubGlobal('window', { addEventListener: vi.fn() });
587
- const { initRouter, registerDirtyChecker, nav } =
588
- await import('../../../../src/client/js/state/router.svelte');
589
- initRouter();
590
- registerDirtyChecker(() => true);
591
-
592
- const event = makeFakeNavigateEvent('http://localhost/admin/posts');
593
- navStub.fire(event);
594
- event.interceptHandler!();
595
-
596
- expect(event.intercepted).toBe(true);
597
- expect(event.preventDefault).not.toHaveBeenCalled();
598
- expect(nav.route).toEqual({ view: 'collection', collection: 'posts' });
599
- });
600
-
601
- it('skips the confirm when the editor is not dirty', async () => {
602
- const navStub = makeNavigationStub();
603
- const confirmSpy = vi.fn(() => false);
604
- vi.stubGlobal('location', { pathname: '/admin' });
605
- vi.stubGlobal('navigation', navStub);
606
- vi.stubGlobal('confirm', confirmSpy);
607
- vi.stubGlobal('window', { addEventListener: vi.fn() });
608
- const { initRouter, registerDirtyChecker } =
609
- await import('../../../../src/client/js/state/router.svelte');
610
- initRouter();
611
- registerDirtyChecker(() => false);
612
-
613
- const event = makeFakeNavigateEvent('http://localhost/admin/posts');
614
- navStub.fire(event);
615
-
616
- expect(confirmSpy).not.toHaveBeenCalled();
617
- expect(event.intercepted).toBe(true);
618
- });
619
- });