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
@@ -0,0 +1,216 @@
1
+ /*
2
+ * Storage adapter backed by the GitHub REST API.
3
+ * Uses a Personal Access Token for authentication. All file operations
4
+ * target src/content/{collection}/ within the repository.
5
+ */
6
+ /**
7
+ * Encodes a Uint8Array to a base64 string without using deprecated unescape().
8
+ * @param {Uint8Array} bytes - The bytes to encode
9
+ * @return {string} Base64-encoded string
10
+ */
11
+ function uint8ToBase64(bytes) {
12
+ let binary = '';
13
+ for (const byte of bytes)
14
+ binary += String.fromCharCode(byte);
15
+ return btoa(binary);
16
+ }
17
+ export class GitHubAdapter {
18
+ token;
19
+ owner;
20
+ repo;
21
+ // Cached on validate(); defaults to 'main' before validation
22
+ defaultBranch = 'main';
23
+ /**
24
+ * @param {string} token - GitHub Personal Access Token
25
+ * @param {string} repoSlug - Repository in "owner/repo" format
26
+ */
27
+ constructor(token, repoSlug) {
28
+ this.token = token;
29
+ const [owner, repo] = repoSlug.split('/');
30
+ this.owner = owner;
31
+ this.repo = repo;
32
+ }
33
+ /**
34
+ * Validates credentials by fetching repo metadata and caches the default branch.
35
+ * @return {Promise<void>}
36
+ */
37
+ async validate() {
38
+ const res = await this.request('GET', `/repos/${this.owner}/${this.repo}`);
39
+ if (!res.ok) {
40
+ const status = res.status;
41
+ if (status === 401)
42
+ throw new Error('Invalid or expired token');
43
+ if (status === 403)
44
+ throw new Error('Token lacks repository access');
45
+ if (status === 404)
46
+ throw new Error(`Repository "${this.owner}/${this.repo}" not found`);
47
+ throw new Error(`GitHub API error: ${status}`);
48
+ }
49
+ const data = await res.json();
50
+ this.defaultBranch = data.default_branch;
51
+ }
52
+ /**
53
+ * Lists files in a collection matching the given extensions, with their raw content.
54
+ * @param {string} collection - The collection name
55
+ * @param {string[]} extensions - File extensions to include (e.g. ['.md', '.mdx'])
56
+ * @return {Promise<FileEntry[]>} Array of filename + content pairs
57
+ */
58
+ async listFiles(collection, extensions) {
59
+ const path = `src/content/${collection}`;
60
+ // Get directory listing
61
+ const listRes = await this.request('GET', `/repos/${this.owner}/${this.repo}/contents/${path}?ref=${this.defaultBranch}`);
62
+ if (!listRes.ok) {
63
+ if (listRes.status === 404)
64
+ return [];
65
+ throw new Error(`Failed to list files: ${listRes.status}`);
66
+ }
67
+ const listing = await listRes.json();
68
+ // Filter to requested extensions and fetch all files in parallel
69
+ const filtered = listing.filter((f) => extensions.some((ext) => f.name.endsWith(ext)));
70
+ return Promise.all(filtered.map(async (file) => ({
71
+ filename: file.name,
72
+ content: await this.readFile(collection, file.name),
73
+ })));
74
+ }
75
+ /**
76
+ * Deletes a file from the repository via the Contents API.
77
+ * @param {string} collection - The collection name
78
+ * @param {string} filename - The filename to delete
79
+ * @return {Promise<void>}
80
+ */
81
+ async deleteFile(collection, filename) {
82
+ const path = `src/content/${collection}/${filename}`;
83
+ // Get the current SHA (required by the GitHub Contents API for deletion)
84
+ const existing = await this.request('GET', `/repos/${this.owner}/${this.repo}/contents/${path}?ref=${this.defaultBranch}`);
85
+ if (!existing.ok)
86
+ throw new Error(`File not found for deletion: ${path}`);
87
+ const data = await existing.json();
88
+ const res = await this.request('DELETE', `/repos/${this.owner}/${this.repo}/contents/${path}`, undefined, {
89
+ message: `Delete ${path}`,
90
+ sha: data.sha,
91
+ branch: this.defaultBranch,
92
+ });
93
+ if (!res.ok)
94
+ throw new Error(`Failed to delete ${path}: ${res.status}`);
95
+ }
96
+ /**
97
+ * Reads a single file's raw content via the raw+json Accept header.
98
+ * @param {string} collection - The collection name
99
+ * @param {string} filename - The filename
100
+ * @return {Promise<string>} The file content
101
+ */
102
+ async readFile(collection, filename) {
103
+ const path = `src/content/${collection}/${filename}`;
104
+ const res = await this.request('GET', `/repos/${this.owner}/${this.repo}/contents/${path}?ref=${this.defaultBranch}`, { Accept: 'application/vnd.github.raw+json' });
105
+ if (!res.ok) {
106
+ throw new Error(`Failed to read ${path}: ${res.status}`);
107
+ }
108
+ return res.text();
109
+ }
110
+ /**
111
+ * Writes a single file via the Contents API. Fetches the current SHA for updates.
112
+ * @param {string} collection - The collection name
113
+ * @param {string} filename - The filename
114
+ * @param {string} content - The content to write
115
+ * @return {Promise<void>}
116
+ */
117
+ async writeFile(collection, filename, content) {
118
+ const path = `src/content/${collection}/${filename}`;
119
+ // Get current SHA if file exists (required by GitHub API for updates)
120
+ let sha;
121
+ const existing = await this.request('GET', `/repos/${this.owner}/${this.repo}/contents/${path}?ref=${this.defaultBranch}`);
122
+ if (existing.ok) {
123
+ const data = await existing.json();
124
+ sha = data.sha;
125
+ }
126
+ const body = {
127
+ message: `Update ${path}`,
128
+ content: uint8ToBase64(new TextEncoder().encode(content)),
129
+ branch: this.defaultBranch,
130
+ };
131
+ if (sha)
132
+ body.sha = sha;
133
+ const res = await this.request('PUT', `/repos/${this.owner}/${this.repo}/contents/${path}`, undefined, body);
134
+ if (!res.ok) {
135
+ const errText = await res.text();
136
+ throw new Error(`Failed to write ${path}: ${res.status} ${errText}`);
137
+ }
138
+ }
139
+ /**
140
+ * Writes multiple files in a single atomic commit using the Git Trees + Commits API.
141
+ * @param {FileWrite[]} files - Array of files to write
142
+ * @return {Promise<void>}
143
+ */
144
+ async writeFiles(files) {
145
+ if (files.length === 0)
146
+ return;
147
+ if (files.length === 1) {
148
+ await this.writeFile(files[0].collection, files[0].filename, files[0].content);
149
+ return;
150
+ }
151
+ // 1. Get current commit SHA for the default branch
152
+ const refRes = await this.request('GET', `/repos/${this.owner}/${this.repo}/git/ref/heads/${this.defaultBranch}`);
153
+ if (!refRes.ok)
154
+ throw new Error(`Failed to get branch ref: ${refRes.status}`);
155
+ const refData = await refRes.json();
156
+ const baseCommitSHA = refData.object.sha;
157
+ // Get the base tree SHA from the current commit
158
+ const commitRes = await this.request('GET', `/repos/${this.owner}/${this.repo}/git/commits/${baseCommitSHA}`);
159
+ if (!commitRes.ok)
160
+ throw new Error(`Failed to get commit: ${commitRes.status}`);
161
+ const commitData = await commitRes.json();
162
+ const baseTreeSHA = commitData.tree.sha;
163
+ // 2. Create a new tree containing all file changes
164
+ const tree = files.map((f) => ({
165
+ path: `src/content/${f.collection}/${f.filename}`,
166
+ mode: '100644',
167
+ type: 'blob',
168
+ content: f.content,
169
+ }));
170
+ const treeRes = await this.request('POST', `/repos/${this.owner}/${this.repo}/git/trees`, undefined, { base_tree: baseTreeSHA, tree });
171
+ if (!treeRes.ok)
172
+ throw new Error(`Failed to create tree: ${treeRes.status}`);
173
+ const treeData = await treeRes.json();
174
+ // 3. Create a new commit pointing at the new tree
175
+ const paths = files.map((f) => `${f.collection}/${f.filename}`).join(', ');
176
+ const newCommitRes = await this.request('POST', `/repos/${this.owner}/${this.repo}/git/commits`, undefined, {
177
+ message: `Update ${paths}`,
178
+ tree: treeData.sha,
179
+ parents: [baseCommitSHA],
180
+ });
181
+ if (!newCommitRes.ok)
182
+ throw new Error(`Failed to create commit: ${newCommitRes.status}`);
183
+ const newCommitData = await newCommitRes.json();
184
+ // 4. Advance the branch ref to the new commit
185
+ const updateRefRes = await this.request('PATCH', `/repos/${this.owner}/${this.repo}/git/refs/heads/${this.defaultBranch}`, undefined, { sha: newCommitData.sha });
186
+ if (!updateRefRes.ok)
187
+ throw new Error(`Failed to update ref: ${updateRefRes.status}`);
188
+ }
189
+ /**
190
+ * Makes an authenticated request to the GitHub API.
191
+ * @param {string} method - HTTP method
192
+ * @param {string} path - API path (appended to https://api.github.com)
193
+ * @param {Record<string, string>} [extraHeaders] - Additional headers to merge
194
+ * @param {unknown} [body] - JSON body to send
195
+ * @return {Promise<Response>} The fetch response
196
+ */
197
+ async request(method, path, extraHeaders, body) {
198
+ const headers = {
199
+ Authorization: `Bearer ${this.token}`,
200
+ 'X-GitHub-Api-Version': '2026-03-10',
201
+ // Default to JSON so the browser cache distinguishes from raw content requests
202
+ Accept: 'application/vnd.github+json',
203
+ ...extraHeaders,
204
+ };
205
+ if (body) {
206
+ headers['Content-Type'] = 'application/json';
207
+ }
208
+ return fetch(`https://api.github.com${path}`, {
209
+ method,
210
+ headers,
211
+ body: body ? JSON.stringify(body) : undefined,
212
+ // API responses must never be served from browser cache
213
+ cache: 'no-store',
214
+ });
215
+ }
216
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Backend configuration stored in IndexedDB. Tagged union discriminated on `type`.
3
+ * Security note: the GitHub token is stored in plaintext in IndexedDB. This is a
4
+ * deliberate trade-off for a client-only app with no server to proxy through.
5
+ * Same-origin policy protects it from other sites, but any XSS vulnerability
6
+ * would expose the token.
7
+ */
8
+ export type BackendConfig = {
9
+ type: 'fsa';
10
+ handle: FileSystemDirectoryHandle;
11
+ } | {
12
+ type: 'github';
13
+ token: string;
14
+ repo: string;
15
+ };
16
+ /**
17
+ * Stores backend configuration in IndexedDB for persistence across sessions.
18
+ * @param {BackendConfig} config - The backend config to store
19
+ * @return {Promise<void>}
20
+ */
21
+ export declare function saveBackend(config: BackendConfig): Promise<void>;
22
+ /**
23
+ * Retrieves stored backend configuration from IndexedDB. Handles migration from the old format where a bare FileSystemDirectoryHandle was stored.
24
+ * @return {Promise<BackendConfig | null>} The stored config, or null if none exists
25
+ */
26
+ export declare function loadBackend(): Promise<BackendConfig | null>;
27
+ /**
28
+ * Removes the stored backend configuration from IndexedDB. Also clears the legacy key if present.
29
+ * @return {Promise<void>}
30
+ */
31
+ export declare function clearBackend(): Promise<void>;
32
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../src/client/js/storage/storage.ts"],"names":[],"mappings":"AAUA;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GACrB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAA;CAAE,GAClD;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAQtE;AAED;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAyBjE;AAED;;;GAGG;AACH,wBAAsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAWlD"}
@@ -0,0 +1,68 @@
1
+ /*
2
+ * Backend configuration persistence using IndexedDB.
3
+ * Stores and retrieves the active storage backend (FSA or GitHub).
4
+ */
5
+ import { openDB } from './db';
6
+ // Fixed key for the backend config
7
+ const BACKEND_KEY = 'backend';
8
+ /**
9
+ * Stores backend configuration in IndexedDB for persistence across sessions.
10
+ * @param {BackendConfig} config - The backend config to store
11
+ * @return {Promise<void>}
12
+ */
13
+ export async function saveBackend(config) {
14
+ const db = await openDB();
15
+ return new Promise((resolve, reject) => {
16
+ const tx = db.transaction('handles', 'readwrite');
17
+ tx.objectStore('handles').put(config, BACKEND_KEY);
18
+ tx.oncomplete = () => resolve();
19
+ tx.onerror = () => reject(tx.error);
20
+ });
21
+ }
22
+ /**
23
+ * Retrieves stored backend configuration from IndexedDB. Handles migration from the old format where a bare FileSystemDirectoryHandle was stored.
24
+ * @return {Promise<BackendConfig | null>} The stored config, or null if none exists
25
+ */
26
+ export async function loadBackend() {
27
+ const db = await openDB();
28
+ return new Promise((resolve, reject) => {
29
+ const tx = db.transaction('handles', 'readonly');
30
+ const request = tx.objectStore('handles').get(BACKEND_KEY);
31
+ request.onsuccess = () => {
32
+ const result = request.result;
33
+ if (!result) {
34
+ // Check for old-format handle stored under the legacy key
35
+ const legacyRequest = tx.objectStore('handles').get('projectRoot');
36
+ legacyRequest.onsuccess = () => {
37
+ const legacy = legacyRequest.result;
38
+ if (legacy && legacy instanceof FileSystemDirectoryHandle) {
39
+ resolve({ type: 'fsa', handle: legacy });
40
+ }
41
+ else {
42
+ resolve(null);
43
+ }
44
+ };
45
+ legacyRequest.onerror = () => resolve(null);
46
+ return;
47
+ }
48
+ resolve(result);
49
+ };
50
+ request.onerror = () => reject(request.error);
51
+ });
52
+ }
53
+ /**
54
+ * Removes the stored backend configuration from IndexedDB. Also clears the legacy key if present.
55
+ * @return {Promise<void>}
56
+ */
57
+ export async function clearBackend() {
58
+ const db = await openDB();
59
+ return new Promise((resolve, reject) => {
60
+ const tx = db.transaction('handles', 'readwrite');
61
+ const store = tx.objectStore('handles');
62
+ store.delete(BACKEND_KEY);
63
+ // Clean up legacy key if present
64
+ store.delete('projectRoot');
65
+ tx.oncomplete = () => resolve();
66
+ tx.onerror = () => reject(tx.error);
67
+ });
68
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=frontmatter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontmatter.d.ts","sourceRoot":"","sources":["../../../../../src/client/js/storage/workers/frontmatter.ts"],"names":[],"mappings":""}
@@ -0,0 +1,253 @@
1
+ /*
2
+ * Orchestrator Worker
3
+ *
4
+ * Routes file parsing by category: frontmatter files have their YAML block
5
+ * extracted via string manipulation and sent to the YAML parser worker;
6
+ * JSON data files are parsed inline; YAML/TOML data files are sent to their
7
+ * respective parser workers. Parser workers are lazily spawned on first need.
8
+ */
9
+ import { StorageClient } from '../client';
10
+ import { getFileCategory, getDataFormat } from '../../utils/file-types';
11
+ import { splitFrontmatter } from '../../utils/frontmatter';
12
+ /**
13
+ * Extracts the raw YAML block from a frontmatter-delimited file (markdown/MDX/Markdoc).
14
+ * Delegates BOM stripping, CRLF normalization, and delimiter logic to splitFrontmatter.
15
+ * Returns the raw YAML string without parsing it — parsing is delegated to the YAML parser worker.
16
+ * @param {string} content - Raw file content
17
+ * @return {string | null} The raw YAML string between --- delimiters, or null if none found
18
+ */
19
+ function extractYamlBlock(content) {
20
+ const { rawFrontmatter } = splitFrontmatter(content);
21
+ return rawFrontmatter.trim() ? rawFrontmatter : null;
22
+ }
23
+ /*
24
+ //////////////////////////////
25
+ // Parser worker management
26
+ //////////////////////////////
27
+ */
28
+ // Lazily-spawned parser workers
29
+ let yamlWorker = null;
30
+ let tomlWorker = null;
31
+ // Incrementing ID for correlating batch requests with responses
32
+ let batchIdCounter = 0;
33
+ // Pending batch response promises keyed by ID
34
+ const pendingBatches = new Map();
35
+ /**
36
+ * Returns the lazily-spawned YAML parser worker, creating it on first call.
37
+ * Uses `.js` extension in the URL because svelte-package doesn't rewrite URL strings.
38
+ * @return {Worker} The YAML parser worker instance
39
+ */
40
+ function getYamlWorker() {
41
+ if (!yamlWorker) {
42
+ yamlWorker = new Worker(new URL('./yaml-parser.js', import.meta.url), {
43
+ type: 'module',
44
+ });
45
+ yamlWorker.onmessage = handleParserResponse;
46
+ yamlWorker.onerror = handleWorkerError;
47
+ }
48
+ return yamlWorker;
49
+ }
50
+ /**
51
+ * Returns the lazily-spawned TOML parser worker, creating it on first call.
52
+ * Uses `.js` extension in the URL because svelte-package doesn't rewrite URL strings.
53
+ * @return {Worker} The TOML parser worker instance
54
+ */
55
+ function getTomlWorker() {
56
+ if (!tomlWorker) {
57
+ tomlWorker = new Worker(new URL('./toml-parser.js', import.meta.url), {
58
+ type: 'module',
59
+ });
60
+ tomlWorker.onmessage = handleParserResponse;
61
+ tomlWorker.onerror = handleWorkerError;
62
+ }
63
+ return tomlWorker;
64
+ }
65
+ /**
66
+ * Handles fatal errors from parser workers by rejecting all pending batch promises
67
+ * so callers don't hang indefinitely waiting for a response that will never arrive.
68
+ * @param {ErrorEvent} e - The error event from the worker
69
+ * @return {void}
70
+ */
71
+ function handleWorkerError(e) {
72
+ for (const [id, { reject }] of pendingBatches) {
73
+ reject(new Error(`Parser worker error: ${e.message}`));
74
+ pendingBatches.delete(id);
75
+ }
76
+ }
77
+ /**
78
+ * Handles responses from parser workers by resolving/rejecting the corresponding
79
+ * pending batch promise based on the response ID.
80
+ * @param {MessageEvent} event - The message event from the parser worker
81
+ * @return {void}
82
+ */
83
+ function handleParserResponse(event) {
84
+ const { type, id, ok, results, error } = event.data;
85
+ // Ignore messages that aren't batch results (e.g. single parse results, stringify results)
86
+ if (type !== 'parse-batch-result')
87
+ return;
88
+ const pending = pendingBatches.get(id);
89
+ if (!pending)
90
+ return;
91
+ pendingBatches.delete(id);
92
+ if (ok) {
93
+ pending.resolve(results);
94
+ }
95
+ else {
96
+ pending.reject(new Error(error));
97
+ }
98
+ }
99
+ /**
100
+ * Sends a batch of key/content pairs to a parser worker and returns a promise
101
+ * that resolves with the parsed results map. Uses an incrementing ID for
102
+ * request/response correlation, following the same pattern as StorageClient.
103
+ * @param {Worker} worker - The parser worker to send the batch to
104
+ * @param {BatchItem[]} items - Array of key/content pairs to parse
105
+ * @return {Promise<Record<string, Record<string, unknown>>>} Map of key to parsed data
106
+ */
107
+ function sendBatch(worker, items) {
108
+ const id = String(++batchIdCounter);
109
+ return new Promise((resolve, reject) => {
110
+ pendingBatches.set(id, { resolve, reject });
111
+ worker.postMessage({ type: 'parse-batch', id, items });
112
+ });
113
+ }
114
+ /*
115
+ //////////////////////////////
116
+ // File categorization and parsing
117
+ //////////////////////////////
118
+ */
119
+ /**
120
+ * Processes a list of files by categorizing each file, collecting batch items
121
+ * for YAML/TOML parser workers, parsing JSON inline, and assembling the final
122
+ * items array with parsed data.
123
+ * @param {FileEntry[]} files - The files returned by the storage adapter
124
+ * @return {Promise<Array<{ filename: string; data: Record<string, unknown> }>>} Parsed items
125
+ */
126
+ async function processFiles(files) {
127
+ // Inline-parsed results (JSON files)
128
+ const inlineResults = [];
129
+ // Batch items for YAML worker (frontmatter YAML blocks + YAML data files)
130
+ const yamlBatch = [];
131
+ // Batch items for TOML worker
132
+ const tomlBatch = [];
133
+ // Files with no frontmatter get empty data
134
+ const emptyDataFiles = [];
135
+ for (const file of files) {
136
+ const category = getFileCategory(file.filename);
137
+ if (category === 'frontmatter') {
138
+ const yamlBlock = extractYamlBlock(file.content);
139
+ if (yamlBlock) {
140
+ yamlBatch.push({ key: file.filename, content: yamlBlock });
141
+ }
142
+ else {
143
+ // No frontmatter found — include with empty data
144
+ emptyDataFiles.push(file.filename);
145
+ }
146
+ continue;
147
+ }
148
+ if (category === 'data') {
149
+ const format = getDataFormat(file.filename);
150
+ if (format === 'json') {
151
+ try {
152
+ const data = JSON.parse(file.content);
153
+ inlineResults.push({ filename: file.filename, data });
154
+ }
155
+ catch {
156
+ // Invalid JSON — include with empty data
157
+ inlineResults.push({ filename: file.filename, data: {} });
158
+ }
159
+ continue;
160
+ }
161
+ if (format === 'yaml') {
162
+ yamlBatch.push({ key: file.filename, content: file.content });
163
+ continue;
164
+ }
165
+ if (format === 'toml') {
166
+ tomlBatch.push({ key: file.filename, content: file.content });
167
+ continue;
168
+ }
169
+ }
170
+ // Unrecognised file type — include with empty data
171
+ emptyDataFiles.push(file.filename);
172
+ }
173
+ // Send batches to parser workers in parallel
174
+ const promises = [];
175
+ let yamlPromiseIdx = -1;
176
+ let tomlPromiseIdx = -1;
177
+ if (yamlBatch.length > 0) {
178
+ yamlPromiseIdx = promises.length;
179
+ promises.push(sendBatch(getYamlWorker(), yamlBatch));
180
+ }
181
+ if (tomlBatch.length > 0) {
182
+ tomlPromiseIdx = promises.length;
183
+ promises.push(sendBatch(getTomlWorker(), tomlBatch));
184
+ }
185
+ const batchResults = await Promise.all(promises);
186
+ // Assemble final items from all sources
187
+ const items = [];
188
+ // Add inline results (JSON)
189
+ items.push(...inlineResults);
190
+ // Add YAML results — filenames are derived from the batch result keys
191
+ if (yamlPromiseIdx >= 0) {
192
+ const yamlResults = batchResults[yamlPromiseIdx];
193
+ for (const [filename, data] of Object.entries(yamlResults)) {
194
+ items.push({ filename, data });
195
+ }
196
+ }
197
+ // Add TOML results — filenames are derived from the batch result keys
198
+ if (tomlPromiseIdx >= 0) {
199
+ const tomlResults = batchResults[tomlPromiseIdx];
200
+ for (const [filename, data] of Object.entries(tomlResults)) {
201
+ items.push({ filename, data });
202
+ }
203
+ }
204
+ // Add empty-data files
205
+ for (const filename of emptyDataFiles) {
206
+ items.push({ filename, data: {} });
207
+ }
208
+ return items;
209
+ }
210
+ /*
211
+ //////////////////////////////
212
+ // Main message handler
213
+ //////////////////////////////
214
+ */
215
+ // Storage client, initialized when the main thread transfers a port
216
+ let storageClient = null;
217
+ // Handle messages from main thread
218
+ self.addEventListener('message', async (event) => {
219
+ const { type } = event.data;
220
+ if (type === 'port') {
221
+ // Main thread is transferring a MessagePort connected to the storage SharedWorker
222
+ const port = event.ports[0];
223
+ storageClient = new StorageClient(port);
224
+ return;
225
+ }
226
+ if (type === 'parse') {
227
+ const { collection } = event.data;
228
+ if (!storageClient) {
229
+ self.postMessage({
230
+ type: 'error',
231
+ message: 'Storage port not initialized',
232
+ });
233
+ return;
234
+ }
235
+ try {
236
+ // Pass extensions from the message, defaulting to markdown for backward compatibility
237
+ const extensions = event.data.extensions ?? ['.md', '.mdx'];
238
+ const files = await storageClient.listFiles(collection, extensions);
239
+ const items = await processFiles(files);
240
+ // Sort alphabetically by title, falling back to filename
241
+ items.sort((a, b) => {
242
+ const aTitle = typeof a.data.title === 'string' ? a.data.title : a.filename;
243
+ const bTitle = typeof b.data.title === 'string' ? b.data.title : b.filename;
244
+ return aTitle.toLowerCase().localeCompare(bTitle.toLowerCase());
245
+ });
246
+ self.postMessage({ type: 'result', collection, items });
247
+ }
248
+ catch (err) {
249
+ const message = err instanceof Error ? err.message : String(err);
250
+ self.postMessage({ type: 'error', message });
251
+ }
252
+ }
253
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../../../../../src/client/js/storage/workers/storage.ts"],"names":[],"mappings":""}