@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,49 +0,0 @@
|
|
|
1
|
-
import { inject } from '@jasonshimmy/custom-elements-runtime'
|
|
2
|
-
|
|
3
|
-
const _g = globalThis as Record<string, unknown>
|
|
4
|
-
const _PROVIDES_KEY = '__cerPluginProvides'
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* useInject — reads a value provided by a plugin via plugin.setup()'s provide().
|
|
8
|
-
*
|
|
9
|
-
* Works consistently across all rendering modes:
|
|
10
|
-
*
|
|
11
|
-
* - **SPA/Client**: Uses inject() from the component context tree (established
|
|
12
|
-
* by cer-layout-view calling provide() for each plugin-provided value).
|
|
13
|
-
*
|
|
14
|
-
* - **SSR/SSG**: Reads from globalThis.__cerPluginProvides, populated when the
|
|
15
|
-
* server entry runs plugin.setup() before rendering.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```ts
|
|
19
|
-
* // In a plugin (app/plugins/my-plugin.ts):
|
|
20
|
-
* export default {
|
|
21
|
-
* name: 'my-plugin',
|
|
22
|
-
* setup({ provide }) {
|
|
23
|
-
* provide('my-service', { greet: () => 'hello' })
|
|
24
|
-
* }
|
|
25
|
-
* }
|
|
26
|
-
*
|
|
27
|
-
* // In a component:
|
|
28
|
-
* component('my-page', () => {
|
|
29
|
-
* const service = useInject<{ greet(): string }>('my-service')
|
|
30
|
-
* })
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function useInject<T = unknown>(key: string, defaultValue?: T): T | undefined {
|
|
34
|
-
// Server-side (SSR/SSG): read from the global plugin provides map.
|
|
35
|
-
// __cerPluginProvides is populated by the server entry before the render pass.
|
|
36
|
-
if (typeof document === 'undefined') {
|
|
37
|
-
const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
|
|
38
|
-
const value = pluginProvides?.get(key)
|
|
39
|
-
return value !== undefined ? (value as T) : defaultValue
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Client-side: inject() walks the component context tree established by
|
|
43
|
-
// cer-layout-view's provide() calls. Falls back to __cerPluginProvides for
|
|
44
|
-
// reads before cer-layout-view mounts (e.g. during plugin-registered components).
|
|
45
|
-
const value = inject<T>(key)
|
|
46
|
-
if (value !== undefined) return value
|
|
47
|
-
const pluginProvides = _g[_PROVIDES_KEY] as Map<PropertyKey, unknown> | undefined
|
|
48
|
-
return (pluginProvides?.get(key) as T | undefined) ?? defaultValue
|
|
49
|
-
}
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import type { IncomingMessage } from 'node:http'
|
|
2
|
-
|
|
3
|
-
/** Return value of `useLocale()`. Exposes the active locale and helpers for locale-aware navigation. */
|
|
4
|
-
export interface LocaleComposable {
|
|
5
|
-
/** The active locale code for the current request / page. */
|
|
6
|
-
readonly locale: string
|
|
7
|
-
/** All configured locale codes. */
|
|
8
|
-
readonly locales: string[]
|
|
9
|
-
/** The default locale code. */
|
|
10
|
-
readonly defaultLocale: string
|
|
11
|
-
/**
|
|
12
|
-
* Returns the localised version of `path` for `targetLocale`.
|
|
13
|
-
*
|
|
14
|
-
* @example
|
|
15
|
-
* switchLocalePath('fr', '/about') // → '/fr/about'
|
|
16
|
-
* switchLocalePath('en', '/fr/about') // → '/about' (when en is default + prefix_except_default)
|
|
17
|
-
*/
|
|
18
|
-
switchLocalePath(targetLocale: string, path?: string): string
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
interface I18nVirtualConfig {
|
|
22
|
-
locales: string[]
|
|
23
|
-
defaultLocale: string
|
|
24
|
-
strategy: 'prefix' | 'prefix_except_default' | 'no_prefix'
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ─── Internal helpers ─────────────────────────────────────────────────────────
|
|
28
|
-
|
|
29
|
-
function _extractLocaleFromPath(pathname: string, locales: string[]): string | null {
|
|
30
|
-
const first = pathname.split('/')[1]
|
|
31
|
-
return locales.includes(first) ? first : null
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function _stripLocalePrefix(pathname: string, locale: string): string {
|
|
35
|
-
const prefix = `/${locale}`
|
|
36
|
-
if (pathname === prefix) return '/'
|
|
37
|
-
if (pathname.startsWith(`${prefix}/`)) return pathname.slice(prefix.length)
|
|
38
|
-
return pathname
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function _buildLocalePath(
|
|
42
|
-
pathname: string,
|
|
43
|
-
targetLocale: string,
|
|
44
|
-
currentLocale: string,
|
|
45
|
-
config: I18nVirtualConfig,
|
|
46
|
-
): string {
|
|
47
|
-
// Strip the current locale prefix (if any) to get the bare path
|
|
48
|
-
const bare = _stripLocalePrefix(pathname, currentLocale)
|
|
49
|
-
if (config.strategy === 'no_prefix') return bare
|
|
50
|
-
if (config.strategy === 'prefix_except_default' && targetLocale === config.defaultLocale) return bare
|
|
51
|
-
return `/${targetLocale}${bare === '/' ? '' : bare}`
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ─── Composable ───────────────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Internationalisation composable. Returns the active locale and helpers for
|
|
58
|
-
* building locale-aware URLs. Requires `i18n` to be configured in `cer.config.ts`.
|
|
59
|
-
*
|
|
60
|
-
* Works isomorphically: on the server it reads the URL path via AsyncLocalStorage;
|
|
61
|
-
* on the client it reads `window.location.pathname`.
|
|
62
|
-
*
|
|
63
|
-
* When `i18n` is not configured, `locale` equals `'default'` and `locales` is `['default']`.
|
|
64
|
-
*
|
|
65
|
-
* @example
|
|
66
|
-
* ```ts
|
|
67
|
-
* const { locale, switchLocalePath } = useLocale()
|
|
68
|
-
* console.log(locale) // 'fr'
|
|
69
|
-
* console.log(switchLocalePath('en', '/fr/about')) // '/about'
|
|
70
|
-
* ```
|
|
71
|
-
*/
|
|
72
|
-
export function useLocale(): LocaleComposable {
|
|
73
|
-
// Read i18n config from the virtual module at runtime.
|
|
74
|
-
// The virtual module is replaced by the bundler at build time.
|
|
75
|
-
const g = globalThis as Record<string, unknown>
|
|
76
|
-
const i18nConfig = g['__CER_I18N_CONFIG__'] as I18nVirtualConfig | null | undefined
|
|
77
|
-
|
|
78
|
-
const locales = i18nConfig?.locales ?? ['default']
|
|
79
|
-
const defaultLocale = i18nConfig?.defaultLocale ?? 'default'
|
|
80
|
-
const strategy = i18nConfig?.strategy ?? 'no_prefix'
|
|
81
|
-
|
|
82
|
-
// Resolve the current pathname from the server request context or window.
|
|
83
|
-
let pathname = '/'
|
|
84
|
-
const reqStore = g['__CER_REQ_STORE__'] as
|
|
85
|
-
| { getStore(): { req: IncomingMessage } | null }
|
|
86
|
-
| undefined
|
|
87
|
-
if (reqStore) {
|
|
88
|
-
const ctx = reqStore.getStore()
|
|
89
|
-
if (ctx?.req.url) {
|
|
90
|
-
try { pathname = new URL(ctx.req.url, 'http://x').pathname } catch { /* ignore */ }
|
|
91
|
-
}
|
|
92
|
-
} else if (typeof window !== 'undefined') {
|
|
93
|
-
pathname = window.location.pathname
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const detectedLocale = strategy !== 'no_prefix'
|
|
97
|
-
? (_extractLocaleFromPath(pathname, locales) ?? defaultLocale)
|
|
98
|
-
: defaultLocale
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
get locale() { return detectedLocale },
|
|
102
|
-
get locales() { return locales },
|
|
103
|
-
get defaultLocale() { return defaultLocale },
|
|
104
|
-
switchLocalePath(targetLocale: string, path?: string): string {
|
|
105
|
-
const base = path ?? pathname
|
|
106
|
-
return _buildLocalePath(base, targetLocale, detectedLocale, { locales, defaultLocale, strategy })
|
|
107
|
-
},
|
|
108
|
-
}
|
|
109
|
-
}
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* navigateTo — programmatic navigation.
|
|
3
|
-
*
|
|
4
|
-
* Works isomorphically:
|
|
5
|
-
* - **Server context** (inside a loader or middleware): sends a 302 redirect
|
|
6
|
-
* immediately by writing to the current request's `res` object via
|
|
7
|
-
* `AsyncLocalStorage`. The response is ended and the function returns.
|
|
8
|
-
* - **Client context**: delegates to the framework router so the SPA
|
|
9
|
-
* navigation pipeline (middleware, loaders, isNavigating) runs normally.
|
|
10
|
-
* Falls back to `window.location.href` if the router is not available.
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```ts
|
|
14
|
-
* // app/middleware/require-auth.ts
|
|
15
|
-
* export default defineMiddleware(() => {
|
|
16
|
-
* const { loggedIn } = useAuth()
|
|
17
|
-
* if (!loggedIn) return navigateTo('/login')
|
|
18
|
-
* })
|
|
19
|
-
* ```
|
|
20
|
-
*
|
|
21
|
-
* @example
|
|
22
|
-
* ```ts
|
|
23
|
-
* // app/pages/dashboard.ts
|
|
24
|
-
* component('page-dashboard', () => {
|
|
25
|
-
* useOnConnected(() => {
|
|
26
|
-
* if (someCondition) navigateTo('/home')
|
|
27
|
-
* })
|
|
28
|
-
* })
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
export function navigateTo(path: string): void | Promise<void> {
|
|
33
|
-
const g = globalThis as Record<string, unknown>
|
|
34
|
-
|
|
35
|
-
// Server: redirect via the request-scoped req/res store.
|
|
36
|
-
const reqStore = g['__CER_REQ_STORE__'] as {
|
|
37
|
-
getStore(): { res: { statusCode: number; setHeader(k: string, v: string): void; end(): void; writableEnded?: boolean } } | null
|
|
38
|
-
} | undefined
|
|
39
|
-
|
|
40
|
-
if (reqStore) {
|
|
41
|
-
const store = reqStore.getStore()
|
|
42
|
-
if (store?.res && !store.res.writableEnded) {
|
|
43
|
-
store.res.statusCode = 302
|
|
44
|
-
store.res.setHeader('Location', path)
|
|
45
|
-
store.res.end()
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Client: use the framework router so the full navigation pipeline runs.
|
|
51
|
-
const router = g['__cerRouter'] as { push(path: string): Promise<void> } | undefined
|
|
52
|
-
if (router) return router.push(path)
|
|
53
|
-
|
|
54
|
-
// Fallback: hard redirect.
|
|
55
|
-
if (typeof window !== 'undefined') {
|
|
56
|
-
window.location.href = path
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* usePageData — reads SSR-injected loader data for the current page.
|
|
3
|
-
*
|
|
4
|
-
* During SSR/SSG the server calls the matched route's `loader()` function
|
|
5
|
-
* and makes the result available in two ways:
|
|
6
|
-
*
|
|
7
|
-
* 1. **Server render pass** — the data is stored in a per-request
|
|
8
|
-
* `AsyncLocalStorage` context (set via `_cerDataStore.run(data, ...)` in the
|
|
9
|
-
* entry-server template). `usePageData()` reads this store so the component
|
|
10
|
-
* renders with real data in the initial SSR/SSG HTML.
|
|
11
|
-
*
|
|
12
|
-
* 2. **Client hydration** — the server also serializes the data as
|
|
13
|
-
* `window.__CER_DATA__` in the page `<head>`. On first component render
|
|
14
|
-
* `usePageData()` returns that value, caches it on the component context, and
|
|
15
|
-
* returns the same cached value on all subsequent re-renders of that component
|
|
16
|
-
* instance. The data remains available for the initial hydrated route until
|
|
17
|
-
* the next client-side navigation clears it before loading new page data.
|
|
18
|
-
*
|
|
19
|
-
* **Why caching is necessary**: the render function passed to `component()` is
|
|
20
|
-
* called on every re-render (it is the render function, not a setup function).
|
|
21
|
-
* Without caching, `usePageData()` could return `null` on later re-renders if a
|
|
22
|
-
* browser upgrades or re-renders the hydrated page after the initial read,
|
|
23
|
-
* flip the `ssrData` guard from truthy → null, and re-trigger client-side fetches.
|
|
24
|
-
*
|
|
25
|
-
* @returns The serialized loader result, or `null` if no SSR data is present.
|
|
26
|
-
*
|
|
27
|
-
* @example
|
|
28
|
-
* ```ts
|
|
29
|
-
* component('page-blog-slug', () => {
|
|
30
|
-
* const ssrData = usePageData<{ title: string; body: string }>()
|
|
31
|
-
* const title = ref(ssrData?.title ?? '')
|
|
32
|
-
* const body = ref(ssrData?.body ?? '')
|
|
33
|
-
*
|
|
34
|
-
* useOnConnected(() => {
|
|
35
|
-
* if (ssrData) return // already hydrated from SSR — skip fetch
|
|
36
|
-
* fetch(`/api/posts/${props.slug}`).then(r => r.json()).then(post => {
|
|
37
|
-
* title.value = post.title
|
|
38
|
-
* body.value = post.body
|
|
39
|
-
* })
|
|
40
|
-
* })
|
|
41
|
-
* })
|
|
42
|
-
* ```
|
|
43
|
-
*/
|
|
44
|
-
import { getCurrentComponentContext } from '@jasonshimmy/custom-elements-runtime'
|
|
45
|
-
|
|
46
|
-
// Key used to cache the page data on the component context object across re-renders.
|
|
47
|
-
const _PAGE_DATA_KEY = '_cerPageData'
|
|
48
|
-
|
|
49
|
-
export function usePageData<T = unknown>(): T | null {
|
|
50
|
-
const g = globalThis as Record<string, unknown>
|
|
51
|
-
|
|
52
|
-
// Server-side: read from the per-request AsyncLocalStorage context.
|
|
53
|
-
// __CER_DATA_STORE__ is set by the entry-server template and is only present
|
|
54
|
-
// in Node.js, so this branch is tree-shaken out of the client bundle.
|
|
55
|
-
const store = g['__CER_DATA_STORE__'] as { getStore(): unknown } | undefined
|
|
56
|
-
if (store) {
|
|
57
|
-
const ssrData = store.getStore() as T | null | undefined
|
|
58
|
-
if (ssrData !== undefined && ssrData !== null) return ssrData
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Client-side: check the component context cache first.
|
|
62
|
-
// getCurrentComponentContext() returns the context object for the component
|
|
63
|
-
// whose renderFn is currently executing (set by the component() runtime
|
|
64
|
-
// before calling renderFn). Caching on the context ensures the same value is
|
|
65
|
-
// returned on every re-render of the same element instance even if the
|
|
66
|
-
// browser performs a later hydration-time re-render.
|
|
67
|
-
const ctx = getCurrentComponentContext() as Record<string, unknown> | null
|
|
68
|
-
if (ctx) {
|
|
69
|
-
if (_PAGE_DATA_KEY in ctx) {
|
|
70
|
-
return ctx[_PAGE_DATA_KEY] as T | null
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Read the raw value from the global set by the server-rendered <script> tag.
|
|
75
|
-
const data = g['__CER_DATA__'] as T | undefined | null
|
|
76
|
-
const result: T | null = (data === undefined || data === null) ? null : data
|
|
77
|
-
|
|
78
|
-
// Cache on the component context so subsequent re-renders of this element
|
|
79
|
-
// instance return the same value without reading __CER_DATA__ again.
|
|
80
|
-
// Use Object.defineProperty to bypass the reactive Proxy set-trap on context,
|
|
81
|
-
// which would otherwise schedule a spurious re-render (and risk an infinite loop)
|
|
82
|
-
// every time we first cache the value.
|
|
83
|
-
if (ctx) {
|
|
84
|
-
Object.defineProperty(ctx, _PAGE_DATA_KEY, {
|
|
85
|
-
value: result,
|
|
86
|
-
writable: false,
|
|
87
|
-
enumerable: false,
|
|
88
|
-
configurable: true,
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
return result
|
|
93
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useRoute — returns the current route's path, params, query, and meta.
|
|
3
|
-
*
|
|
4
|
-
* Works isomorphically:
|
|
5
|
-
* - **Server render pass** — reads from `globalThis.__CER_ROUTE_STORE__`
|
|
6
|
-
* (AsyncLocalStorage populated by the entry-server handler before rendering).
|
|
7
|
-
* - **Client** — reads from the global router instance exposed by the app
|
|
8
|
-
* entry (`globalThis.__cerRouter`).
|
|
9
|
-
*
|
|
10
|
-
* @example
|
|
11
|
-
* ```ts
|
|
12
|
-
* // app/layouts/default.ts
|
|
13
|
-
* component('layout-default', () => {
|
|
14
|
-
* const route = useRoute()
|
|
15
|
-
* return html`
|
|
16
|
-
* <nav>${route.meta?.title ?? 'My App'}</nav>
|
|
17
|
-
* <slot></slot>
|
|
18
|
-
* `
|
|
19
|
-
* })
|
|
20
|
-
* ```
|
|
21
|
-
*
|
|
22
|
-
* @example
|
|
23
|
-
* ```ts
|
|
24
|
-
* // app/pages/post.ts
|
|
25
|
-
* component('page-post', () => {
|
|
26
|
-
* const { params } = useRoute()
|
|
27
|
-
* const { data: post } = useFetch(\`/api/posts/\${params.id}\`)
|
|
28
|
-
* })
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
export interface RouteInfo {
|
|
33
|
-
/** Current URL path, e.g. `'/posts/42'`. */
|
|
34
|
-
path: string
|
|
35
|
-
/** Dynamic route params, e.g. `{ id: '42' }`. */
|
|
36
|
-
params: Record<string, string>
|
|
37
|
-
/** Parsed query string params, e.g. `{ page: '2' }`. */
|
|
38
|
-
query: Record<string, string>
|
|
39
|
-
/** Static route meta object exported by the matched page module. */
|
|
40
|
-
meta: Record<string, unknown> | null
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export function useRoute(): RouteInfo {
|
|
44
|
-
const g = globalThis as Record<string, unknown>
|
|
45
|
-
|
|
46
|
-
// Server: read from the per-request AsyncLocalStorage context.
|
|
47
|
-
const routeStore = g['__CER_ROUTE_STORE__'] as { getStore(): unknown } | undefined
|
|
48
|
-
if (routeStore) {
|
|
49
|
-
const info = routeStore.getStore() as RouteInfo | null
|
|
50
|
-
if (info) return info
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Client: read from the global router set by the app entry.
|
|
54
|
-
const router = g['__cerRouter'] as {
|
|
55
|
-
getCurrent(): { path: string; query?: Record<string, string> }
|
|
56
|
-
matchRoute(path: string): { route?: { meta?: unknown }; params?: Record<string, string> } | null
|
|
57
|
-
} | undefined
|
|
58
|
-
|
|
59
|
-
if (router) {
|
|
60
|
-
const current = router.getCurrent()
|
|
61
|
-
const matched = router.matchRoute(current.path)
|
|
62
|
-
return {
|
|
63
|
-
path: current.path,
|
|
64
|
-
params: (matched?.params ?? {}) as Record<string, string>,
|
|
65
|
-
query: (current.query ?? {}) as Record<string, string>,
|
|
66
|
-
meta: (matched?.route?.meta as Record<string, unknown>) ?? null,
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
return { path: '/', params: {}, query: {}, meta: null }
|
|
71
|
-
}
|
|
@@ -1,123 +0,0 @@
|
|
|
1
|
-
/** Resolved public runtime config values. Available on both server and client. */
|
|
2
|
-
export interface RuntimeConfigPublic {
|
|
3
|
-
[key: string]: unknown
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
/** Resolved private runtime config values. Available on the server only — never present on the client. */
|
|
7
|
-
export interface RuntimeConfigPrivate {
|
|
8
|
-
[key: string]: string | string[]
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/** Return value of `useRuntimeConfig()`. */
|
|
12
|
-
export interface RuntimeConfigResult {
|
|
13
|
-
public: RuntimeConfigPublic
|
|
14
|
-
private?: RuntimeConfigPrivate
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Returns the runtime configuration set in `cer.config.ts`.
|
|
19
|
-
*
|
|
20
|
-
* - `public` — available on both server and client.
|
|
21
|
-
* - `private` — available on the server only (resolved from `process.env` at
|
|
22
|
-
* startup). Never present on the client.
|
|
23
|
-
*
|
|
24
|
-
* @example
|
|
25
|
-
* // cer.config.ts
|
|
26
|
-
* export default defineConfig({
|
|
27
|
-
* runtimeConfig: {
|
|
28
|
-
* public: { apiBase: process.env.VITE_API_BASE ?? '/api' },
|
|
29
|
-
* private: { dbUrl: '', secretKey: '' },
|
|
30
|
-
* },
|
|
31
|
-
* })
|
|
32
|
-
*
|
|
33
|
-
* // app/pages/index.ts (loader — server-only)
|
|
34
|
-
* const { private: priv } = useRuntimeConfig()
|
|
35
|
-
* const rows = await db.query(priv.dbUrl)
|
|
36
|
-
*/
|
|
37
|
-
export function useRuntimeConfig(): RuntimeConfigResult {
|
|
38
|
-
// Dynamic import resolved at runtime — avoids a static circular dependency
|
|
39
|
-
// between the composable and the virtual module.
|
|
40
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
|
-
const mod = (globalThis as any).__cerRuntimeConfig
|
|
42
|
-
const config: RuntimeConfigResult = mod ? (mod as RuntimeConfigResult) : { public: {} }
|
|
43
|
-
|
|
44
|
-
// In browser contexts, wrap the result in a Proxy that throws a clear,
|
|
45
|
-
// actionable error if any code attempts to read `runtimeConfig.private`.
|
|
46
|
-
// Private values are server-only secrets and are never serialized into the
|
|
47
|
-
// client bundle — accessing them client-side is always a bug.
|
|
48
|
-
if (typeof window !== 'undefined') {
|
|
49
|
-
return new Proxy(config, {
|
|
50
|
-
get(target, prop) {
|
|
51
|
-
if (prop === 'private') {
|
|
52
|
-
throw new Error(
|
|
53
|
-
'[cer-app] runtimeConfig.private is not available in the browser. ' +
|
|
54
|
-
'Move this access into a server-only loader, middleware, or API handler.',
|
|
55
|
-
)
|
|
56
|
-
}
|
|
57
|
-
return Reflect.get(target, prop)
|
|
58
|
-
},
|
|
59
|
-
})
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return config
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Called once during app bootstrap to store the resolved runtimeConfig on
|
|
67
|
-
* globalThis so useRuntimeConfig() can access it synchronously in any context
|
|
68
|
-
* (component render, composable, server handler).
|
|
69
|
-
*/
|
|
70
|
-
export function initRuntimeConfig(config: RuntimeConfigResult): void {
|
|
71
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
72
|
-
;(globalThis as any).__cerRuntimeConfig = config
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Converts a camelCase or mixed key to UPPER_SNAKE_CASE for env var lookup.
|
|
77
|
-
* Examples: `dbUrl` → `DB_URL`, `secretKey` → `SECRET_KEY`, `API_KEY` → `API_KEY`.
|
|
78
|
-
*/
|
|
79
|
-
function toUpperSnakeCase(key: string): string {
|
|
80
|
-
return key
|
|
81
|
-
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
82
|
-
.toUpperCase()
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Resolves a private config object by looking up each key in the supplied
|
|
87
|
-
* environment variable map, with the following precedence:
|
|
88
|
-
*
|
|
89
|
-
* 1. `env[key]` — exact case (e.g. `process.env.dbUrl`)
|
|
90
|
-
* 2. `env[UPPER_SNAKE_CASE(key)]` — conventional env var form (e.g. `process.env.DB_URL`)
|
|
91
|
-
* 3. `defaultValue` — the declared default from `cer.config.ts`
|
|
92
|
-
*
|
|
93
|
-
* Array values (e.g. `sessionSecret: [newSecret, oldSecret]`) are passed
|
|
94
|
-
* through unchanged — they are already resolved by the caller at config time.
|
|
95
|
-
*
|
|
96
|
-
* Accepts an optional `env` parameter so the function is unit-testable
|
|
97
|
-
* without mutating `process.env`.
|
|
98
|
-
*/
|
|
99
|
-
export function resolvePrivateConfig(
|
|
100
|
-
defaults: Record<string, string | string[]>,
|
|
101
|
-
env: Record<string, string | undefined> = process.env as Record<string, string | undefined>,
|
|
102
|
-
): Record<string, string | string[]> {
|
|
103
|
-
return Object.fromEntries(
|
|
104
|
-
Object.entries(defaults).map(([key, defaultValue]) => {
|
|
105
|
-
// Array values are already fully resolved at config build time — pass through.
|
|
106
|
-
if (Array.isArray(defaultValue)) {
|
|
107
|
-
return [key, defaultValue.filter(Boolean)]
|
|
108
|
-
}
|
|
109
|
-
const envKey = toUpperSnakeCase(key)
|
|
110
|
-
const resolved = env[key] ?? env[envKey] ?? defaultValue
|
|
111
|
-
// Warn when no env var was found and the declared default is an empty string.
|
|
112
|
-
// An empty-string default is the conventional way to declare a required secret
|
|
113
|
-
// (the key exists for typing purposes but has no safe default value).
|
|
114
|
-
if (resolved === '' && env[key] === undefined && env[envKey] === undefined) {
|
|
115
|
-
console.warn(
|
|
116
|
-
`[cer-app] runtimeConfig.private: "${key}" is an empty string — ` +
|
|
117
|
-
`set ${envKey} in the environment to provide a value.`,
|
|
118
|
-
)
|
|
119
|
-
}
|
|
120
|
-
return [key, resolved]
|
|
121
|
-
}),
|
|
122
|
-
)
|
|
123
|
-
}
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { useHead } from './use-head.js'
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Convenience input for common SEO meta tags.
|
|
5
|
-
* Pass to `useSeoMeta()` — it maps each field to the correct `<meta>` or `<title>` tag automatically.
|
|
6
|
-
* All fields are optional.
|
|
7
|
-
*/
|
|
8
|
-
export interface SeoMetaInput {
|
|
9
|
-
/** Sets the document title. */
|
|
10
|
-
title?: string
|
|
11
|
-
/** Sets the meta description. */
|
|
12
|
-
description?: string
|
|
13
|
-
// Open Graph
|
|
14
|
-
ogTitle?: string
|
|
15
|
-
ogDescription?: string
|
|
16
|
-
ogImage?: string
|
|
17
|
-
ogUrl?: string
|
|
18
|
-
/** e.g. `'website'`, `'article'`. No tag is emitted when omitted. */
|
|
19
|
-
ogType?: string
|
|
20
|
-
ogSiteName?: string
|
|
21
|
-
// Twitter / X
|
|
22
|
-
/** e.g. `'summary'`, `'summary_large_image'`. No tag is emitted when omitted. */
|
|
23
|
-
twitterCard?: string
|
|
24
|
-
twitterTitle?: string
|
|
25
|
-
twitterDescription?: string
|
|
26
|
-
twitterImage?: string
|
|
27
|
-
/** Twitter/X site handle, e.g. '@mysite'. */
|
|
28
|
-
twitterSite?: string
|
|
29
|
-
/** Canonical URL injected as <link rel="canonical">. */
|
|
30
|
-
canonical?: string
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Thin wrapper over `useHead()` for common SEO tags.
|
|
35
|
-
*
|
|
36
|
-
* Sets the page title, meta description, Open Graph tags, Twitter Card tags,
|
|
37
|
-
* and a canonical link element. Only tags with a non-undefined value are emitted.
|
|
38
|
-
*
|
|
39
|
-
* It is auto-imported, so you don't need to import it manually.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* useSeoMeta({
|
|
44
|
-
* title: 'My page',
|
|
45
|
-
* description: 'A great page.',
|
|
46
|
-
* ogImage: 'https://example.com/og.png',
|
|
47
|
-
* canonical: 'https://example.com/my-page',
|
|
48
|
-
* })
|
|
49
|
-
* ```
|
|
50
|
-
*/
|
|
51
|
-
export function useSeoMeta(input: SeoMetaInput): void {
|
|
52
|
-
const meta: Array<Record<string, string>> = []
|
|
53
|
-
const link: Array<Record<string, string>> = []
|
|
54
|
-
|
|
55
|
-
if (input.description !== undefined) meta.push({ name: 'description', content: input.description })
|
|
56
|
-
|
|
57
|
-
// Open Graph
|
|
58
|
-
if (input.ogTitle !== undefined) meta.push({ property: 'og:title', content: input.ogTitle })
|
|
59
|
-
if (input.ogDescription !== undefined) meta.push({ property: 'og:description', content: input.ogDescription })
|
|
60
|
-
if (input.ogImage !== undefined) meta.push({ property: 'og:image', content: input.ogImage })
|
|
61
|
-
if (input.ogUrl !== undefined) meta.push({ property: 'og:url', content: input.ogUrl })
|
|
62
|
-
if (input.ogType !== undefined) meta.push({ property: 'og:type', content: input.ogType })
|
|
63
|
-
if (input.ogSiteName !== undefined) meta.push({ property: 'og:site_name', content: input.ogSiteName })
|
|
64
|
-
|
|
65
|
-
// Twitter / X
|
|
66
|
-
if (input.twitterCard !== undefined) meta.push({ name: 'twitter:card', content: input.twitterCard })
|
|
67
|
-
if (input.twitterTitle !== undefined) meta.push({ name: 'twitter:title', content: input.twitterTitle })
|
|
68
|
-
if (input.twitterDescription !== undefined) meta.push({ name: 'twitter:description', content: input.twitterDescription })
|
|
69
|
-
if (input.twitterImage !== undefined) meta.push({ name: 'twitter:image', content: input.twitterImage })
|
|
70
|
-
if (input.twitterSite !== undefined) meta.push({ name: 'twitter:site', content: input.twitterSite })
|
|
71
|
-
|
|
72
|
-
// Canonical link
|
|
73
|
-
if (input.canonical !== undefined) link.push({ rel: 'canonical', href: input.canonical })
|
|
74
|
-
|
|
75
|
-
useHead({
|
|
76
|
-
title: input.title,
|
|
77
|
-
meta: meta.length > 0 ? meta : undefined,
|
|
78
|
-
link: link.length > 0 ? link : undefined,
|
|
79
|
-
})
|
|
80
|
-
}
|