@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.24.0
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/dist/cli/checks/lighthouse.d.ts +26 -0
- package/dist/cli/checks/lighthouse.d.ts.map +1 -0
- package/dist/cli/checks/lighthouse.js +121 -0
- package/dist/cli/checks/lighthouse.js.map +1 -0
- package/dist/cli/checks/links.d.ts +12 -0
- package/dist/cli/checks/links.d.ts.map +1 -0
- package/dist/cli/checks/links.js +89 -0
- package/dist/cli/checks/links.js.map +1 -0
- package/dist/cli/checks/performance.d.ts +17 -0
- package/dist/cli/checks/performance.d.ts.map +1 -0
- package/dist/cli/checks/performance.js +61 -0
- package/dist/cli/checks/performance.js.map +1 -0
- package/dist/cli/commands/check.d.ts +3 -0
- package/dist/cli/commands/check.d.ts.map +1 -0
- package/dist/cli/commands/check.js +85 -0
- package/dist/cli/commands/check.js.map +1 -0
- package/dist/cli/create/index.d.ts +11 -1
- package/dist/cli/create/index.d.ts.map +1 -1
- package/dist/cli/create/index.js +135 -65
- package/dist/cli/create/index.js.map +1 -1
- package/dist/cli/create/templates/spa/package.json.tpl +5 -3
- package/dist/cli/create/templates/ssg/cer.config.ts.tpl +2 -0
- package/dist/cli/create/templates/ssg/package.json.tpl +5 -3
- package/dist/cli/create/templates/ssr/package.json.tpl +5 -3
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/plugin/build-ssg.d.ts +22 -6
- package/dist/plugin/build-ssg.d.ts.map +1 -1
- package/dist/plugin/build-ssg.js +227 -17
- package/dist/plugin/build-ssg.js.map +1 -1
- package/dist/plugin/content/parser.d.ts.map +1 -1
- package/dist/plugin/content/parser.js +54 -10
- package/dist/plugin/content/parser.js.map +1 -1
- package/dist/plugin/dev-server.d.ts +4 -1
- package/dist/plugin/dev-server.d.ts.map +1 -1
- package/dist/plugin/dev-server.js.map +1 -1
- package/dist/plugin/dts-generator.d.ts.map +1 -1
- package/dist/plugin/dts-generator.js +50 -8
- package/dist/plugin/dts-generator.js.map +1 -1
- package/dist/plugin/generated-dir.d.ts +2 -0
- package/dist/plugin/generated-dir.d.ts.map +1 -1
- package/dist/plugin/generated-dir.js +17 -6
- package/dist/plugin/generated-dir.js.map +1 -1
- package/dist/plugin/html-post-process.d.ts +6 -0
- package/dist/plugin/html-post-process.d.ts.map +1 -1
- package/dist/plugin/html-post-process.js +24 -0
- package/dist/plugin/html-post-process.js.map +1 -1
- package/dist/plugin/index.d.ts.map +1 -1
- package/dist/plugin/index.js +60 -13
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/transforms/auto-import.d.ts.map +1 -1
- package/dist/plugin/transforms/auto-import.js +7 -0
- package/dist/plugin/transforms/auto-import.js.map +1 -1
- package/dist/plugin/transforms/prune-runtime-css.d.ts +8 -0
- package/dist/plugin/transforms/prune-runtime-css.d.ts.map +1 -0
- package/dist/plugin/transforms/prune-runtime-css.js +40 -0
- package/dist/plugin/transforms/prune-runtime-css.js.map +1 -0
- package/dist/runtime/app-template.d.ts +1 -1
- package/dist/runtime/app-template.d.ts.map +1 -1
- package/dist/runtime/app-template.js +206 -36
- package/dist/runtime/app-template.js.map +1 -1
- package/dist/runtime/composables/index.d.ts +4 -0
- package/dist/runtime/composables/index.d.ts.map +1 -1
- package/dist/runtime/composables/index.js +2 -0
- package/dist/runtime/composables/index.js.map +1 -1
- package/dist/runtime/composables/use-active-headings.d.ts +24 -0
- package/dist/runtime/composables/use-active-headings.d.ts.map +1 -0
- package/dist/runtime/composables/use-active-headings.js +112 -0
- package/dist/runtime/composables/use-active-headings.js.map +1 -0
- package/dist/runtime/composables/use-content-page.d.ts +45 -0
- package/dist/runtime/composables/use-content-page.d.ts.map +1 -0
- package/dist/runtime/composables/use-content-page.js +179 -0
- package/dist/runtime/composables/use-content-page.js.map +1 -0
- package/dist/runtime/composables/use-content-search.d.ts.map +1 -1
- package/dist/runtime/composables/use-content-search.js +1 -5
- package/dist/runtime/composables/use-content-search.js.map +1 -1
- package/dist/runtime/composables/use-fetch.d.ts +2 -1
- package/dist/runtime/composables/use-fetch.d.ts.map +1 -1
- package/dist/runtime/composables/use-fetch.js +112 -43
- package/dist/runtime/composables/use-fetch.js.map +1 -1
- package/dist/runtime/composables/use-route.d.ts.map +1 -1
- package/dist/runtime/composables/use-route.js +39 -11
- package/dist/runtime/composables/use-route.js.map +1 -1
- package/dist/runtime/content/client.js +2 -2
- package/dist/runtime/content/client.js.map +1 -1
- package/dist/runtime/entry-server-template.d.ts +16 -1
- package/dist/runtime/entry-server-template.d.ts.map +1 -1
- package/dist/runtime/entry-server-template.js +120 -13
- package/dist/runtime/entry-server-template.js.map +1 -1
- package/dist/types/config.d.ts +45 -4
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js.map +1 -1
- package/dist/types/middleware.d.ts +2 -2
- package/dist/types/middleware.d.ts.map +1 -1
- package/package.json +22 -19
- package/.github/copilot-instructions.md +0 -169
- package/.github/workflows/publish.yml +0 -257
- package/.nvmrc +0 -1
- package/CHANGELOG.md +0 -236
- package/CLAUDE.md +0 -113
- package/commits.txt +0 -1
- package/cypress.config.ts +0 -16
- package/docs/authentication.md +0 -285
- package/docs/cli.md +0 -324
- package/docs/components.md +0 -237
- package/docs/composables.md +0 -907
- package/docs/configuration.md +0 -696
- package/docs/content.md +0 -570
- package/docs/data-loading.md +0 -336
- package/docs/getting-started.md +0 -237
- package/docs/head-management.md +0 -212
- package/docs/i18n.md +0 -159
- package/docs/layouts.md +0 -224
- package/docs/middleware.md +0 -234
- package/docs/plugins.md +0 -170
- package/docs/rendering-modes.md +0 -377
- package/docs/routing.md +0 -485
- package/docs/server-api.md +0 -253
- package/docs/testing.md +0 -455
- package/docs/use-fetch.md +0 -183
- package/e2e/cypress/e2e/api.cy.ts +0 -123
- package/e2e/cypress/e2e/async-component.cy.ts +0 -27
- package/e2e/cypress/e2e/component-splitting.cy.ts +0 -50
- package/e2e/cypress/e2e/content.cy.ts +0 -425
- package/e2e/cypress/e2e/cookie.cy.ts +0 -85
- package/e2e/cypress/e2e/data.cy.ts +0 -158
- package/e2e/cypress/e2e/error-boundary.cy.ts +0 -88
- package/e2e/cypress/e2e/fouc.cy.ts +0 -254
- package/e2e/cypress/e2e/group-meta.cy.ts +0 -36
- package/e2e/cypress/e2e/head.cy.ts +0 -89
- package/e2e/cypress/e2e/i18n.cy.ts +0 -79
- package/e2e/cypress/e2e/interactive.cy.ts +0 -137
- package/e2e/cypress/e2e/isr-nested-runtime.cy.ts +0 -102
- package/e2e/cypress/e2e/jit-css-custom-colors.cy.ts +0 -43
- package/e2e/cypress/e2e/middleware-chain.cy.ts +0 -89
- package/e2e/cypress/e2e/middleware-status.cy.ts +0 -30
- package/e2e/cypress/e2e/middleware.cy.ts +0 -45
- package/e2e/cypress/e2e/navigate.cy.ts +0 -39
- package/e2e/cypress/e2e/observability.cy.ts +0 -77
- package/e2e/cypress/e2e/per-route-error.cy.ts +0 -42
- package/e2e/cypress/e2e/per-route-render.cy.ts +0 -70
- package/e2e/cypress/e2e/preview-hardening.cy.ts +0 -88
- package/e2e/cypress/e2e/routes.cy.ts +0 -128
- package/e2e/cypress/e2e/seo-meta.cy.ts +0 -108
- package/e2e/cypress/e2e/server-middleware.cy.ts +0 -46
- package/e2e/cypress/e2e/session-rotation.cy.ts +0 -25
- package/e2e/cypress/e2e/session.cy.ts +0 -73
- package/e2e/cypress/e2e/ssr-render-error.cy.ts +0 -68
- package/e2e/cypress/e2e/synthetic-404.cy.ts +0 -45
- package/e2e/cypress/e2e/use-page-data.cy.ts +0 -122
- package/e2e/cypress/e2e/use-state.cy.ts +0 -138
- package/e2e/cypress/support/commands.ts +0 -120
- package/e2e/cypress/support/e2e.ts +0 -10
- package/e2e/cypress/tsconfig.json +0 -14
- package/e2e/kitchen-sink/app/components/ks-badge.ts +0 -8
- package/e2e/kitchen-sink/app/composables/useKsCounter.ts +0 -9
- package/e2e/kitchen-sink/app/error.ts +0 -18
- package/e2e/kitchen-sink/app/layouts/admin.ts +0 -13
- package/e2e/kitchen-sink/app/layouts/default.ts +0 -25
- package/e2e/kitchen-sink/app/layouts/group.ts +0 -9
- package/e2e/kitchen-sink/app/layouts/minimal.ts +0 -7
- package/e2e/kitchen-sink/app/loading.ts +0 -9
- package/e2e/kitchen-sink/app/middleware/auth.ts +0 -9
- package/e2e/kitchen-sink/app/middleware/custom-log.ts +0 -4
- package/e2e/kitchen-sink/app/middleware/group-auth.ts +0 -7
- package/e2e/kitchen-sink/app/middleware/logger.ts +0 -14
- package/e2e/kitchen-sink/app/pages/(auth)/login.ts +0 -13
- package/e2e/kitchen-sink/app/pages/(auth)/protected.ts +0 -16
- package/e2e/kitchen-sink/app/pages/404.ts +0 -9
- package/e2e/kitchen-sink/app/pages/about.ts +0 -17
- package/e2e/kitchen-sink/app/pages/admin/_layout.ts +0 -1
- package/e2e/kitchen-sink/app/pages/admin/dashboard.ts +0 -11
- package/e2e/kitchen-sink/app/pages/async-component-error-test.ts +0 -18
- package/e2e/kitchen-sink/app/pages/async-component-test.ts +0 -18
- package/e2e/kitchen-sink/app/pages/blog/[slug].ts +0 -59
- package/e2e/kitchen-sink/app/pages/blog/index.ts +0 -51
- package/e2e/kitchen-sink/app/pages/content-blog.ts +0 -37
- package/e2e/kitchen-sink/app/pages/content-doc.ts +0 -42
- package/e2e/kitchen-sink/app/pages/content-fallback.ts +0 -36
- package/e2e/kitchen-sink/app/pages/content-guides.ts +0 -35
- package/e2e/kitchen-sink/app/pages/content-index.ts +0 -39
- package/e2e/kitchen-sink/app/pages/content-search.ts +0 -36
- package/e2e/kitchen-sink/app/pages/cookie-test.ts +0 -22
- package/e2e/kitchen-sink/app/pages/counter.ts +0 -17
- package/e2e/kitchen-sink/app/pages/group-auth-blocked.ts +0 -9
- package/e2e/kitchen-sink/app/pages/group-meta-test/_layout.ts +0 -6
- package/e2e/kitchen-sink/app/pages/group-meta-test/custom-mw.ts +0 -14
- package/e2e/kitchen-sink/app/pages/group-meta-test/page.ts +0 -9
- package/e2e/kitchen-sink/app/pages/group-meta-test/protected.ts +0 -8
- package/e2e/kitchen-sink/app/pages/head.ts +0 -20
- package/e2e/kitchen-sink/app/pages/i18n-test.ts +0 -14
- package/e2e/kitchen-sink/app/pages/index.ts +0 -27
- package/e2e/kitchen-sink/app/pages/isr-test.ts +0 -17
- package/e2e/kitchen-sink/app/pages/items/[id].ts +0 -20
- package/e2e/kitchen-sink/app/pages/jit-css-custom-colors-test.ts +0 -14
- package/e2e/kitchen-sink/app/pages/loader-error-test.ts +0 -13
- package/e2e/kitchen-sink/app/pages/loader-props-test.ts +0 -19
- package/e2e/kitchen-sink/app/pages/loader-response-error-test.ts +0 -13
- package/e2e/kitchen-sink/app/pages/middleware-chain-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/middleware-status-test.ts +0 -9
- package/e2e/kitchen-sink/app/pages/navigate-test.ts +0 -16
- package/e2e/kitchen-sink/app/pages/observability-test.ts +0 -25
- package/e2e/kitchen-sink/app/pages/per-route-error-test.error.ts +0 -13
- package/e2e/kitchen-sink/app/pages/per-route-error-test.ts +0 -15
- package/e2e/kitchen-sink/app/pages/plugin-dsd-test.ts +0 -10
- package/e2e/kitchen-sink/app/pages/render-error-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/render-server-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/render-spa-test.ts +0 -12
- package/e2e/kitchen-sink/app/pages/route-info.ts +0 -14
- package/e2e/kitchen-sink/app/pages/seo-test.ts +0 -23
- package/e2e/kitchen-sink/app/pages/session-rotation-test.ts +0 -25
- package/e2e/kitchen-sink/app/pages/use-state-test.ts +0 -37
- package/e2e/kitchen-sink/app/plugins/01.setup.ts +0 -7
- package/e2e/kitchen-sink/app/plugins/02.ks-plugin-components.ts +0 -27
- package/e2e/kitchen-sink/cer-auto-imports.d.ts +0 -51
- package/e2e/kitchen-sink/cer-env.d.ts +0 -35
- package/e2e/kitchen-sink/cer-tsconfig.json +0 -30
- package/e2e/kitchen-sink/cer.config.ts +0 -43
- package/e2e/kitchen-sink/content/01.guides/01.index.md +0 -8
- package/e2e/kitchen-sink/content/01.guides/02.intro.md +0 -8
- package/e2e/kitchen-sink/content/01.guides/10.advanced.md +0 -8
- package/e2e/kitchen-sink/content/blog/2026-04-01-hello.md +0 -26
- package/e2e/kitchen-sink/content/blog/2026-04-02-draft.md +0 -10
- package/e2e/kitchen-sink/content/blog/index.md +0 -8
- package/e2e/kitchen-sink/content/blog/no-frontmatter.md +0 -7
- package/e2e/kitchen-sink/content/docs/getting-started.md +0 -48
- package/e2e/kitchen-sink/content/index.md +0 -16
- package/e2e/kitchen-sink/server/api/echo.ts +0 -12
- package/e2e/kitchen-sink/server/api/health.ts +0 -3
- package/e2e/kitchen-sink/server/api/posts/[slug].ts +0 -11
- package/e2e/kitchen-sink/server/api/posts/index.ts +0 -5
- package/e2e/kitchen-sink/server/api/session.ts +0 -23
- package/e2e/kitchen-sink/server/data/posts.ts +0 -21
- package/e2e/kitchen-sink/server/middleware/01-headers.ts +0 -6
- package/e2e/kitchen-sink/server/middleware/02-status-test.ts +0 -10
- package/e2e/kitchen-sink/tsconfig.json +0 -3
- package/e2e/scripts/clean.mjs +0 -14
- package/eslint.config.ts +0 -22
- package/src/__tests__/cli/adapters/cloudflare-worker.integration.test.ts +0 -132
- package/src/__tests__/cli/adapters/cloudflare.test.ts +0 -332
- package/src/__tests__/cli/adapters/netlify-bridge.integration.test.ts +0 -139
- package/src/__tests__/cli/adapters/netlify.test.ts +0 -286
- package/src/__tests__/cli/adapters/vercel-launcher.integration.test.ts +0 -129
- package/src/__tests__/cli/adapters/vercel.test.ts +0 -263
- package/src/__tests__/cli/preview-hardening.test.ts +0 -265
- package/src/__tests__/cli/preview-isr.test.ts +0 -320
- package/src/__tests__/index.test.ts +0 -21
- package/src/__tests__/plugin/app-template.test.ts +0 -166
- package/src/__tests__/plugin/build-ssg-render.test.ts +0 -155
- package/src/__tests__/plugin/build-ssg.test.ts +0 -598
- package/src/__tests__/plugin/build-ssr.test.ts +0 -192
- package/src/__tests__/plugin/cer-app-plugin.test.ts +0 -552
- package/src/__tests__/plugin/content/emitter.test.ts +0 -117
- package/src/__tests__/plugin/content/loader.test.ts +0 -154
- package/src/__tests__/plugin/content/parser.test.ts +0 -381
- package/src/__tests__/plugin/content/path-utils.test.ts +0 -69
- package/src/__tests__/plugin/content/search.test.ts +0 -119
- package/src/__tests__/plugin/dev-server.test.ts +0 -813
- package/src/__tests__/plugin/dts-generator.test.ts +0 -394
- package/src/__tests__/plugin/entry-server-template.test.ts +0 -385
- package/src/__tests__/plugin/generated-dir.test.ts +0 -144
- package/src/__tests__/plugin/html-post-process.test.ts +0 -146
- package/src/__tests__/plugin/path-utils.test.ts +0 -411
- package/src/__tests__/plugin/resolve-config.test.ts +0 -233
- package/src/__tests__/plugin/scanner.test.ts +0 -186
- package/src/__tests__/plugin/transforms/auto-import.test.ts +0 -510
- package/src/__tests__/plugin/transforms/head-inject.test.ts +0 -178
- package/src/__tests__/plugin/virtual/composables.test.ts +0 -57
- package/src/__tests__/plugin/virtual/content-components.test.ts +0 -66
- package/src/__tests__/plugin/virtual/error.test.ts +0 -71
- package/src/__tests__/plugin/virtual/layouts.test.ts +0 -70
- package/src/__tests__/plugin/virtual/loading.test.ts +0 -72
- package/src/__tests__/plugin/virtual/middleware.test.ts +0 -83
- package/src/__tests__/plugin/virtual/plugins.test.ts +0 -129
- package/src/__tests__/plugin/virtual/routes.test.ts +0 -884
- package/src/__tests__/plugin/virtual/server-api.test.ts +0 -164
- package/src/__tests__/plugin/virtual/server-middleware.test.ts +0 -102
- package/src/__tests__/runtime/app-template.test.ts +0 -208
- package/src/__tests__/runtime/define-middleware.test.ts +0 -64
- package/src/__tests__/runtime/define-server-middleware.test.ts +0 -54
- package/src/__tests__/runtime/entry-client-template.test.ts +0 -61
- package/src/__tests__/runtime/isr-handler.test.ts +0 -338
- package/src/__tests__/runtime/oauth/handler.test.ts +0 -358
- package/src/__tests__/runtime/oauth/providers.test.ts +0 -47
- package/src/__tests__/runtime/oauth-index.test.ts +0 -43
- package/src/__tests__/runtime/use-auth.test.ts +0 -169
- package/src/__tests__/runtime/use-content-search-composable.test.ts +0 -405
- package/src/__tests__/runtime/use-content-search.test.ts +0 -163
- package/src/__tests__/runtime/use-content.test.ts +0 -226
- package/src/__tests__/runtime/use-cookie.test.ts +0 -277
- package/src/__tests__/runtime/use-fetch-component.test.ts +0 -214
- package/src/__tests__/runtime/use-fetch.test.ts +0 -377
- package/src/__tests__/runtime/use-head.test.ts +0 -311
- package/src/__tests__/runtime/use-inject-client.test.ts +0 -67
- package/src/__tests__/runtime/use-inject.test.ts +0 -66
- package/src/__tests__/runtime/use-locale.test.ts +0 -152
- package/src/__tests__/runtime/use-navigate.test.ts +0 -121
- package/src/__tests__/runtime/use-page-data.test.ts +0 -298
- package/src/__tests__/runtime/use-route.test.ts +0 -156
- package/src/__tests__/runtime/use-runtime-config.test.ts +0 -182
- package/src/__tests__/runtime/use-seo-meta.test.ts +0 -109
- package/src/__tests__/runtime/use-session.test.ts +0 -320
- package/src/__tests__/runtime/use-state.test.ts +0 -340
- package/src/__tests__/types/config.test.ts +0 -18
- package/src/cli/adapters/cloudflare.ts +0 -335
- package/src/cli/adapters/netlify.ts +0 -326
- package/src/cli/adapters/vercel.ts +0 -311
- package/src/cli/commands/adapt.ts +0 -84
- package/src/cli/commands/build.ts +0 -165
- package/src/cli/commands/dev.ts +0 -125
- package/src/cli/commands/generate.ts +0 -91
- package/src/cli/commands/preview-isr.ts +0 -172
- package/src/cli/commands/preview.ts +0 -523
- package/src/cli/create/index.ts +0 -275
- package/src/cli/create/templates/shared/.gitignore.tpl +0 -25
- package/src/cli/create/templates/shared/app/layouts/default.ts.tpl +0 -15
- package/src/cli/create/templates/shared/app/pages/index.ts.tpl +0 -8
- package/src/cli/create/templates/shared/index.html.tpl +0 -12
- package/src/cli/create/templates/shared/tsconfig.json.tpl +0 -6
- package/src/cli/create/templates/spa/cer.config.ts.tpl +0 -6
- package/src/cli/create/templates/spa/package.json.tpl +0 -18
- package/src/cli/create/templates/ssg/cer.config.ts.tpl +0 -10
- package/src/cli/create/templates/ssg/package.json.tpl +0 -19
- package/src/cli/create/templates/ssr/cer.config.ts.tpl +0 -6
- package/src/cli/create/templates/ssr/package.json.tpl +0 -18
- package/src/cli/index.ts +0 -22
- package/src/index.ts +0 -20
- package/src/plugin/build-ssg.ts +0 -403
- package/src/plugin/build-ssr.ts +0 -134
- package/src/plugin/content/emitter.ts +0 -50
- package/src/plugin/content/index.ts +0 -233
- package/src/plugin/content/parser.ts +0 -259
- package/src/plugin/content/path-utils.ts +0 -55
- package/src/plugin/content/scanner.ts +0 -26
- package/src/plugin/content/search.ts +0 -28
- package/src/plugin/dev-server.ts +0 -379
- package/src/plugin/dts-generator.ts +0 -273
- package/src/plugin/generated-dir.ts +0 -121
- package/src/plugin/html-post-process.ts +0 -96
- package/src/plugin/index.ts +0 -584
- package/src/plugin/path-utils.ts +0 -186
- package/src/plugin/scanner.ts +0 -65
- package/src/plugin/transforms/auto-import.ts +0 -206
- package/src/plugin/transforms/head-inject.ts +0 -161
- package/src/plugin/virtual/composables.ts +0 -28
- package/src/plugin/virtual/content-components.ts +0 -83
- package/src/plugin/virtual/error.ts +0 -34
- package/src/plugin/virtual/layouts.ts +0 -41
- package/src/plugin/virtual/loading.ts +0 -32
- package/src/plugin/virtual/middleware.ts +0 -45
- package/src/plugin/virtual/plugins.ts +0 -46
- package/src/plugin/virtual/routes.ts +0 -514
- package/src/plugin/virtual/server-api.ts +0 -85
- package/src/plugin/virtual/server-middleware.ts +0 -44
- package/src/runtime/app-template.ts +0 -377
- package/src/runtime/composables/define-middleware.ts +0 -17
- package/src/runtime/composables/define-server-middleware.ts +0 -18
- package/src/runtime/composables/index.ts +0 -27
- package/src/runtime/composables/use-auth.ts +0 -125
- package/src/runtime/composables/use-content-search.ts +0 -205
- package/src/runtime/composables/use-content.ts +0 -146
- package/src/runtime/composables/use-cookie.ts +0 -135
- package/src/runtime/composables/use-fetch.ts +0 -438
- package/src/runtime/composables/use-head.ts +0 -234
- package/src/runtime/composables/use-inject.ts +0 -49
- package/src/runtime/composables/use-locale.ts +0 -109
- package/src/runtime/composables/use-navigate.ts +0 -58
- package/src/runtime/composables/use-page-data.ts +0 -93
- package/src/runtime/composables/use-route.ts +0 -71
- package/src/runtime/composables/use-runtime-config.ts +0 -123
- package/src/runtime/composables/use-seo-meta.ts +0 -80
- package/src/runtime/composables/use-session.ts +0 -186
- package/src/runtime/composables/use-state.ts +0 -92
- package/src/runtime/content/client.ts +0 -168
- package/src/runtime/entry-client-template.ts +0 -39
- package/src/runtime/entry-server-template.ts +0 -493
- package/src/runtime/isr-handler.ts +0 -184
- package/src/runtime/oauth/handler.ts +0 -407
- package/src/runtime/oauth/index.ts +0 -49
- package/src/runtime/oauth/providers.ts +0 -38
- package/src/types/api.ts +0 -40
- package/src/types/config.ts +0 -379
- package/src/types/content.ts +0 -66
- package/src/types/index.ts +0 -7
- package/src/types/middleware.ts +0 -63
- package/src/types/page.ts +0 -117
- package/src/types/plugin.ts +0 -34
- package/tsconfig.build.json +0 -10
- package/tsconfig.json +0 -19
- package/vitest.config.ts +0 -33
|
@@ -1,205 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
createComposable,
|
|
3
|
-
getCurrentComponentContext,
|
|
4
|
-
ref,
|
|
5
|
-
useOnConnected,
|
|
6
|
-
useOnDisconnected,
|
|
7
|
-
watch,
|
|
8
|
-
} from '@jasonshimmy/custom-elements-runtime'
|
|
9
|
-
import type { ReactiveState } from '@jasonshimmy/custom-elements-runtime'
|
|
10
|
-
import type { ContentSearchResult } from '../../types/content.js'
|
|
11
|
-
import { contentSearchIndexUrl } from '../content/client.js'
|
|
12
|
-
|
|
13
|
-
// ─── Module-level singleton ───────────────────────────────────────────────────
|
|
14
|
-
|
|
15
|
-
// The MiniSearch instance is built lazily on first search from the manifest.
|
|
16
|
-
// The same promise is reused across all mounted instances so we only build once.
|
|
17
|
-
let _indexPromise: Promise<unknown> | null = null
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Lazily loads the MiniSearch index from `/_content/search-index.json`.
|
|
21
|
-
* Returns the same Promise on repeated calls — the index is built at most once
|
|
22
|
-
* per session regardless of how many search components are mounted.
|
|
23
|
-
*
|
|
24
|
-
* If the fetch fails the singleton is cleared so the next search attempt
|
|
25
|
-
* retries automatically (no page reload required after a transient error).
|
|
26
|
-
*
|
|
27
|
-
* @internal Exported for unit testing only.
|
|
28
|
-
*/
|
|
29
|
-
export function loadIndex(): Promise<unknown> {
|
|
30
|
-
if (_indexPromise) return _indexPromise
|
|
31
|
-
const attempt = (async () => {
|
|
32
|
-
const [{ default: MiniSearch }, raw] = await Promise.all([
|
|
33
|
-
import('minisearch'),
|
|
34
|
-
fetch(contentSearchIndexUrl()).then((r) => {
|
|
35
|
-
if (!r.ok) throw new Error(`Failed to fetch search index: ${r.status}`)
|
|
36
|
-
return r.text()
|
|
37
|
-
}),
|
|
38
|
-
])
|
|
39
|
-
return MiniSearch.loadJSON(raw, {
|
|
40
|
-
fields: ['title', 'description'],
|
|
41
|
-
storeFields: ['_path', 'title', 'description'],
|
|
42
|
-
idField: '_path',
|
|
43
|
-
})
|
|
44
|
-
})()
|
|
45
|
-
_indexPromise = attempt
|
|
46
|
-
// Clear the singleton on failure so the next call retries the fetch.
|
|
47
|
-
// The === guard ensures a newer concurrent attempt is not accidentally cleared.
|
|
48
|
-
attempt.catch(() => {
|
|
49
|
-
if (_indexPromise === attempt) _indexPromise = null
|
|
50
|
-
})
|
|
51
|
-
return attempt
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** Resets the module-level singleton. Used in tests only. @internal */
|
|
55
|
-
export function resetIndexSingleton(): void {
|
|
56
|
-
_indexPromise = null
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// ─── Per-component debounce state ────────────────────────────────────────────
|
|
60
|
-
|
|
61
|
-
// Stores the debounce timer handle and stale-seq counter directly on the
|
|
62
|
-
// component context object (non-enumerable, same pattern the runtime uses for
|
|
63
|
-
// _hookCallbacks). This makes the values stable across re-renders — the context
|
|
64
|
-
// object is fixed for the lifetime of the element instance — without leaking
|
|
65
|
-
// into the reactive proxy or triggering spurious updates.
|
|
66
|
-
|
|
67
|
-
interface DebounceState {
|
|
68
|
-
seq: number
|
|
69
|
-
timer: ReturnType<typeof setTimeout> | null
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
const _STATE_KEY = '_cerSearchDebounce'
|
|
73
|
-
|
|
74
|
-
function getDebounceState(ctx: Record<string, unknown>): DebounceState {
|
|
75
|
-
if (!Object.prototype.hasOwnProperty.call(ctx, _STATE_KEY)) {
|
|
76
|
-
Object.defineProperty(ctx, _STATE_KEY, {
|
|
77
|
-
value: { seq: 0, timer: null } as DebounceState,
|
|
78
|
-
writable: false, // object ref is fixed; its properties are still mutable
|
|
79
|
-
enumerable: false, // invisible to the reactive proxy set-trap
|
|
80
|
-
configurable: false,
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
return (ctx as Record<string, DebounceState>)[_STATE_KEY]
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// ─── Composable ───────────────────────────────────────────────────────────────
|
|
87
|
-
|
|
88
|
-
export interface UseContentSearchReturn {
|
|
89
|
-
query: ReactiveState<string>
|
|
90
|
-
results: ReactiveState<ContentSearchResult[]>
|
|
91
|
-
/** `true` from the moment the user starts typing until results (or an error) arrive. `false` when the query is empty or the search is complete. */
|
|
92
|
-
loading: ReactiveState<boolean>
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
const _factory = createComposable((): UseContentSearchReturn => {
|
|
96
|
-
const query = ref('')
|
|
97
|
-
const results = ref<ContentSearchResult[]>([])
|
|
98
|
-
const loading = ref(false)
|
|
99
|
-
|
|
100
|
-
// Debounce state lives on the component context, not in local render-body variables.
|
|
101
|
-
// Local variables are re-created on every re-render; the context object is stable
|
|
102
|
-
// for the lifetime of the element. Storing state here lets the render-body
|
|
103
|
-
// watch() (see below) pick up the same timer and sequence counter across re-renders
|
|
104
|
-
// without the watcher accumulation that occurs when watch() is placed inside
|
|
105
|
-
// useOnConnected() (which runs once per mount but is not registered for cleanup
|
|
106
|
-
// by the reactive system, leaking watchers on every disconnect + reconnect cycle).
|
|
107
|
-
const state = getDebounceState(getCurrentComponentContext()! as Record<string, unknown>)
|
|
108
|
-
|
|
109
|
-
// Pre-warm the index on first mount so the first real search is faster.
|
|
110
|
-
useOnConnected(() => {
|
|
111
|
-
loadIndex().catch(() => {/* silently ignore pre-warm errors */})
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
// Cancel any in-flight debounce on unmount so stale async work doesn't land
|
|
115
|
-
// after the component is gone.
|
|
116
|
-
useOnDisconnected(() => {
|
|
117
|
-
if (state.timer !== null) {
|
|
118
|
-
clearTimeout(state.timer)
|
|
119
|
-
state.timer = null
|
|
120
|
-
}
|
|
121
|
-
state.seq++ // discard any in-flight async search
|
|
122
|
-
loading.value = false
|
|
123
|
-
})
|
|
124
|
-
|
|
125
|
-
// watch() is in the render body so the reactive system registers it under the
|
|
126
|
-
// current component and tears it down automatically on re-render and disconnect.
|
|
127
|
-
// The mutable state (seq / timer) lives on the context (above) and persists
|
|
128
|
-
// across re-renders — new watcher instances see the same timer and counter,
|
|
129
|
-
// which is what makes debounce cancellation correct even after a re-render.
|
|
130
|
-
watch(query, (q: string) => {
|
|
131
|
-
if (state.timer !== null) {
|
|
132
|
-
clearTimeout(state.timer)
|
|
133
|
-
state.timer = null
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (!q) {
|
|
137
|
-
// Increment seq so any in-flight async search is discarded when it resolves
|
|
138
|
-
state.seq++
|
|
139
|
-
loading.value = false
|
|
140
|
-
results.value = []
|
|
141
|
-
return
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Signal loading immediately so the UI can respond before the debounce fires
|
|
145
|
-
loading.value = true
|
|
146
|
-
|
|
147
|
-
state.timer = setTimeout(async () => {
|
|
148
|
-
state.timer = null
|
|
149
|
-
const seq = ++state.seq
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const index = await loadIndex() as { search(q: string, opts?: { prefix?: boolean }): ContentSearchResult[] }
|
|
153
|
-
if (seq !== state.seq) return // stale — a newer query is in flight
|
|
154
|
-
results.value = index.search(q, { prefix: true }) as ContentSearchResult[]
|
|
155
|
-
} catch {
|
|
156
|
-
if (seq !== state.seq) return
|
|
157
|
-
results.value = []
|
|
158
|
-
} finally {
|
|
159
|
-
// Only clear loading for the most recent search; a newer in-flight search
|
|
160
|
-
// keeps loading=true until it settles.
|
|
161
|
-
if (seq === state.seq) loading.value = false
|
|
162
|
-
}
|
|
163
|
-
}, 200)
|
|
164
|
-
})
|
|
165
|
-
|
|
166
|
-
return { query, results, loading }
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
/**
|
|
170
|
-
* Full-text content search composable.
|
|
171
|
-
*
|
|
172
|
-
* Loads a pre-built MiniSearch index lazily on first use by fetching
|
|
173
|
-
* `/_content/search-index.json`. Both MiniSearch and the index are loaded via
|
|
174
|
-
* dynamic import — neither is in the app bundle.
|
|
175
|
-
*
|
|
176
|
-
* Searches `title` and `description` fields. Input is debounced (200 ms) so
|
|
177
|
-
* the index is not queried on every keystroke. `loading` becomes `true` as soon
|
|
178
|
-
* as the user starts typing and returns to `false` once results arrive. Results
|
|
179
|
-
* are empty when the query is empty.
|
|
180
|
-
*
|
|
181
|
-
* **SSR note**: search is always client-side. In SSR mode the component renders
|
|
182
|
-
* with empty results and hydrates on mount.
|
|
183
|
-
*
|
|
184
|
-
* @example
|
|
185
|
-
* ```ts
|
|
186
|
-
* component('site-search', () => {
|
|
187
|
-
* const { query, results, loading } = useContentSearch()
|
|
188
|
-
*
|
|
189
|
-
* return html`
|
|
190
|
-
* <input type="search" :model="${query}" placeholder="Search…" />
|
|
191
|
-
* ${loading.value ? html`<p>Searching…</p>` : ''}
|
|
192
|
-
* ${when(results.value.length > 0, () => html`
|
|
193
|
-
* <ul>
|
|
194
|
-
* ${each(results.value, r => html`
|
|
195
|
-
* <li><a :href="${r._path}">${r.title}</a></li>
|
|
196
|
-
* `)}
|
|
197
|
-
* </ul>
|
|
198
|
-
* `)}
|
|
199
|
-
* `
|
|
200
|
-
* })
|
|
201
|
-
* ```
|
|
202
|
-
*/
|
|
203
|
-
export function useContentSearch(): UseContentSearchReturn {
|
|
204
|
-
return _factory()
|
|
205
|
-
}
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import type { ContentItem, ContentMeta } from '../../types/content.js'
|
|
2
|
-
import { ContentClient } from '../content/client.js'
|
|
3
|
-
|
|
4
|
-
// ─── QueryBuilder ─────────────────────────────────────────────────────────────
|
|
5
|
-
|
|
6
|
-
export class QueryBuilder {
|
|
7
|
-
private _prefix: string | undefined
|
|
8
|
-
private _predicates: Array<(doc: ContentMeta) => boolean> = []
|
|
9
|
-
private _sortField: string | undefined
|
|
10
|
-
private _sortDir: 'asc' | 'desc' = 'asc'
|
|
11
|
-
private _limit: number | undefined
|
|
12
|
-
private _skip: number | undefined
|
|
13
|
-
|
|
14
|
-
constructor(prefix?: string) {
|
|
15
|
-
this._prefix = prefix
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/** Filter results by a predicate. Receives a fully-typed `ContentMeta`. */
|
|
19
|
-
where(predicate: (doc: ContentMeta) => boolean): this {
|
|
20
|
-
this._predicates.push(predicate)
|
|
21
|
-
return this
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Sort by a frontmatter field. Defaults to ascending order. */
|
|
25
|
-
sortBy(field: string, dir: 'asc' | 'desc' = 'asc'): this {
|
|
26
|
-
this._sortField = field
|
|
27
|
-
this._sortDir = dir
|
|
28
|
-
return this
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Cap the result count. */
|
|
32
|
-
limit(n: number): this {
|
|
33
|
-
this._limit = n
|
|
34
|
-
return this
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Skip the first `n` results (for pagination). */
|
|
38
|
-
skip(n: number): this {
|
|
39
|
-
this._skip = n
|
|
40
|
-
return this
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** Returns all matching `ContentMeta` items (no body loaded). */
|
|
44
|
-
async find(): Promise<ContentMeta[]> {
|
|
45
|
-
const manifest = await ContentClient.getManifest()
|
|
46
|
-
return this._applyFilters(manifest)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/** Returns the count of matching documents (no body loaded). */
|
|
50
|
-
async count(): Promise<number> {
|
|
51
|
-
const manifest = await ContentClient.getManifest()
|
|
52
|
-
return this._applyFilters(manifest).length
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* Returns the full `ContentItem` (body + toc) for the first matching document.
|
|
57
|
-
* When a `_path` prefix is provided and no predicates/sort/pagination are set,
|
|
58
|
-
* this fetches the single document directly by path.
|
|
59
|
-
*/
|
|
60
|
-
async first(): Promise<ContentItem | null> {
|
|
61
|
-
// Fast path: no filters, no sort, no pagination — fetch directly by path
|
|
62
|
-
if (
|
|
63
|
-
this._prefix !== undefined &&
|
|
64
|
-
this._predicates.length === 0 &&
|
|
65
|
-
this._sortField === undefined &&
|
|
66
|
-
this._limit === undefined &&
|
|
67
|
-
this._skip === undefined
|
|
68
|
-
) {
|
|
69
|
-
return ContentClient.getItem(this._prefix)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Slow path: apply filters on the manifest, then fetch the first match
|
|
73
|
-
const manifest = await ContentClient.getManifest()
|
|
74
|
-
const filtered = this._applyFilters(manifest)
|
|
75
|
-
if (filtered.length === 0) return null
|
|
76
|
-
return ContentClient.getItem(filtered[0]._path)
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
private _applyFilters(items: ContentMeta[]): ContentMeta[] {
|
|
80
|
-
let result = items
|
|
81
|
-
|
|
82
|
-
// Prefix filter
|
|
83
|
-
if (this._prefix !== undefined) {
|
|
84
|
-
const p = this._prefix
|
|
85
|
-
result = result.filter(
|
|
86
|
-
(doc) => doc._path === p || doc._path.startsWith(p + '/'),
|
|
87
|
-
)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// Where predicates
|
|
91
|
-
for (const pred of this._predicates) {
|
|
92
|
-
result = result.filter(pred)
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Sort
|
|
96
|
-
if (this._sortField !== undefined) {
|
|
97
|
-
const field = this._sortField
|
|
98
|
-
const dir = this._sortDir
|
|
99
|
-
result = [...result].sort((a, b) => {
|
|
100
|
-
const av = a[field] as string | number | undefined
|
|
101
|
-
const bv = b[field] as string | number | undefined
|
|
102
|
-
if (av === undefined && bv === undefined) return 0
|
|
103
|
-
if (av === undefined) return 1
|
|
104
|
-
if (bv === undefined) return -1
|
|
105
|
-
const cmp = av < bv ? -1 : av > bv ? 1 : 0
|
|
106
|
-
return dir === 'asc' ? cmp : -cmp
|
|
107
|
-
})
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// Skip
|
|
111
|
-
if (this._skip !== undefined) {
|
|
112
|
-
result = result.slice(this._skip)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Limit
|
|
116
|
-
if (this._limit !== undefined) {
|
|
117
|
-
result = result.slice(0, this._limit)
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return result
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Content query composable.
|
|
126
|
-
*
|
|
127
|
-
* Returns a `QueryBuilder` scoped to the given `_path` prefix. Chain filter,
|
|
128
|
-
* sort, and pagination methods before calling a terminal method
|
|
129
|
-
* (`.find()`, `.first()`, or `.count()`).
|
|
130
|
-
*
|
|
131
|
-
* @example
|
|
132
|
-
* ```ts
|
|
133
|
-
* // Single document
|
|
134
|
-
* const doc = await queryContent('/blog/hello').first()
|
|
135
|
-
*
|
|
136
|
-
* // Listing — no body loaded
|
|
137
|
-
* const posts = await queryContent('/blog')
|
|
138
|
-
* .where(doc => !doc.draft)
|
|
139
|
-
* .sortBy('date', 'desc')
|
|
140
|
-
* .limit(10)
|
|
141
|
-
* .find()
|
|
142
|
-
* ```
|
|
143
|
-
*/
|
|
144
|
-
export function queryContent(path?: string): QueryBuilder {
|
|
145
|
-
return new QueryBuilder(path)
|
|
146
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage, ServerResponse } from 'node:http'
|
|
2
|
-
|
|
3
|
-
/** Options forwarded to the `Set-Cookie` header when writing a cookie via `useCookie()`. */
|
|
4
|
-
export interface CookieOptions {
|
|
5
|
-
/** Cookie path. Defaults to '/' when setting/removing. */
|
|
6
|
-
path?: string
|
|
7
|
-
domain?: string
|
|
8
|
-
/** Max age in seconds. */
|
|
9
|
-
maxAge?: number
|
|
10
|
-
expires?: Date
|
|
11
|
-
httpOnly?: boolean
|
|
12
|
-
secure?: boolean
|
|
13
|
-
sameSite?: 'Strict' | 'Lax' | 'None'
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Isomorphic cookie accessor returned by `useCookie()`. Reads the current value and provides `set`/`remove` methods. */
|
|
17
|
-
export interface CookieRef {
|
|
18
|
-
/** The current cookie value, or undefined if not set. */
|
|
19
|
-
readonly value: string | undefined
|
|
20
|
-
/** Write the cookie value. */
|
|
21
|
-
set(value: string, options?: CookieOptions): void
|
|
22
|
-
/** Remove the cookie by setting Max-Age=0. */
|
|
23
|
-
remove(options?: CookieOptions): void
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
function parseCookies(cookieHeader: string): Record<string, string> {
|
|
29
|
-
const result: Record<string, string> = {}
|
|
30
|
-
for (const part of cookieHeader.split(';')) {
|
|
31
|
-
const eqIdx = part.indexOf('=')
|
|
32
|
-
if (eqIdx < 0) continue
|
|
33
|
-
const key = part.slice(0, eqIdx).trim()
|
|
34
|
-
const rawValue = part.slice(eqIdx + 1).trim()
|
|
35
|
-
try {
|
|
36
|
-
result[key] = decodeURIComponent(rawValue)
|
|
37
|
-
} catch {
|
|
38
|
-
result[key] = rawValue
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return result
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function serializeCookie(name: string, value: string, options: CookieOptions = {}): string {
|
|
45
|
-
let str = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
|
|
46
|
-
const path = options.path ?? '/'
|
|
47
|
-
if (path) str += `; Path=${path}`
|
|
48
|
-
if (options.domain) str += `; Domain=${options.domain}`
|
|
49
|
-
if (options.maxAge !== undefined) str += `; Max-Age=${options.maxAge}`
|
|
50
|
-
if (options.expires) str += `; Expires=${options.expires.toUTCString()}`
|
|
51
|
-
if (options.httpOnly) str += '; HttpOnly'
|
|
52
|
-
if (options.secure) str += '; Secure'
|
|
53
|
-
// Default SameSite to 'Lax' to prevent CSRF exposure on browsers that do not
|
|
54
|
-
// enforce a default (older Safari, Firefox < 79). 'Lax' allows top-level
|
|
55
|
-
// navigations while blocking third-party POSTs — the right default for most apps.
|
|
56
|
-
// Callers can override with sameSite: 'None' (requires Secure) or 'Strict'.
|
|
57
|
-
const sameSite = options.sameSite ?? 'Lax'
|
|
58
|
-
str += `; SameSite=${sameSite}`
|
|
59
|
-
return str
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function appendSetCookie(res: ServerResponse, cookie: string): void {
|
|
63
|
-
const existing = res.getHeader('Set-Cookie')
|
|
64
|
-
const list: string[] = existing == null
|
|
65
|
-
? []
|
|
66
|
-
: Array.isArray(existing) ? existing : [String(existing)]
|
|
67
|
-
list.push(cookie)
|
|
68
|
-
res.setHeader('Set-Cookie', list)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// ─── Composable ───────────────────────────────────────────────────────────────
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Isomorphic cookie composable.
|
|
75
|
-
*
|
|
76
|
-
* - **Server (SSR/SSG)**: reads from `req.headers.cookie` via AsyncLocalStorage;
|
|
77
|
-
* writes/removes via `res.setHeader('Set-Cookie', ...)`.
|
|
78
|
-
* - **Client**: reads and writes `document.cookie`.
|
|
79
|
-
*
|
|
80
|
-
* It is auto-imported, so you don't need to import it manually.
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```ts
|
|
84
|
-
* const token = useCookie('auth-token')
|
|
85
|
-
* console.log(token.value) // read
|
|
86
|
-
* token.set('abc123') // write
|
|
87
|
-
* token.remove() // delete
|
|
88
|
-
* ```
|
|
89
|
-
*/
|
|
90
|
-
export function useCookie(name: string, defaultOptions: CookieOptions = {}): CookieRef {
|
|
91
|
-
const g = globalThis as Record<string, unknown>
|
|
92
|
-
|
|
93
|
-
// ── SSR path ──────────────────────────────────────────────────────────────
|
|
94
|
-
const store = g['__CER_REQ_STORE__'] as
|
|
95
|
-
| { getStore(): { req: IncomingMessage; res: ServerResponse } | null }
|
|
96
|
-
| undefined
|
|
97
|
-
|
|
98
|
-
if (store) {
|
|
99
|
-
const ctx = store.getStore()
|
|
100
|
-
if (ctx) {
|
|
101
|
-
const { req, res } = ctx
|
|
102
|
-
const parsed = parseCookies(req.headers['cookie'] ?? '')
|
|
103
|
-
return {
|
|
104
|
-
value: parsed[name],
|
|
105
|
-
set(value: string, options?: CookieOptions) {
|
|
106
|
-
appendSetCookie(res, serializeCookie(name, value, { ...defaultOptions, ...options }))
|
|
107
|
-
},
|
|
108
|
-
remove(options?: CookieOptions) {
|
|
109
|
-
appendSetCookie(res, serializeCookie(name, '', { ...defaultOptions, ...options, maxAge: 0 }))
|
|
110
|
-
},
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── Client path ───────────────────────────────────────────────────────────
|
|
116
|
-
if (typeof document !== 'undefined') {
|
|
117
|
-
const parsed = parseCookies(document.cookie)
|
|
118
|
-
return {
|
|
119
|
-
value: parsed[name],
|
|
120
|
-
set(value: string, options?: CookieOptions) {
|
|
121
|
-
document.cookie = serializeCookie(name, value, { ...defaultOptions, ...options })
|
|
122
|
-
},
|
|
123
|
-
remove(options?: CookieOptions) {
|
|
124
|
-
document.cookie = serializeCookie(name, '', { ...defaultOptions, ...options, maxAge: 0 })
|
|
125
|
-
},
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// ── Build-time / unknown context ──────────────────────────────────────────
|
|
130
|
-
return {
|
|
131
|
-
value: undefined,
|
|
132
|
-
set() {},
|
|
133
|
-
remove() {},
|
|
134
|
-
}
|
|
135
|
-
}
|