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.
- package/README.md +3 -1
- package/dist/astro/index.d.ts +43 -0
- package/dist/astro/index.d.ts.map +1 -0
- package/dist/astro/index.js +223 -0
- package/{src → dist}/client/Admin.svelte +20 -11
- package/dist/client/Admin.svelte.d.ts +11 -0
- package/dist/client/Admin.svelte.d.ts.map +1 -0
- package/dist/client/components/BackendPicker.svelte.d.ts +4 -0
- package/dist/client/components/BackendPicker.svelte.d.ts.map +1 -0
- package/dist/client/components/DraftChip.svelte.d.ts +10 -0
- package/dist/client/components/DraftChip.svelte.d.ts.map +1 -0
- package/dist/client/components/MetadataForm.svelte.d.ts +12 -0
- package/dist/client/components/MetadataForm.svelte.d.ts.map +1 -0
- package/dist/client/components/ThemeToggle.svelte.d.ts +19 -0
- package/dist/client/components/ThemeToggle.svelte.d.ts.map +1 -0
- package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts +11 -0
- package/dist/client/components/dialogs/DeleteDraftDialog.svelte.d.ts.map +1 -0
- package/{src → dist}/client/components/dialogs/FilenameDialog.svelte +1 -2
- package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts +13 -0
- package/dist/client/components/dialogs/FilenameDialog.svelte.d.ts.map +1 -0
- package/dist/client/components/editor/EditorPane.svelte.d.ts +4 -0
- package/dist/client/components/editor/EditorPane.svelte.d.ts.map +1 -0
- package/dist/client/components/editor/EditorTabs.svelte.d.ts +8 -0
- package/dist/client/components/editor/EditorTabs.svelte.d.ts.map +1 -0
- package/dist/client/components/editor/EditorToolbar.svelte.d.ts +4 -0
- package/dist/client/components/editor/EditorToolbar.svelte.d.ts.map +1 -0
- package/dist/client/components/editor/FormatSelector.svelte.d.ts +4 -0
- package/dist/client/components/editor/FormatSelector.svelte.d.ts.map +1 -0
- package/dist/client/components/editor/Toolbar.svelte.d.ts +19 -0
- package/dist/client/components/editor/Toolbar.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/ArrayField.svelte.d.ts +15 -0
- package/dist/client/components/fields/ArrayField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/ArrayItem.svelte.d.ts +28 -0
- package/dist/client/components/fields/ArrayItem.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/BooleanField.svelte.d.ts +16 -0
- package/dist/client/components/fields/BooleanField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/DateField.svelte.d.ts +16 -0
- package/dist/client/components/fields/DateField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/EnumField.svelte.d.ts +17 -0
- package/dist/client/components/fields/EnumField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/FieldWrapper.svelte.d.ts +18 -0
- package/dist/client/components/fields/FieldWrapper.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/NumberField.svelte.d.ts +16 -0
- package/dist/client/components/fields/NumberField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/ObjectField.svelte.d.ts +16 -0
- package/dist/client/components/fields/ObjectField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/SchemaField.svelte.d.ts +16 -0
- package/dist/client/components/fields/SchemaField.svelte.d.ts.map +1 -0
- package/dist/client/components/fields/StringField.svelte.d.ts +16 -0
- package/dist/client/components/fields/StringField.svelte.d.ts.map +1 -0
- package/{src → dist}/client/components/sidebar/AdminSidebar.svelte +2 -4
- package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts +19 -0
- package/dist/client/components/sidebar/AdminSidebar.svelte.d.ts.map +1 -0
- package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts +12 -0
- package/dist/client/components/sidebar/AdminSidebarSort.svelte.d.ts.map +1 -0
- package/dist/client/css/icons.css +29 -0
- package/dist/client/index.d.ts +2 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/js/drafts/merge.svelte.d.ts +24 -0
- package/dist/client/js/drafts/merge.svelte.d.ts.map +1 -0
- package/dist/client/js/drafts/merge.svelte.js +106 -0
- package/dist/client/js/drafts/ops.svelte.d.ts +31 -0
- package/dist/client/js/drafts/ops.svelte.d.ts.map +1 -0
- package/dist/client/js/drafts/ops.svelte.js +182 -0
- package/dist/client/js/drafts/storage.d.ts +45 -0
- package/dist/client/js/drafts/storage.d.ts.map +1 -0
- package/dist/client/js/drafts/storage.js +76 -0
- package/dist/client/js/drafts/workers/diff.d.ts +2 -0
- package/dist/client/js/drafts/workers/diff.d.ts.map +1 -0
- package/dist/client/js/drafts/workers/diff.js +20 -0
- package/dist/client/js/editor/editor.svelte.d.ts +124 -0
- package/dist/client/js/editor/editor.svelte.d.ts.map +1 -0
- package/dist/client/js/editor/editor.svelte.js +294 -0
- package/dist/client/js/editor/languages.d.ts +11 -0
- package/dist/client/js/editor/languages.d.ts.map +1 -0
- package/dist/client/js/editor/languages.js +93 -0
- package/dist/client/js/editor/link-wrap.d.ts +6 -0
- package/dist/client/js/editor/link-wrap.d.ts.map +1 -0
- package/{src/client/js/editor/link-wrap.ts → dist/client/js/editor/link-wrap.js} +17 -24
- package/dist/client/js/editor/markdown-shortcuts.d.ts +4 -0
- package/dist/client/js/editor/markdown-shortcuts.d.ts.map +1 -0
- package/dist/client/js/editor/markdown-shortcuts.js +219 -0
- package/dist/client/js/handlers/admin.d.ts +64 -0
- package/dist/client/js/handlers/admin.d.ts.map +1 -0
- package/dist/client/js/handlers/admin.js +186 -0
- package/dist/client/js/state/dialogs.svelte.d.ts +16 -0
- package/dist/client/js/state/dialogs.svelte.d.ts.map +1 -0
- package/dist/client/js/state/dialogs.svelte.js +28 -0
- package/dist/client/js/state/router.svelte.d.ts +44 -0
- package/dist/client/js/state/router.svelte.d.ts.map +1 -0
- package/dist/client/js/state/router.svelte.js +141 -0
- package/dist/client/js/state/schema.svelte.d.ts +51 -0
- package/dist/client/js/state/schema.svelte.d.ts.map +1 -0
- package/{src/client/js/state/schema.svelte.ts → dist/client/js/state/schema.svelte.js} +55 -70
- package/dist/client/js/state/state.svelte.d.ts +68 -0
- package/dist/client/js/state/state.svelte.d.ts.map +1 -0
- package/dist/client/js/state/state.svelte.js +300 -0
- package/dist/client/js/state/theme.svelte.d.ts +24 -0
- package/dist/client/js/state/theme.svelte.d.ts.map +1 -0
- package/{src/client/js/state/theme.svelte.ts → dist/client/js/state/theme.svelte.js} +54 -91
- package/dist/client/js/storage/adapter.d.ts +130 -0
- package/dist/client/js/storage/adapter.d.ts.map +1 -0
- package/dist/client/js/storage/adapter.js +5 -0
- package/dist/client/js/storage/client.d.ts +72 -0
- package/dist/client/js/storage/client.d.ts.map +1 -0
- package/dist/client/js/storage/client.js +121 -0
- package/dist/client/js/storage/db.d.ts +8 -0
- package/dist/client/js/storage/db.d.ts.map +1 -0
- package/dist/client/js/storage/db.js +35 -0
- package/dist/client/js/storage/fsa.d.ts +51 -0
- package/dist/client/js/storage/fsa.d.ts.map +1 -0
- package/dist/client/js/storage/fsa.js +91 -0
- package/dist/client/js/storage/github.d.ts +62 -0
- package/dist/client/js/storage/github.d.ts.map +1 -0
- package/dist/client/js/storage/github.js +216 -0
- package/dist/client/js/storage/storage.d.ts +32 -0
- package/dist/client/js/storage/storage.d.ts.map +1 -0
- package/dist/client/js/storage/storage.js +68 -0
- package/dist/client/js/storage/workers/frontmatter.d.ts +2 -0
- package/dist/client/js/storage/workers/frontmatter.d.ts.map +1 -0
- package/dist/client/js/storage/workers/frontmatter.js +253 -0
- package/dist/client/js/storage/workers/storage.d.ts +2 -0
- package/dist/client/js/storage/workers/storage.d.ts.map +1 -0
- package/dist/client/js/storage/workers/storage.js +167 -0
- package/dist/client/js/storage/workers/toml-parser.d.ts +2 -0
- package/dist/client/js/storage/workers/toml-parser.d.ts.map +1 -0
- package/dist/client/js/storage/workers/toml-parser.js +75 -0
- package/dist/client/js/storage/workers/yaml-parser.d.ts +2 -0
- package/dist/client/js/storage/workers/yaml-parser.d.ts.map +1 -0
- package/dist/client/js/storage/workers/yaml-parser.js +100 -0
- package/dist/client/js/utils/file-types.d.ts +58 -0
- package/dist/client/js/utils/file-types.d.ts.map +1 -0
- package/{src/client/js/utils/file-types.ts → dist/client/js/utils/file-types.js} +75 -107
- package/dist/client/js/utils/format.d.ts +8 -0
- package/dist/client/js/utils/format.d.ts.map +1 -0
- package/{src/client/js/utils/format.ts → dist/client/js/utils/format.js} +5 -6
- package/dist/client/js/utils/frontmatter.d.ts +12 -0
- package/dist/client/js/utils/frontmatter.d.ts.map +1 -0
- package/dist/client/js/utils/frontmatter.js +29 -0
- package/dist/client/js/utils/schema-utils.d.ts +110 -0
- package/dist/client/js/utils/schema-utils.d.ts.map +1 -0
- package/dist/client/js/utils/schema-utils.js +242 -0
- package/dist/client/js/utils/slug.d.ts +8 -0
- package/dist/client/js/utils/slug.d.ts.map +1 -0
- package/{src/client/js/utils/slug.ts → dist/client/js/utils/slug.js} +6 -7
- package/dist/client/js/utils/sort.d.ts +41 -0
- package/dist/client/js/utils/sort.d.ts.map +1 -0
- package/dist/client/js/utils/sort.js +65 -0
- package/dist/client/js/utils/stable-stringify.d.ts +8 -0
- package/dist/client/js/utils/stable-stringify.d.ts.map +1 -0
- package/dist/client/js/utils/stable-stringify.js +23 -0
- package/dist/client/js/utils/url-utils.d.ts +11 -0
- package/dist/client/js/utils/url-utils.d.ts.map +1 -0
- package/{src/client/js/utils/url-utils.ts → dist/client/js/utils/url-utils.js} +22 -23
- package/dist/client/types/browser-apis.d.ts +39 -0
- package/dist/types.d.ts +22 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/package.json +13 -3
- package/.github/workflows/ci.yml +0 -27
- package/.github/workflows/publish.yml +0 -34
- package/.mcp.json +0 -12
- package/.prettierignore +0 -5
- package/.prettierrc.cjs +0 -22
- package/AGENTS.md +0 -183
- package/playground/astro.config.mjs +0 -7
- package/playground/node_modules/.bin/astro +0 -21
- package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js +0 -85
- package/playground/node_modules/.vite/deps/@astrojs_svelte_client__js.js.map +0 -7
- package/playground/node_modules/.vite/deps/_metadata.json +0 -184
- package/playground/node_modules/.vite/deps/astro___aria-query.js +0 -6776
- package/playground/node_modules/.vite/deps/astro___aria-query.js.map +0 -7
- package/playground/node_modules/.vite/deps/astro___axobject-query.js +0 -3754
- package/playground/node_modules/.vite/deps/astro___axobject-query.js.map +0 -7
- package/playground/node_modules/.vite/deps/astro___html-escaper.js +0 -34
- package/playground/node_modules/.vite/deps/astro___html-escaper.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js +0 -0
- package/playground/node_modules/.vite/deps/chunk-AJXJMYAF.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js +0 -8
- package/playground/node_modules/.vite/deps/chunk-BUSYA2B4.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js +0 -21
- package/playground/node_modules/.vite/deps/chunk-CNYJBM5F.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js +0 -223
- package/playground/node_modules/.vite/deps/chunk-DBPNBGEI.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js +0 -27
- package/playground/node_modules/.vite/deps/chunk-FPEUJ7DG.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js +0 -1005
- package/playground/node_modules/.vite/deps/chunk-MHDZ3SK7.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js +0 -204
- package/playground/node_modules/.vite/deps/chunk-RBDTDTPY.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js +0 -688
- package/playground/node_modules/.vite/deps/chunk-RJGEXL5C.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js +0 -5099
- package/playground/node_modules/.vite/deps/chunk-YL4MIWGJ.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js +0 -4376
- package/playground/node_modules/.vite/deps/chunk-ZOV3DWEJ.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js +0 -23
- package/playground/node_modules/.vite/deps/chunk-ZP4UNCSN.js.map +0 -7
- package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js +0 -148
- package/playground/node_modules/.vite/deps/chunk-ZREFNRZZ.js.map +0 -7
- package/playground/node_modules/.vite/deps/package.json +0 -3
- package/playground/node_modules/.vite/deps/smol-toml.js +0 -843
- package/playground/node_modules/.vite/deps/smol-toml.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte.js +0 -55
- package/playground/node_modules/.vite/deps/svelte.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte___clsx.js +0 -9
- package/playground/node_modules/.vite/deps/svelte___clsx.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_animate.js +0 -57
- package/playground/node_modules/.vite/deps/svelte_animate.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_attachments.js +0 -15
- package/playground/node_modules/.vite/deps/svelte_attachments.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_easing.js +0 -67
- package/playground/node_modules/.vite/deps/svelte_easing.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_events.js +0 -11
- package/playground/node_modules/.vite/deps/svelte_events.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal.js +0 -5
- package/playground/node_modules/.vite/deps/svelte_internal.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal_client.js +0 -402
- package/playground/node_modules/.vite/deps/svelte_internal_client.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js +0 -10
- package/playground/node_modules/.vite/deps/svelte_internal_disclose-version.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js +0 -8
- package/playground/node_modules/.vite/deps/svelte_internal_flags_async.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js +0 -8
- package/playground/node_modules/.vite/deps/svelte_internal_flags_legacy.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js +0 -8
- package/playground/node_modules/.vite/deps/svelte_internal_flags_tracing.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_legacy.js +0 -35
- package/playground/node_modules/.vite/deps/svelte_legacy.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_motion.js +0 -545
- package/playground/node_modules/.vite/deps/svelte_motion.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_reactivity.js +0 -29
- package/playground/node_modules/.vite/deps/svelte_reactivity.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_reactivity_window.js +0 -127
- package/playground/node_modules/.vite/deps/svelte_reactivity_window.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_store.js +0 -103
- package/playground/node_modules/.vite/deps/svelte_store.js.map +0 -7
- package/playground/node_modules/.vite/deps/svelte_transition.js +0 -208
- package/playground/node_modules/.vite/deps/svelte_transition.js.map +0 -7
- package/playground/package.json +0 -16
- package/playground/pnpm-lock.yaml +0 -3167
- package/playground/src/content/authors/jane-doe.json +0 -8
- package/playground/src/content/config/build.toml +0 -2
- package/playground/src/content/courses/web-fundamentals.json +0 -29
- package/playground/src/content/docs/advanced.mdx +0 -6
- package/playground/src/content/docs/intro.md +0 -6
- package/playground/src/content/guides/getting-started.mdx +0 -6
- package/playground/src/content/posts/hello-world.md +0 -7
- package/playground/src/content/products/t-shirt.json +0 -16
- package/playground/src/content/recipes/pancakes.mdoc +0 -8
- package/playground/src/content/settings/site.yml +0 -2
- package/playground/src/content.config.ts +0 -198
- package/playground/src/env.d.ts +0 -1
- package/playground/src/pages/index.astro +0 -11
- package/playground/src/pages/nebula.astro +0 -14
- package/pnpm-workspace.yaml +0 -2
- package/scripts/subset-icons.mjs +0 -178
- package/src/astro/index.ts +0 -295
- package/src/client/js/drafts/merge.svelte.ts +0 -121
- package/src/client/js/drafts/ops.svelte.ts +0 -227
- package/src/client/js/drafts/storage.ts +0 -108
- package/src/client/js/drafts/workers/diff.ts +0 -40
- package/src/client/js/editor/editor.svelte.ts +0 -343
- package/src/client/js/editor/languages.ts +0 -98
- package/src/client/js/editor/markdown-shortcuts.ts +0 -261
- package/src/client/js/handlers/admin.ts +0 -246
- package/src/client/js/state/dialogs.svelte.ts +0 -35
- package/src/client/js/state/router.svelte.ts +0 -156
- package/src/client/js/state/state.svelte.ts +0 -334
- package/src/client/js/storage/adapter.ts +0 -102
- package/src/client/js/storage/client.ts +0 -150
- package/src/client/js/storage/db.ts +0 -36
- package/src/client/js/storage/fsa.ts +0 -110
- package/src/client/js/storage/github.ts +0 -297
- package/src/client/js/storage/storage.ts +0 -83
- package/src/client/js/storage/workers/frontmatter.ts +0 -320
- package/src/client/js/storage/workers/storage.ts +0 -177
- package/src/client/js/storage/workers/toml-parser.ts +0 -106
- package/src/client/js/storage/workers/yaml-parser.ts +0 -132
- package/src/client/js/utils/frontmatter.ts +0 -38
- package/src/client/js/utils/schema-utils.ts +0 -295
- package/src/client/js/utils/sort.ts +0 -84
- package/src/client/js/utils/stable-stringify.ts +0 -27
- package/src/types.ts +0 -25
- package/svelte.config.js +0 -4
- package/tests/astro/build.test.ts +0 -63
- package/tests/astro/index.test.ts +0 -689
- package/tests/client/components/Admin.test.ts +0 -446
- package/tests/client/components/BackendPicker.test.ts +0 -239
- package/tests/client/components/DraftChip.test.ts +0 -53
- package/tests/client/components/MetadataForm.test.ts +0 -164
- package/tests/client/components/dialogs/DeleteDraftDialog.test.ts +0 -91
- package/tests/client/components/dialogs/FilenameDialog.test.ts +0 -209
- package/tests/client/components/dialogs/dialog-stubs.ts +0 -19
- package/tests/client/components/editor/EditorPane.test.ts +0 -100
- package/tests/client/components/editor/EditorTabs.test.ts +0 -253
- package/tests/client/components/editor/EditorToolbar.test.ts +0 -252
- package/tests/client/components/editor/fixtures.ts +0 -31
- package/tests/client/components/fields/ArrayField.test.ts +0 -197
- package/tests/client/components/fields/BooleanField.test.ts +0 -206
- package/tests/client/components/fields/DateField.test.ts +0 -210
- package/tests/client/components/fields/EnumField.test.ts +0 -246
- package/tests/client/components/fields/NumberField.test.ts +0 -240
- package/tests/client/components/fields/ObjectField.test.ts +0 -157
- package/tests/client/components/fields/SchemaField.test.ts +0 -190
- package/tests/client/components/fields/StringField.test.ts +0 -223
- package/tests/client/components/sidebar/AdminSidebar.test.ts +0 -285
- package/tests/client/components/sidebar/AdminSidebarSort.test.ts +0 -135
- package/tests/client/components/sidebar/sort-mock.ts +0 -23
- package/tests/client/js/drafts/fixtures.ts +0 -22
- package/tests/client/js/drafts/merge.test.ts +0 -282
- package/tests/client/js/drafts/ops.test.ts +0 -658
- package/tests/client/js/drafts/storage.test.ts +0 -200
- package/tests/client/js/drafts/workers/diff.test.ts +0 -165
- package/tests/client/js/editor/editor.test.ts +0 -616
- package/tests/client/js/editor/link-wrap.test.ts +0 -225
- package/tests/client/js/editor/markdown-shortcuts.test.ts +0 -370
- package/tests/client/js/handlers/admin.test.ts +0 -467
- package/tests/client/js/state/router.test.ts +0 -619
- package/tests/client/js/state/schema.test.ts +0 -266
- package/tests/client/js/state/state.test.ts +0 -328
- package/tests/client/js/storage/adapter.test.ts +0 -115
- package/tests/client/js/storage/client.test.ts +0 -250
- package/tests/client/js/storage/db.test.ts +0 -59
- package/tests/client/js/storage/fsa.test.ts +0 -284
- package/tests/client/js/storage/github.test.ts +0 -349
- package/tests/client/js/storage/mock-port.ts +0 -95
- package/tests/client/js/storage/storage.test.ts +0 -77
- package/tests/client/js/storage/workers/frontmatter.test.ts +0 -479
- package/tests/client/js/storage/workers/storage.test.ts +0 -299
- package/tests/client/js/storage/workers/toml-parser.test.ts +0 -169
- package/tests/client/js/storage/workers/yaml-parser.test.ts +0 -168
- package/tests/client/js/utils/file-types.test.ts +0 -268
- package/tests/client/js/utils/frontmatter.test.ts +0 -87
- package/tests/client/js/utils/schema-utils.test.ts +0 -318
- package/tests/client/js/utils/slug.test.ts +0 -58
- package/tests/client/js/utils/sort.test.ts +0 -276
- package/tests/client/js/utils/stable-stringify.test.ts +0 -68
- package/tests/client/js/utils/url-utils.test.ts +0 -70
- package/tests/e2e/backend-connection.test.ts +0 -301
- package/tests/e2e/draft-lifecycle.test.ts +0 -388
- package/tests/e2e/editing.test.ts +0 -355
- package/tests/e2e/github-adapter.test.ts +0 -330
- package/tests/e2e/helpers/mock-adapter.ts +0 -166
- package/tests/e2e/helpers/test-app.ts +0 -155
- package/tests/e2e/navigation.test.ts +0 -358
- package/tests/e2e/publishing.test.ts +0 -345
- package/tests/e2e/unsaved-changes.test.ts +0 -317
- package/tests/setup.ts +0 -2
- package/tests/stubs/codemirror.ts +0 -197
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -178
- /package/{src → dist}/client/components/BackendPicker.svelte +0 -0
- /package/{src → dist}/client/components/DraftChip.svelte +0 -0
- /package/{src → dist}/client/components/MetadataForm.svelte +0 -0
- /package/{src → dist}/client/components/ThemeToggle.svelte +0 -0
- /package/{src → dist}/client/components/dialogs/DeleteDraftDialog.svelte +0 -0
- /package/{src → dist}/client/components/editor/EditorPane.svelte +0 -0
- /package/{src → dist}/client/components/editor/EditorTabs.svelte +0 -0
- /package/{src → dist}/client/components/editor/EditorToolbar.svelte +0 -0
- /package/{src → dist}/client/components/editor/FormatSelector.svelte +0 -0
- /package/{src → dist}/client/components/editor/Toolbar.svelte +0 -0
- /package/{src → dist}/client/components/fields/ArrayField.svelte +0 -0
- /package/{src → dist}/client/components/fields/ArrayItem.svelte +0 -0
- /package/{src → dist}/client/components/fields/BooleanField.svelte +0 -0
- /package/{src → dist}/client/components/fields/DateField.svelte +0 -0
- /package/{src → dist}/client/components/fields/EnumField.svelte +0 -0
- /package/{src → dist}/client/components/fields/FieldWrapper.svelte +0 -0
- /package/{src → dist}/client/components/fields/NumberField.svelte +0 -0
- /package/{src → dist}/client/components/fields/ObjectField.svelte +0 -0
- /package/{src → dist}/client/components/fields/SchemaField.svelte +0 -0
- /package/{src → dist}/client/components/fields/StringField.svelte +0 -0
- /package/{src → dist}/client/components/sidebar/AdminSidebarSort.svelte +0 -0
- /package/{src → dist}/client/css/a11y.css +0 -0
- /package/{src → dist}/client/css/btn.css +0 -0
- /package/{src → dist}/client/css/dialog.css +0 -0
- /package/{src → dist}/client/css/field-input.css +0 -0
- /package/{src → dist}/client/css/reset.css +0 -0
- /package/{src → dist}/client/css/theme.css +0 -0
- /package/{src/client/index.ts → dist/client/index.js} +0 -0
- /package/{src → dist}/virtual.d.ts +0 -0
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Global application state for the admin SPA.
|
|
3
|
-
* Manages backend connection, schema loading, collections, and navigation state.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import schemas from 'virtual:nebula/collections';
|
|
7
|
-
import {
|
|
8
|
-
loadBackend,
|
|
9
|
-
saveBackend,
|
|
10
|
-
clearBackend,
|
|
11
|
-
type BackendConfig,
|
|
12
|
-
} from '../storage/storage';
|
|
13
|
-
import { StorageClient } from '../storage/client';
|
|
14
|
-
import { nav, navigate, adminPath } from './router.svelte';
|
|
15
|
-
import {
|
|
16
|
-
drafts,
|
|
17
|
-
mergeDrafts,
|
|
18
|
-
refreshDrafts,
|
|
19
|
-
resetDraftMerge,
|
|
20
|
-
} from '../drafts/merge.svelte';
|
|
21
|
-
import { getSchemaExtensions } from './schema.svelte';
|
|
22
|
-
|
|
23
|
-
export { drafts, refreshDrafts };
|
|
24
|
-
|
|
25
|
-
// Content item with full frontmatter data returned by the worker.
|
|
26
|
-
export type ContentItem = {
|
|
27
|
-
filename: string;
|
|
28
|
-
data: Record<string, unknown>;
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Permission state for the stored FSA directory handle.
|
|
32
|
-
type PermissionState = 'granted' | 'prompt' | 'denied';
|
|
33
|
-
// Backend type discriminator.
|
|
34
|
-
type BackendType = 'fsa' | 'github' | null;
|
|
35
|
-
// Sorted collection names derived from virtual:nebula/collections.
|
|
36
|
-
export const collections = Object.keys(schemas).sort();
|
|
37
|
-
/*
|
|
38
|
-
* Uses .js extension because svelte-package does not rewrite URL string literals;
|
|
39
|
-
* the dist output must reference the compiled .js file, not the source .ts file.
|
|
40
|
-
*/
|
|
41
|
-
const sharedWorker = new SharedWorker(
|
|
42
|
-
new URL('../storage/workers/storage.js', import.meta.url),
|
|
43
|
-
{ type: 'module', name: 'cms-storage' },
|
|
44
|
-
);
|
|
45
|
-
// Main-thread StorageClient for editor and draft-merge I/O.
|
|
46
|
-
export const storageClient = new StorageClient(sharedWorker.port);
|
|
47
|
-
let backendType = $state<BackendType>(null);
|
|
48
|
-
let backendReady = $state(false);
|
|
49
|
-
let permissionState = $state<PermissionState>('denied');
|
|
50
|
-
let contentList = $state<ContentItem[]>([]);
|
|
51
|
-
let error = $state<string | null>(null);
|
|
52
|
-
let loading = $state(false);
|
|
53
|
-
|
|
54
|
-
export const backend = {
|
|
55
|
-
// Active backend type, or null if not connected.
|
|
56
|
-
get type(): BackendType {
|
|
57
|
-
return backendType;
|
|
58
|
-
},
|
|
59
|
-
// True if a backend is initialized and ready.
|
|
60
|
-
get ready(): boolean {
|
|
61
|
-
return backendReady;
|
|
62
|
-
},
|
|
63
|
-
// FSA permission state for the re-auth flow in BackendPicker.
|
|
64
|
-
get permission(): PermissionState {
|
|
65
|
-
return permissionState;
|
|
66
|
-
},
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
export const content = {
|
|
70
|
-
// Parsed content items for the selected collection.
|
|
71
|
-
get list(): ContentItem[] {
|
|
72
|
-
return contentList;
|
|
73
|
-
},
|
|
74
|
-
// True if the frontmatter worker is actively parsing.
|
|
75
|
-
get loading(): boolean {
|
|
76
|
-
return loading;
|
|
77
|
-
},
|
|
78
|
-
// Error message for display in the sidebar.
|
|
79
|
-
get error(): string | null {
|
|
80
|
-
return error;
|
|
81
|
-
},
|
|
82
|
-
};
|
|
83
|
-
let worker: Worker | null = null;
|
|
84
|
-
let loadedCollection = '';
|
|
85
|
-
let initPromise: Promise<void> | null = null;
|
|
86
|
-
const contentCache = new Map<string, ContentItem[]>();
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Initializes the frontmatter worker and bridges a port to the SharedWorker.
|
|
90
|
-
* @return {Worker}
|
|
91
|
-
*/
|
|
92
|
-
function ensureWorker(): Worker {
|
|
93
|
-
if (worker) return worker;
|
|
94
|
-
// Uses .js extension because svelte-package does not rewrite URL string literals
|
|
95
|
-
worker = new Worker(
|
|
96
|
-
new URL('../storage/workers/frontmatter.js', import.meta.url),
|
|
97
|
-
{ type: 'module' },
|
|
98
|
-
);
|
|
99
|
-
worker.addEventListener('message', (event) => {
|
|
100
|
-
const data = event.data;
|
|
101
|
-
if (data.type === 'result') {
|
|
102
|
-
const forCollection = data.collection as string;
|
|
103
|
-
// Always cache under the correct collection
|
|
104
|
-
contentCache.set(forCollection, data.items);
|
|
105
|
-
// Only update the visible list if this result is for the current collection
|
|
106
|
-
if (forCollection === loadedCollection) {
|
|
107
|
-
contentList = data.items;
|
|
108
|
-
loading = false;
|
|
109
|
-
error = null;
|
|
110
|
-
if (backendReady) mergeDrafts(forCollection);
|
|
111
|
-
}
|
|
112
|
-
} else if (data.type === 'error') {
|
|
113
|
-
error = data.message;
|
|
114
|
-
loading = false;
|
|
115
|
-
contentList = [];
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
// Bridge a port so the frontmatter worker can talk to the storage SharedWorker
|
|
120
|
-
const channel = new MessageChannel();
|
|
121
|
-
worker.postMessage({ type: 'port' }, [channel.port1]);
|
|
122
|
-
sharedWorker.port.postMessage({ type: 'connect-port' }, [channel.port2]);
|
|
123
|
-
|
|
124
|
-
return worker;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Sends a parse request to the frontmatter worker. Refresh mode keeps current sidebar visible.
|
|
129
|
-
* @param {string} collection - The collection name to parse
|
|
130
|
-
* @param {boolean} refresh - If true, keep current contentList and skip loading state
|
|
131
|
-
* @return {Promise<void>}
|
|
132
|
-
*/
|
|
133
|
-
async function dispatchWorker(
|
|
134
|
-
collection: string,
|
|
135
|
-
refresh = false,
|
|
136
|
-
): Promise<void> {
|
|
137
|
-
if (!backendReady) return;
|
|
138
|
-
if (!refresh) {
|
|
139
|
-
loading = true;
|
|
140
|
-
contentList = [];
|
|
141
|
-
}
|
|
142
|
-
error = null;
|
|
143
|
-
// Wait for the SharedWorker adapter to be ready before dispatching
|
|
144
|
-
if (initPromise) await initPromise;
|
|
145
|
-
const w = ensureWorker();
|
|
146
|
-
const extensions = getSchemaExtensions(collection);
|
|
147
|
-
w.postMessage({ type: 'parse', collection, extensions });
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Restores a stored backend config from IndexedDB and initializes the SharedWorker.
|
|
152
|
-
* @return {Promise<void>}
|
|
153
|
-
*/
|
|
154
|
-
export async function restoreBackend(): Promise<void> {
|
|
155
|
-
try {
|
|
156
|
-
const config = await loadBackend();
|
|
157
|
-
if (!config) {
|
|
158
|
-
backendType = null;
|
|
159
|
-
backendReady = false;
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (config.type === 'fsa') {
|
|
164
|
-
// Check FSA permission state
|
|
165
|
-
const perm = await config.handle.queryPermission({ mode: 'readwrite' });
|
|
166
|
-
permissionState = perm;
|
|
167
|
-
backendType = 'fsa';
|
|
168
|
-
|
|
169
|
-
if (perm === 'granted') {
|
|
170
|
-
await storageClient.init({ type: 'init', backend: config });
|
|
171
|
-
backendReady = true;
|
|
172
|
-
navigateToFirstCollectionIfHome();
|
|
173
|
-
}
|
|
174
|
-
// If perm is 'prompt', BackendPicker shows re-auth button
|
|
175
|
-
} else {
|
|
176
|
-
// GitHub — show UI optimistically, validate in background
|
|
177
|
-
backendType = 'github';
|
|
178
|
-
backendReady = true;
|
|
179
|
-
navigateToFirstCollectionIfHome();
|
|
180
|
-
initPromise = storageClient
|
|
181
|
-
.init({ type: 'init', backend: config })
|
|
182
|
-
.catch(async () => {
|
|
183
|
-
await clearBackend();
|
|
184
|
-
backendType = null;
|
|
185
|
-
backendReady = false;
|
|
186
|
-
contentList = [];
|
|
187
|
-
contentCache.clear();
|
|
188
|
-
loadedCollection = '';
|
|
189
|
-
navigate(adminPath());
|
|
190
|
-
})
|
|
191
|
-
.finally(() => {
|
|
192
|
-
initPromise = null;
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
} catch {
|
|
196
|
-
backendType = null;
|
|
197
|
-
backendReady = false;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
/**
|
|
202
|
-
* Re-requests FSA permission. Must be called from a user gesture.
|
|
203
|
-
* @return {Promise<void>}
|
|
204
|
-
*/
|
|
205
|
-
export async function requestPermission(): Promise<void> {
|
|
206
|
-
const config = await loadBackend();
|
|
207
|
-
if (!config || config.type !== 'fsa') return;
|
|
208
|
-
try {
|
|
209
|
-
const perm = await config.handle.requestPermission({ mode: 'readwrite' });
|
|
210
|
-
permissionState = perm;
|
|
211
|
-
if (perm === 'granted') {
|
|
212
|
-
await storageClient.init({ type: 'init', backend: config });
|
|
213
|
-
backendReady = true;
|
|
214
|
-
navigateToFirstCollectionIfHome();
|
|
215
|
-
}
|
|
216
|
-
} catch {
|
|
217
|
-
permissionState = 'denied';
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
/**
|
|
222
|
-
* Opens the directory picker and initializes the FSA backend. Must be called from a user gesture.
|
|
223
|
-
* @return {Promise<void>}
|
|
224
|
-
*/
|
|
225
|
-
export async function pickDirectory(): Promise<void> {
|
|
226
|
-
try {
|
|
227
|
-
const handle = await window.showDirectoryPicker({ mode: 'readwrite' });
|
|
228
|
-
const config: BackendConfig = { type: 'fsa', handle };
|
|
229
|
-
await storageClient.init({ type: 'init', backend: config });
|
|
230
|
-
await saveBackend(config);
|
|
231
|
-
backendType = 'fsa';
|
|
232
|
-
permissionState = 'granted';
|
|
233
|
-
backendReady = true;
|
|
234
|
-
navigateToFirstCollectionIfHome();
|
|
235
|
-
} catch (err) {
|
|
236
|
-
if (err instanceof DOMException && err.name === 'AbortError') return;
|
|
237
|
-
error = err instanceof Error ? err.message : String(err);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Connects to a GitHub repository via PAT and persists the config.
|
|
243
|
-
* @param {string} token - GitHub Personal Access Token
|
|
244
|
-
* @param {string} repo - Repository in "owner/repo" format
|
|
245
|
-
* @return {Promise<void>}
|
|
246
|
-
*/
|
|
247
|
-
export async function connectGitHub(
|
|
248
|
-
token: string,
|
|
249
|
-
repo: string,
|
|
250
|
-
): Promise<void> {
|
|
251
|
-
const config: BackendConfig = { type: 'github', token, repo };
|
|
252
|
-
await storageClient.init({ type: 'init', backend: config });
|
|
253
|
-
await saveBackend(config);
|
|
254
|
-
backendType = 'github';
|
|
255
|
-
backendReady = true;
|
|
256
|
-
navigateToFirstCollectionIfHome();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
/**
|
|
260
|
-
* Disconnects the backend, clears credentials, and resets all state.
|
|
261
|
-
* @return {Promise<void>}
|
|
262
|
-
*/
|
|
263
|
-
export async function disconnect(): Promise<void> {
|
|
264
|
-
worker?.terminate();
|
|
265
|
-
worker = null;
|
|
266
|
-
resetDraftMerge();
|
|
267
|
-
await storageClient.teardown();
|
|
268
|
-
await clearBackend();
|
|
269
|
-
backendType = null;
|
|
270
|
-
backendReady = false;
|
|
271
|
-
permissionState = 'denied';
|
|
272
|
-
contentList = [];
|
|
273
|
-
contentCache.clear();
|
|
274
|
-
loadedCollection = '';
|
|
275
|
-
error = null;
|
|
276
|
-
loading = false;
|
|
277
|
-
navigate(adminPath());
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Navigates to the first collection if currently on the home view.
|
|
282
|
-
* @return {void}
|
|
283
|
-
*/
|
|
284
|
-
function navigateToFirstCollectionIfHome(): void {
|
|
285
|
-
if (nav.route.view === 'home' && collections.length > 0) {
|
|
286
|
-
navigate(adminPath(collections[0]));
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/**
|
|
291
|
-
* Loads a collection. Serves from cache instantly if available, then background refreshes.
|
|
292
|
-
* @param {string} collection - The collection name to load
|
|
293
|
-
* @return {void}
|
|
294
|
-
*/
|
|
295
|
-
export function loadCollection(collection: string): void {
|
|
296
|
-
if (collection === loadedCollection) return;
|
|
297
|
-
loadedCollection = collection;
|
|
298
|
-
const cached = contentCache.get(collection);
|
|
299
|
-
if (cached) {
|
|
300
|
-
// Serve cached items instantly, then refresh in background
|
|
301
|
-
contentList = cached;
|
|
302
|
-
refreshDrafts(collection);
|
|
303
|
-
dispatchWorker(collection, true);
|
|
304
|
-
} else {
|
|
305
|
-
dispatchWorker(collection);
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Forces a background reload, keeping the sidebar visible.
|
|
311
|
-
* @param {string} collection - The collection to reload
|
|
312
|
-
* @return {void}
|
|
313
|
-
*/
|
|
314
|
-
export function reloadCollection(collection: string): void {
|
|
315
|
-
loadedCollection = collection;
|
|
316
|
-
dispatchWorker(collection, true);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Optimistically updates a single item's frontmatter in the content list and cache.
|
|
321
|
-
* @param {string} filename - The filename to update
|
|
322
|
-
* @param {Record<string, unknown>} data - The new frontmatter data
|
|
323
|
-
* @return {void}
|
|
324
|
-
*/
|
|
325
|
-
export function updateContentItem(
|
|
326
|
-
filename: string,
|
|
327
|
-
data: Record<string, unknown>,
|
|
328
|
-
): void {
|
|
329
|
-
const updated = contentList.map((item) =>
|
|
330
|
-
item.filename === filename ? { ...item, data } : item,
|
|
331
|
-
);
|
|
332
|
-
contentList = updated;
|
|
333
|
-
if (loadedCollection) contentCache.set(loadedCollection, updated);
|
|
334
|
-
}
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Storage adapter interface and shared types.
|
|
3
|
-
* Defines the contract that FSA and GitHub storage backends implement.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// A single file's name and content as returned by listFiles.
|
|
7
|
-
export type FileEntry = {
|
|
8
|
-
filename: string;
|
|
9
|
-
content: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
// A file write target with collection path, filename, and content.
|
|
13
|
-
export type FileWrite = {
|
|
14
|
-
collection: string;
|
|
15
|
-
filename: string;
|
|
16
|
-
content: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
// Contract for storage backend adapters. Both FSA and GitHub adapters implement this.
|
|
20
|
-
export interface StorageAdapter {
|
|
21
|
-
/**
|
|
22
|
-
* Lists files in a collection matching the given extensions, returning their names and content.
|
|
23
|
-
* @param {string} collection - The collection name
|
|
24
|
-
* @param {string[]} extensions - File extensions to include (e.g. ['.md', '.mdx', '.yaml'])
|
|
25
|
-
* @return {Promise<FileEntry[]>} Array of filename + content pairs
|
|
26
|
-
*/
|
|
27
|
-
listFiles(collection: string, extensions: string[]): Promise<FileEntry[]>;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Deletes a single file from the collection. Used during file type conversion to remove the old file after the new one is written.
|
|
31
|
-
* @param {string} collection - The collection name
|
|
32
|
-
* @param {string} filename - The filename to delete
|
|
33
|
-
* @return {Promise<void>}
|
|
34
|
-
*/
|
|
35
|
-
deleteFile(collection: string, filename: string): Promise<void>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Reads a single file's content.
|
|
39
|
-
* @param {string} collection - The collection name
|
|
40
|
-
* @param {string} filename - The filename within the collection
|
|
41
|
-
* @return {Promise<string>} The file content as a string
|
|
42
|
-
*/
|
|
43
|
-
readFile(collection: string, filename: string): Promise<string>;
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Writes content to a single file, creating it if it doesn't exist.
|
|
47
|
-
* @param {string} collection - The collection name
|
|
48
|
-
* @param {string} filename - The filename within the collection
|
|
49
|
-
* @param {string} content - The content to write
|
|
50
|
-
* @return {Promise<void>}
|
|
51
|
-
*/
|
|
52
|
-
writeFile(
|
|
53
|
-
collection: string,
|
|
54
|
-
filename: string,
|
|
55
|
-
content: string,
|
|
56
|
-
): Promise<void>;
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Writes multiple files atomically (single commit for GitHub, sequential for FSA).
|
|
60
|
-
* @param {FileWrite[]} files - Array of files to write
|
|
61
|
-
* @return {Promise<void>}
|
|
62
|
-
*/
|
|
63
|
-
writeFiles(files: FileWrite[]): Promise<void>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/*
|
|
67
|
-
//////////////////////////////
|
|
68
|
-
// Message types for SharedWorker communication
|
|
69
|
-
//////////////////////////////
|
|
70
|
-
*/
|
|
71
|
-
|
|
72
|
-
// Union of all request messages that can be sent to the storage SharedWorker.
|
|
73
|
-
export type StorageRequest =
|
|
74
|
-
| {
|
|
75
|
-
type: 'init';
|
|
76
|
-
backend:
|
|
77
|
-
| { type: 'fsa'; handle: FileSystemDirectoryHandle }
|
|
78
|
-
| { type: 'github'; token: string; repo: string };
|
|
79
|
-
}
|
|
80
|
-
| { type: 'listFiles'; collection: string; extensions: string[] }
|
|
81
|
-
| { type: 'readFile'; collection: string; filename: string }
|
|
82
|
-
| { type: 'writeFile'; collection: string; filename: string; content: string }
|
|
83
|
-
| { type: 'writeFiles'; files: FileWrite[] }
|
|
84
|
-
| { type: 'deleteFile'; collection: string; filename: string }
|
|
85
|
-
| { type: 'teardown' };
|
|
86
|
-
|
|
87
|
-
// Union of all response messages from the storage SharedWorker.
|
|
88
|
-
export type StorageResponse =
|
|
89
|
-
| { type: 'init'; ok: true }
|
|
90
|
-
| { type: 'init'; ok: false; error: string }
|
|
91
|
-
| { type: 'listFiles'; ok: true; files: FileEntry[] }
|
|
92
|
-
| { type: 'listFiles'; ok: false; error: string }
|
|
93
|
-
| { type: 'readFile'; ok: true; content: string }
|
|
94
|
-
| { type: 'readFile'; ok: false; error: string }
|
|
95
|
-
| { type: 'writeFile'; ok: true }
|
|
96
|
-
| { type: 'writeFile'; ok: false; error: string }
|
|
97
|
-
| { type: 'writeFiles'; ok: true }
|
|
98
|
-
| { type: 'writeFiles'; ok: false; error: string }
|
|
99
|
-
| { type: 'deleteFile'; ok: true }
|
|
100
|
-
| { type: 'deleteFile'; ok: false; error: string }
|
|
101
|
-
| { type: 'teardown'; ok: true }
|
|
102
|
-
| { type: 'port-connected' };
|
|
@@ -1,150 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Typed client for communicating with the storage SharedWorker over a MessagePort.
|
|
3
|
-
* Wraps postMessage/onmessage into async request/response calls.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import type {
|
|
7
|
-
StorageRequest,
|
|
8
|
-
StorageResponse,
|
|
9
|
-
FileEntry,
|
|
10
|
-
FileWrite,
|
|
11
|
-
} from './adapter';
|
|
12
|
-
|
|
13
|
-
export class StorageClient {
|
|
14
|
-
private port: MessagePort;
|
|
15
|
-
private pending = new Map<
|
|
16
|
-
string,
|
|
17
|
-
{ resolve: (v: any) => void; reject: (e: Error) => void }
|
|
18
|
-
>();
|
|
19
|
-
private idCounter = 0;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Creates a client wrapping the given port.
|
|
23
|
-
* @param {MessagePort} port - The port connected to the storage SharedWorker
|
|
24
|
-
*/
|
|
25
|
-
constructor(port: MessagePort) {
|
|
26
|
-
this.port = port;
|
|
27
|
-
this.port.addEventListener('message', (event) => {
|
|
28
|
-
this.handleResponse(event.data);
|
|
29
|
-
});
|
|
30
|
-
this.port.start();
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Routes responses to their pending promises. Responses without an _id are broadcast (like port-connected) and are ignored here.
|
|
35
|
-
* @param {StorageResponse & { _id?: string }} data - The response data
|
|
36
|
-
* @return {void}
|
|
37
|
-
*/
|
|
38
|
-
private handleResponse(data: StorageResponse & { _id?: string }): void {
|
|
39
|
-
if (!data._id) return;
|
|
40
|
-
const entry = this.pending.get(data._id);
|
|
41
|
-
if (!entry) return;
|
|
42
|
-
this.pending.delete(data._id);
|
|
43
|
-
if ('ok' in data && data.ok === false && 'error' in data) {
|
|
44
|
-
entry.reject(new Error((data as any).error));
|
|
45
|
-
} else {
|
|
46
|
-
entry.resolve(data);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Sends a request and waits for the matching response.
|
|
52
|
-
* @param {StorageRequest} msg - The request to send
|
|
53
|
-
* @return {Promise<StorageResponse>} The matched response
|
|
54
|
-
*/
|
|
55
|
-
private send<T extends StorageResponse>(msg: StorageRequest): Promise<T> {
|
|
56
|
-
const _id = String(++this.idCounter);
|
|
57
|
-
return new Promise((resolve, reject) => {
|
|
58
|
-
this.pending.set(_id, { resolve, reject });
|
|
59
|
-
this.port.postMessage({ ...msg, _id });
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Initializes the backend adapter in the SharedWorker.
|
|
65
|
-
* @param {StorageRequest & { type: 'init' }} config - The init config
|
|
66
|
-
* @return {Promise<void>}
|
|
67
|
-
*/
|
|
68
|
-
async init(config: StorageRequest & { type: 'init' }): Promise<void> {
|
|
69
|
-
await this.send(config);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* Lists files in a collection matching the given extensions.
|
|
74
|
-
* @param {string} collection - The collection name
|
|
75
|
-
* @param {string[]} extensions - File extensions to include (e.g. ['.md', '.mdx'])
|
|
76
|
-
* @return {Promise<FileEntry[]>} Array of file entries
|
|
77
|
-
*/
|
|
78
|
-
async listFiles(
|
|
79
|
-
collection: string,
|
|
80
|
-
extensions: string[],
|
|
81
|
-
): Promise<FileEntry[]> {
|
|
82
|
-
const res = await this.send<
|
|
83
|
-
Extract<StorageResponse, { type: 'listFiles'; ok: true }>
|
|
84
|
-
>({
|
|
85
|
-
type: 'listFiles',
|
|
86
|
-
collection,
|
|
87
|
-
extensions,
|
|
88
|
-
});
|
|
89
|
-
return res.files;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Reads a single file's content.
|
|
94
|
-
* @param {string} collection - The collection name
|
|
95
|
-
* @param {string} filename - The filename
|
|
96
|
-
* @return {Promise<string>} The file content
|
|
97
|
-
*/
|
|
98
|
-
async readFile(collection: string, filename: string): Promise<string> {
|
|
99
|
-
const res = await this.send<
|
|
100
|
-
Extract<StorageResponse, { type: 'readFile'; ok: true }>
|
|
101
|
-
>({
|
|
102
|
-
type: 'readFile',
|
|
103
|
-
collection,
|
|
104
|
-
filename,
|
|
105
|
-
});
|
|
106
|
-
return res.content;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Writes a single file.
|
|
111
|
-
* @param {string} collection - The collection name
|
|
112
|
-
* @param {string} filename - The filename
|
|
113
|
-
* @param {string} content - The content to write
|
|
114
|
-
* @return {Promise<void>}
|
|
115
|
-
*/
|
|
116
|
-
async writeFile(
|
|
117
|
-
collection: string,
|
|
118
|
-
filename: string,
|
|
119
|
-
content: string,
|
|
120
|
-
): Promise<void> {
|
|
121
|
-
await this.send({ type: 'writeFile', collection, filename, content });
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Writes multiple files atomically.
|
|
126
|
-
* @param {FileWrite[]} files - Array of files to write
|
|
127
|
-
* @return {Promise<void>}
|
|
128
|
-
*/
|
|
129
|
-
async writeFiles(files: FileWrite[]): Promise<void> {
|
|
130
|
-
await this.send({ type: 'writeFiles', files });
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/**
|
|
134
|
-
* Deletes a file from a collection. Used during file type conversion to remove the old file after the new one is written.
|
|
135
|
-
* @param {string} collection - The collection name
|
|
136
|
-
* @param {string} filename - The filename to delete
|
|
137
|
-
* @return {Promise<void>}
|
|
138
|
-
*/
|
|
139
|
-
async deleteFile(collection: string, filename: string): Promise<void> {
|
|
140
|
-
await this.send({ type: 'deleteFile', collection, filename });
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Tears down the active backend adapter.
|
|
145
|
-
* @return {Promise<void>}
|
|
146
|
-
*/
|
|
147
|
-
async teardown(): Promise<void> {
|
|
148
|
-
await this.send({ type: 'teardown' });
|
|
149
|
-
}
|
|
150
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
// Database name for admin CMS persistence
|
|
2
|
-
const DB_NAME = 'cms-admin';
|
|
3
|
-
// Current database version — bumped from 1 to add the drafts store
|
|
4
|
-
const DB_VERSION = 2;
|
|
5
|
-
|
|
6
|
-
// Cached database promise — opened once, reused by all callers
|
|
7
|
-
let dbPromise: Promise<IDBDatabase> | null = null;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Opens (or returns the cached) shared IndexedDB database, creating or upgrading stores as needed.
|
|
11
|
-
* The connection is opened once and reused, avoiding a new indexedDB.open() round-trip per call.
|
|
12
|
-
* Version 1: handles store only. Version 2: adds drafts store.
|
|
13
|
-
* @return {Promise<IDBDatabase>} Promise resolving to the database instance
|
|
14
|
-
*/
|
|
15
|
-
export function openDB(): Promise<IDBDatabase> {
|
|
16
|
-
if (dbPromise) return dbPromise;
|
|
17
|
-
dbPromise = new Promise((resolve, reject) => {
|
|
18
|
-
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
19
|
-
request.onupgradeneeded = () => {
|
|
20
|
-
const db = request.result;
|
|
21
|
-
if (!db.objectStoreNames.contains('handles')) {
|
|
22
|
-
db.createObjectStore('handles');
|
|
23
|
-
}
|
|
24
|
-
if (!db.objectStoreNames.contains('drafts')) {
|
|
25
|
-
db.createObjectStore('drafts', { keyPath: 'id' });
|
|
26
|
-
}
|
|
27
|
-
};
|
|
28
|
-
request.onsuccess = () => resolve(request.result);
|
|
29
|
-
request.onerror = () => {
|
|
30
|
-
// Reset cache so the next caller retries instead of getting a stale rejection
|
|
31
|
-
dbPromise = null;
|
|
32
|
-
reject(request.error);
|
|
33
|
-
};
|
|
34
|
-
});
|
|
35
|
-
return dbPromise;
|
|
36
|
-
}
|