@jasonshimmy/vite-plugin-cer-app 0.23.2 → 0.25.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/adapters/netlify.js +4 -2
- package/dist/cli/adapters/netlify.js.map +1 -1
- 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/commands/preview.d.ts.map +1 -1
- package/dist/cli/commands/preview.js +4 -2
- package/dist/cli/commands/preview.js.map +1 -1
- 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 +4 -2
- 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 +226 -17
- package/dist/plugin/build-ssg.js.map +1 -1
- package/dist/plugin/content/index.d.ts +1 -1
- package/dist/plugin/content/index.d.ts.map +1 -1
- package/dist/plugin/content/index.js +8 -8
- package/dist/plugin/content/index.js.map +1 -1
- package/dist/plugin/content/parser.d.ts +3 -3
- package/dist/plugin/content/parser.d.ts.map +1 -1
- package/dist/plugin/content/parser.js +103 -24
- 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 +7 -0
- package/dist/plugin/html-post-process.d.ts.map +1 -1
- package/dist/plugin/html-post-process.js +26 -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 +181 -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/content.d.ts +18 -1
- package/dist/types/content.d.ts.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,70 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Tests for per-route render strategy (meta.render).
|
|
3
|
-
*
|
|
4
|
-
* render: 'server' — route is always SSR'd, skipped during SSG pre-rendering.
|
|
5
|
-
* render: 'spa' — route is served as SPA shell in SSR mode, skipped in SSG.
|
|
6
|
-
* render: 'static' — serve pre-rendered HTML from disk; fall back to SSR.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
10
|
-
|
|
11
|
-
// ─── render: 'server' ─────────────────────────────────────────────────────────
|
|
12
|
-
|
|
13
|
-
describe('render: server — always SSR', () => {
|
|
14
|
-
it('renders the page in SSR mode', () => {
|
|
15
|
-
if (mode !== 'ssr') return
|
|
16
|
-
cy.visit('/render-server-test')
|
|
17
|
-
cy.get('[data-cy=render-server-heading]').should('contain', 'Render Server Test')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
it('renders the page in SPA mode (client-side navigation)', () => {
|
|
21
|
-
if (mode !== 'spa') return
|
|
22
|
-
cy.visit('/render-server-test')
|
|
23
|
-
cy.get('[data-cy=render-server-heading]').should('contain', 'Render Server Test')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
if (mode === 'ssr') {
|
|
27
|
-
it('pre-renders the page in the initial HTML (SSR)', () => {
|
|
28
|
-
cy.request('/render-server-test').then((response) => {
|
|
29
|
-
expect(response.body).to.include('render-server-heading')
|
|
30
|
-
expect(response.body).to.include('Render Server Test')
|
|
31
|
-
})
|
|
32
|
-
})
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (mode === 'ssg') {
|
|
36
|
-
it('route with render:server is not pre-rendered — not found in ssg dist', () => {
|
|
37
|
-
// The route was skipped during SSG. The static preview falls back to
|
|
38
|
-
// dist/index.html (SPA shell) rather than a pre-rendered page, so the
|
|
39
|
-
// server-rendered heading is absent from the raw HTML response.
|
|
40
|
-
cy.request('/render-server-test').then((response) => {
|
|
41
|
-
expect(response.body).not.to.include('render-server-heading')
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
// ─── render: 'spa' ────────────────────────────────────────────────────────────
|
|
48
|
-
|
|
49
|
-
describe('render: spa — client-only', () => {
|
|
50
|
-
it('renders the page heading after JS boots', () => {
|
|
51
|
-
cy.visit('/render-spa-test')
|
|
52
|
-
cy.get('[data-cy=render-spa-heading]').should('contain', 'Render SPA Test')
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if (mode === 'ssr') {
|
|
56
|
-
it('raw HTML response is the SPA shell (no SSR content)', () => {
|
|
57
|
-
cy.request('/render-spa-test').then((response) => {
|
|
58
|
-
expect(response.body).not.to.include('render-spa-heading')
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (mode === 'ssg') {
|
|
64
|
-
it('route with render:spa is not pre-rendered — not found in ssg dist', () => {
|
|
65
|
-
cy.request('/render-spa-test').then((response) => {
|
|
66
|
-
expect(response.body).not.to.include('render-spa-heading')
|
|
67
|
-
})
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
})
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Preview server hardening e2e tests — verifies security headers,
|
|
3
|
-
* Cache-Control values, and graceful shutdown behaviour.
|
|
4
|
-
*
|
|
5
|
-
* Security headers and Cache-Control are server-level concerns, so these
|
|
6
|
-
* tests only run in SSR and SSG modes (both use `cer-app preview`).
|
|
7
|
-
* SPA mode also runs through the preview server, so the tests apply there too.
|
|
8
|
-
* Dev mode uses the Vite dev server which does not set these headers.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg' | 'dev'
|
|
12
|
-
|
|
13
|
-
// Security headers and Cache-Control are only set by the production preview server.
|
|
14
|
-
// Skip these tests in dev mode (Vite dev server does not set them).
|
|
15
|
-
if (mode !== 'dev') {
|
|
16
|
-
describe('Preview server — security headers', () => {
|
|
17
|
-
it('responds with X-Content-Type-Options: nosniff', () => {
|
|
18
|
-
cy.request('/').then((response) => {
|
|
19
|
-
expect(response.headers['x-content-type-options']).to.eq('nosniff')
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('responds with X-Frame-Options: DENY', () => {
|
|
24
|
-
cy.request('/').then((response) => {
|
|
25
|
-
expect(response.headers['x-frame-options']).to.eq('DENY')
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('responds with Referrer-Policy: strict-origin-when-cross-origin', () => {
|
|
30
|
-
cy.request('/').then((response) => {
|
|
31
|
-
expect(response.headers['referrer-policy']).to.eq('strict-origin-when-cross-origin')
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('includes security headers on 404 responses', () => {
|
|
36
|
-
cy.request({ url: '/definitely-not-a-real-page-xyz', failOnStatusCode: false }).then((response) => {
|
|
37
|
-
expect(response.headers['x-content-type-options']).to.eq('nosniff')
|
|
38
|
-
})
|
|
39
|
-
})
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
describe('Preview server — path traversal protection', () => {
|
|
44
|
-
// HTTP clients (including Cypress/got) normalize `..` segments before sending,
|
|
45
|
-
// so a raw traversal like `/../../../../etc/passwd` arrives at the server as
|
|
46
|
-
// `/etc/passwd`. The `isPathBounded` guard is exercised via unit tests
|
|
47
|
-
// (src/__tests__/cli/preview-isr.test.ts). Here we verify the observable
|
|
48
|
-
// guarantee: files that do not exist inside dist/ are never served.
|
|
49
|
-
it('does not serve a file that does not exist inside dist/', () => {
|
|
50
|
-
cy.request({ url: '/etc/passwd', failOnStatusCode: false }).then((response) => {
|
|
51
|
-
// Either 404 (static) or 200 (SSR SPA fallback) — never a served file from
|
|
52
|
-
// outside the dist directory. Critically, the response body must NOT contain
|
|
53
|
-
// typical /etc/passwd content.
|
|
54
|
-
expect(response.body).not.to.include('root:x:')
|
|
55
|
-
expect(response.body).not.to.include('/bin/bash')
|
|
56
|
-
})
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
// NOTE: HTTP clients (including Cypress/got) normalize `..` segments before
|
|
60
|
-
// sending, so raw traversal sequences never arrive at the server unchanged.
|
|
61
|
-
// The isPathBounded() guard is exhaustively covered at the unit level in
|
|
62
|
-
// src/__tests__/cli/preview-isr.test.ts. No additional e2e assertion is needed.
|
|
63
|
-
})
|
|
64
|
-
|
|
65
|
-
if (mode !== 'dev') {
|
|
66
|
-
describe('Preview server — Cache-Control', () => {
|
|
67
|
-
it('serves HTML with Cache-Control: no-cache', () => {
|
|
68
|
-
cy.request('/').then((response) => {
|
|
69
|
-
expect(response.headers['cache-control']).to.include('no-cache')
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
it('serves content-hashed assets with immutable Cache-Control', () => {
|
|
74
|
-
// Get the page to discover an actual asset URL (Vite hashes asset filenames)
|
|
75
|
-
cy.request('/').then((htmlResponse) => {
|
|
76
|
-
const assetMatch = htmlResponse.body.match(/\/assets\/[^"'\s]+\.js/)
|
|
77
|
-
if (!assetMatch) return // no JS asset found in this page, skip
|
|
78
|
-
|
|
79
|
-
const assetUrl = assetMatch[0]
|
|
80
|
-
cy.request(assetUrl).then((assetResponse) => {
|
|
81
|
-
const cc = assetResponse.headers['cache-control'] as string
|
|
82
|
-
expect(cc).to.include('max-age=31536000')
|
|
83
|
-
expect(cc).to.include('immutable')
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Routes test — verifies every route renders correct content in all build modes.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
6
|
-
|
|
7
|
-
describe('Route rendering', () => {
|
|
8
|
-
context('Home page (/)', () => {
|
|
9
|
-
it('renders the home heading', () => {
|
|
10
|
-
cy.visit('/')
|
|
11
|
-
cy.get('[data-cy=home-heading]').should('contain', 'Kitchen Sink')
|
|
12
|
-
})
|
|
13
|
-
|
|
14
|
-
it('renders the ks-badge component', () => {
|
|
15
|
-
cy.visit('/')
|
|
16
|
-
cy.get('[data-cy=ks-badge]').should('exist')
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('renders the default layout with nav', () => {
|
|
20
|
-
cy.visit('/')
|
|
21
|
-
cy.get('[data-cy=site-nav]').should('exist')
|
|
22
|
-
cy.get('[data-cy=site-header]').should('exist')
|
|
23
|
-
cy.get('[data-cy=site-footer]').should('exist')
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
it('renders all nav links', () => {
|
|
27
|
-
cy.visit('/')
|
|
28
|
-
cy.get('[data-cy=nav-home]').should('exist')
|
|
29
|
-
cy.get('[data-cy=nav-about]').should('exist')
|
|
30
|
-
cy.get('[data-cy=nav-counter]').should('exist')
|
|
31
|
-
cy.get('[data-cy=nav-blog]').should('exist')
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
context('About page (/about) — minimal layout + useHead', () => {
|
|
36
|
-
it('renders the about heading', () => {
|
|
37
|
-
cy.visit('/about')
|
|
38
|
-
cy.get('[data-cy=about-heading]').should('contain', 'About')
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
it('uses the minimal layout (no site-nav)', () => {
|
|
42
|
-
cy.visit('/about')
|
|
43
|
-
cy.get('[data-cy=minimal-layout]').should('exist')
|
|
44
|
-
cy.get('[data-cy=site-nav]').should('not.exist')
|
|
45
|
-
})
|
|
46
|
-
})
|
|
47
|
-
|
|
48
|
-
context('Counter page (/counter)', () => {
|
|
49
|
-
it('renders the counter heading', () => {
|
|
50
|
-
cy.visit('/counter')
|
|
51
|
-
cy.get('[data-cy=counter-heading]').should('contain', 'Counter')
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it('renders the counter widget with initial count of 0', () => {
|
|
55
|
-
cy.visit('/counter')
|
|
56
|
-
cy.get('[data-cy=count]').should('contain', '0')
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
it('renders increment, decrement, and reset buttons', () => {
|
|
60
|
-
cy.visit('/counter')
|
|
61
|
-
cy.get('[data-cy=increment]').should('exist')
|
|
62
|
-
cy.get('[data-cy=decrement]').should('exist')
|
|
63
|
-
cy.get('[data-cy=reset]').should('exist')
|
|
64
|
-
})
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
context('Head page (/head) — useHead', () => {
|
|
68
|
-
it('renders the head-test heading', () => {
|
|
69
|
-
cy.visit('/head')
|
|
70
|
-
cy.get('[data-cy=head-heading]').should('contain', 'Head Test')
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
context('Blog list page (/blog)', () => {
|
|
75
|
-
it('renders the blog heading', () => {
|
|
76
|
-
cy.visit('/blog')
|
|
77
|
-
cy.get('[data-cy=blog-heading]').should('contain', 'Blog')
|
|
78
|
-
})
|
|
79
|
-
})
|
|
80
|
-
|
|
81
|
-
context('Blog detail page (/blog/first-post) — dynamic route', () => {
|
|
82
|
-
it('renders the post title', () => {
|
|
83
|
-
cy.visit('/blog/first-post')
|
|
84
|
-
cy.get('[data-cy=post-title]').should('contain', 'First Post')
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('renders the post slug', () => {
|
|
88
|
-
cy.visit('/blog/first-post')
|
|
89
|
-
cy.get('[data-cy=post-slug]').should('contain', 'first-post')
|
|
90
|
-
})
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
context('Item page (/items/1) — route params via useProps', () => {
|
|
94
|
-
it('renders the item heading', () => {
|
|
95
|
-
cy.visit('/items/1')
|
|
96
|
-
cy.get('[data-cy=item-heading]').should('contain', 'Item Detail')
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('shows the correct item ID from route params', () => {
|
|
100
|
-
cy.visit('/items/1')
|
|
101
|
-
cy.get('[data-cy=item-id]').should('contain', '1')
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it('shows a different ID for /items/2', () => {
|
|
105
|
-
cy.visit('/items/2')
|
|
106
|
-
cy.get('[data-cy=item-id]').should('contain', '2')
|
|
107
|
-
})
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
context('Login page (/login) — route group, minimal layout', () => {
|
|
111
|
-
it('renders the login heading', () => {
|
|
112
|
-
cy.visit('/login')
|
|
113
|
-
cy.get('[data-cy=login-heading]').should('contain', 'Login')
|
|
114
|
-
})
|
|
115
|
-
|
|
116
|
-
it('uses the minimal layout', () => {
|
|
117
|
-
cy.visit('/login')
|
|
118
|
-
cy.get('[data-cy=minimal-layout]').should('exist')
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
context('404 page — catch-all route', () => {
|
|
123
|
-
it('renders the 404 heading for an unknown path', () => {
|
|
124
|
-
cy.visit('/this-page-does-not-exist-at-all', { failOnStatusCode: false })
|
|
125
|
-
cy.get('[data-cy=not-found-heading]').should('contain', '404')
|
|
126
|
-
})
|
|
127
|
-
})
|
|
128
|
-
})
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useSeoMeta() e2e tests — verifies Open Graph, Twitter Card, canonical, and
|
|
3
|
-
* title/description tags are injected correctly in all modes.
|
|
4
|
-
*
|
|
5
|
-
* In SSR/SSG: tags must be present in the initial server-rendered HTML.
|
|
6
|
-
* In all modes: tags must be present after client hydration.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
10
|
-
|
|
11
|
-
describe('useSeoMeta() — title and description', () => {
|
|
12
|
-
it('sets document title', () => {
|
|
13
|
-
cy.visit('/seo-test')
|
|
14
|
-
cy.title().should('eq', 'SEO Test — Kitchen Sink')
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
it('sets meta description', () => {
|
|
18
|
-
cy.visit('/seo-test')
|
|
19
|
-
cy.get('meta[name="description"]').should('have.attr', 'content', 'A test page for useSeoMeta().')
|
|
20
|
-
})
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
describe('useSeoMeta() — Open Graph tags', () => {
|
|
24
|
-
it('sets og:title', () => {
|
|
25
|
-
cy.visit('/seo-test')
|
|
26
|
-
cy.get('meta[property="og:title"]').should('have.attr', 'content', 'SEO Test OG Title')
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('sets og:description', () => {
|
|
30
|
-
cy.visit('/seo-test')
|
|
31
|
-
cy.get('meta[property="og:description"]').should('have.attr', 'content', 'SEO Test OG description.')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('sets og:image', () => {
|
|
35
|
-
cy.visit('/seo-test')
|
|
36
|
-
cy.get('meta[property="og:image"]').should('have.attr', 'content', 'https://example.com/og/seo-test.png')
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('sets og:url', () => {
|
|
40
|
-
cy.visit('/seo-test')
|
|
41
|
-
cy.get('meta[property="og:url"]').should('have.attr', 'content', 'https://example.com/seo-test')
|
|
42
|
-
})
|
|
43
|
-
|
|
44
|
-
it('sets og:type', () => {
|
|
45
|
-
cy.visit('/seo-test')
|
|
46
|
-
cy.get('meta[property="og:type"]').should('have.attr', 'content', 'website')
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
it('sets og:site_name', () => {
|
|
50
|
-
cy.visit('/seo-test')
|
|
51
|
-
cy.get('meta[property="og:site_name"]').should('have.attr', 'content', 'Kitchen Sink')
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
describe('useSeoMeta() — Twitter Card tags', () => {
|
|
56
|
-
it('sets twitter:card', () => {
|
|
57
|
-
cy.visit('/seo-test')
|
|
58
|
-
cy.get('meta[name="twitter:card"]').should('have.attr', 'content', 'summary_large_image')
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
it('sets twitter:title', () => {
|
|
62
|
-
cy.visit('/seo-test')
|
|
63
|
-
cy.get('meta[name="twitter:title"]').should('have.attr', 'content', 'SEO Test Twitter Title')
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('sets twitter:site', () => {
|
|
67
|
-
cy.visit('/seo-test')
|
|
68
|
-
cy.get('meta[name="twitter:site"]').should('have.attr', 'content', '@ks')
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
describe('useSeoMeta() — canonical link', () => {
|
|
73
|
-
it('sets canonical link element', () => {
|
|
74
|
-
cy.visit('/seo-test')
|
|
75
|
-
cy.get('link[rel="canonical"]').should('have.attr', 'href', 'https://example.com/seo-test')
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
if (mode !== 'spa') {
|
|
80
|
-
describe('useSeoMeta() — server-side injection (SSR/SSG)', () => {
|
|
81
|
-
it('title is in the initial HTML', () => {
|
|
82
|
-
cy.request('/seo-test').then((response) => {
|
|
83
|
-
expect(response.body).to.include('<title>SEO Test — Kitchen Sink</title>')
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('og:title meta is in the initial HTML', () => {
|
|
88
|
-
cy.request('/seo-test').then((response) => {
|
|
89
|
-
expect(response.body).to.include('og:title')
|
|
90
|
-
expect(response.body).to.include('SEO Test OG Title')
|
|
91
|
-
})
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('twitter:card meta is in the initial HTML', () => {
|
|
95
|
-
cy.request('/seo-test').then((response) => {
|
|
96
|
-
expect(response.body).to.include('twitter:card')
|
|
97
|
-
expect(response.body).to.include('summary_large_image')
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
it('canonical link is in the initial HTML', () => {
|
|
102
|
-
cy.request('/seo-test').then((response) => {
|
|
103
|
-
expect(response.body).to.include('canonical')
|
|
104
|
-
expect(response.body).to.include('https://example.com/seo-test')
|
|
105
|
-
})
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
}
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Server middleware e2e tests — verifies that files in server/middleware/ run
|
|
3
|
-
* on every HTTP request before API routes and SSR rendering.
|
|
4
|
-
*
|
|
5
|
-
* The kitchen-sink includes `server/middleware/01-headers.ts` which sets
|
|
6
|
-
* `X-CER-Middleware: active` on every response. These tests confirm that
|
|
7
|
-
* header is present, proving the middleware chain executed.
|
|
8
|
-
*
|
|
9
|
-
* Server middleware runs in SSR mode (production) and dev mode. It does NOT
|
|
10
|
-
* run in SPA or SSG static-file serving modes, so tests are gated accordingly.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
14
|
-
|
|
15
|
-
// Server middleware only runs when there is a live server processing requests.
|
|
16
|
-
// In dev mode the Vite middleware chain applies it; in SSR preview it is called
|
|
17
|
-
// by runServerMiddleware(). SPA and SSG preview servers serve static files and
|
|
18
|
-
// do not execute server middleware.
|
|
19
|
-
if (mode === 'ssr') {
|
|
20
|
-
describe('Server middleware — header injection (SSR)', () => {
|
|
21
|
-
it('adds X-CER-Middleware header to HTML responses', () => {
|
|
22
|
-
cy.request('/').then((response) => {
|
|
23
|
-
expect(response.headers['x-cer-middleware']).to.eq('active')
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
it('adds X-CER-Middleware header to API responses', () => {
|
|
28
|
-
cy.request('/api/health').then((response) => {
|
|
29
|
-
expect(response.headers['x-cer-middleware']).to.eq('active')
|
|
30
|
-
})
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
it('adds X-CER-Middleware header to 404 responses', () => {
|
|
34
|
-
cy.request({ url: '/this-path-does-not-exist', failOnStatusCode: false }).then((response) => {
|
|
35
|
-
expect(response.headers['x-cer-middleware']).to.eq('active')
|
|
36
|
-
})
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
it('middleware runs before API route handlers (header present on /api/posts)', () => {
|
|
40
|
-
cy.request('/api/posts').then((response) => {
|
|
41
|
-
expect(response.status).to.eq(200)
|
|
42
|
-
expect(response.headers['x-cer-middleware']).to.eq('active')
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* P1-3: Session secret rotation.
|
|
3
|
-
*
|
|
4
|
-
* Tests that useSession() supports multiple secrets for key rotation:
|
|
5
|
-
* - New sessions are signed with the first (active) secret.
|
|
6
|
-
* - Old sessions signed with a secondary secret are still readable.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
10
|
-
|
|
11
|
-
if (mode === 'ssr') {
|
|
12
|
-
describe('Session secret rotation (P1-3)', () => {
|
|
13
|
-
it('writes and reads back a session when sessionSecret is an array', () => {
|
|
14
|
-
// The kitchen-sink /session-rotation-test page sets a session and reads it back
|
|
15
|
-
cy.visit('/session-rotation-test')
|
|
16
|
-
cy.get('[data-cy=session-read]', { timeout: 5000 }).should('contain', 'rotation-user')
|
|
17
|
-
})
|
|
18
|
-
|
|
19
|
-
it('session is stored as an httpOnly cookie', () => {
|
|
20
|
-
cy.visit('/session-rotation-test')
|
|
21
|
-
// After visiting the page that sets a session, the cookie should be present
|
|
22
|
-
cy.getCookie('session').should('exist')
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
}
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useSession() e2e tests — verifies that HMAC-signed cookie sessions
|
|
3
|
-
* round-trip correctly across requests via the /api/session endpoint.
|
|
4
|
-
*
|
|
5
|
-
* The kitchen-sink exposes:
|
|
6
|
-
* GET /api/session → { userId: string | null }
|
|
7
|
-
* POST /api/session → creates session with userId = 'test-user'
|
|
8
|
-
* DELETE /api/session → clears the session
|
|
9
|
-
*
|
|
10
|
-
* Sessions require a server-side signing key, so these tests only run in
|
|
11
|
-
* SSR mode where the session secret is available.
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
15
|
-
|
|
16
|
-
if (mode === 'ssr') {
|
|
17
|
-
describe('useSession() — session create / read / clear (SSR)', () => {
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
cy.clearCookies()
|
|
20
|
-
})
|
|
21
|
-
|
|
22
|
-
it('GET /api/session returns null userId when no session cookie is set', () => {
|
|
23
|
-
cy.request('/api/session').then((response) => {
|
|
24
|
-
expect(response.status).to.eq(200)
|
|
25
|
-
expect(response.body.userId).to.be.null
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('POST /api/session creates a session cookie', () => {
|
|
30
|
-
cy.request({ method: 'POST', url: '/api/session' }).then((response) => {
|
|
31
|
-
expect(response.status).to.eq(200)
|
|
32
|
-
expect(response.body.ok).to.be.true
|
|
33
|
-
})
|
|
34
|
-
// Cookie should now be present
|
|
35
|
-
cy.getCookie('session').should('exist')
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('GET /api/session returns userId after session is created', () => {
|
|
39
|
-
// Create session
|
|
40
|
-
cy.request({ method: 'POST', url: '/api/session' })
|
|
41
|
-
// Read session — Cypress automatically forwards the cookie
|
|
42
|
-
cy.request('/api/session').then((response) => {
|
|
43
|
-
expect(response.body.userId).to.eq('test-user')
|
|
44
|
-
})
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
it('DELETE /api/session clears the session', () => {
|
|
48
|
-
// Create then clear
|
|
49
|
-
cy.request({ method: 'POST', url: '/api/session' })
|
|
50
|
-
cy.request({ method: 'DELETE', url: '/api/session' })
|
|
51
|
-
// Reading again should return null
|
|
52
|
-
cy.request('/api/session').then((response) => {
|
|
53
|
-
expect(response.body.userId).to.be.null
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('session persists across multiple requests in the same Cypress session', () => {
|
|
58
|
-
cy.request({ method: 'POST', url: '/api/session' })
|
|
59
|
-
// Make several read requests — all should see the same userId
|
|
60
|
-
cy.request('/api/session').its('body.userId').should('eq', 'test-user')
|
|
61
|
-
cy.request('/api/session').its('body.userId').should('eq', 'test-user')
|
|
62
|
-
})
|
|
63
|
-
|
|
64
|
-
it('tampered session cookie causes GET to return null userId', () => {
|
|
65
|
-
cy.request({ method: 'POST', url: '/api/session' })
|
|
66
|
-
// Overwrite the cookie with garbage
|
|
67
|
-
cy.setCookie('session', 'tampered.invalidsig')
|
|
68
|
-
cy.request('/api/session').then((response) => {
|
|
69
|
-
expect(response.body.userId).to.be.null
|
|
70
|
-
})
|
|
71
|
-
})
|
|
72
|
-
})
|
|
73
|
-
}
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* E2E tests for SSR render error resilience (P0-1).
|
|
3
|
-
*
|
|
4
|
-
* When a page component throws during the SSR render pass, the custom-elements
|
|
5
|
-
* runtime catches the error internally (logs a warning, emits an empty DSD
|
|
6
|
-
* placeholder) and continues rendering — so the server returns 200 with a valid
|
|
7
|
-
* HTML document. The entry-server-template's try/catch protects against
|
|
8
|
-
* *infrastructure-level* errors that escape the runtime's own protection (e.g.
|
|
9
|
-
* a catastrophic failure in the SSR helpers themselves).
|
|
10
|
-
*
|
|
11
|
-
* Observable behaviour for a component-level throw:
|
|
12
|
-
* - HTTP 200 (runtime handles it gracefully)
|
|
13
|
-
* - Valid HTML body with the page skeleton
|
|
14
|
-
* - Server remains operational for subsequent requests
|
|
15
|
-
*
|
|
16
|
-
* Only meaningful in SSR mode where the server renders pages on-request.
|
|
17
|
-
*/
|
|
18
|
-
|
|
19
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
20
|
-
|
|
21
|
-
if (mode === 'ssr') {
|
|
22
|
-
describe('SSR render error resilience — P0-1', () => {
|
|
23
|
-
it('returns HTTP 200 when a page component throws during render (runtime handles gracefully)', () => {
|
|
24
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false }).then((response) => {
|
|
25
|
-
expect(response.status).to.eq(200)
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
it('returns a non-empty HTML body even when a component throws', () => {
|
|
30
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false }).then((response) => {
|
|
31
|
-
expect(response.body).to.be.a('string')
|
|
32
|
-
expect(response.body.length).to.be.greaterThan(0)
|
|
33
|
-
expect(response.body).to.include('<!DOCTYPE html>')
|
|
34
|
-
})
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
it('serves a Content-Type: text/html header', () => {
|
|
38
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false }).then((response) => {
|
|
39
|
-
expect(response.headers['content-type']).to.include('text/html')
|
|
40
|
-
})
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
it('the broken component renders as an empty DSD placeholder (graceful degradation)', () => {
|
|
44
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false }).then((response) => {
|
|
45
|
-
// The runtime emits <page-render-error-test><template shadowrootmode="open"></template>...
|
|
46
|
-
// or the component tag is present with an empty shadow root.
|
|
47
|
-
expect(response.body).to.include('page-render-error-test')
|
|
48
|
-
})
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('server responds normally to other routes after a component render error', () => {
|
|
52
|
-
// First hit the broken route.
|
|
53
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false })
|
|
54
|
-
// Verify the server hasn't crashed and can still serve other routes.
|
|
55
|
-
cy.request('/about').then((response) => {
|
|
56
|
-
expect(response.status).to.eq(200)
|
|
57
|
-
})
|
|
58
|
-
})
|
|
59
|
-
|
|
60
|
-
it('does not leave the server hanging after a component render error', () => {
|
|
61
|
-
// If the server hung, cy.request() would time out (30s default).
|
|
62
|
-
// A response (any status) within timeout confirms no hang.
|
|
63
|
-
cy.request({ url: '/render-error-test', failOnStatusCode: false }).then((response) => {
|
|
64
|
-
expect(response.status).to.be.a('number')
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
})
|
|
68
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* P1-1: Synthetic 404 catch-all fallback.
|
|
3
|
-
*
|
|
4
|
-
* When no user-defined catch-all page exists the framework injects a synthetic
|
|
5
|
-
* /:all* route that returns `default: null`. The server must return HTTP 404
|
|
6
|
-
* (not 500) and render the global error component if defined.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
const mode = Cypress.env('mode') as 'spa' | 'ssr' | 'ssg'
|
|
10
|
-
|
|
11
|
-
describe('Synthetic 404 fallback (P1-1)', () => {
|
|
12
|
-
context('visiting a completely unknown route', () => {
|
|
13
|
-
it('navigating client-side to an unknown route does not crash the app', () => {
|
|
14
|
-
cy.visit('/')
|
|
15
|
-
cy.window().then((win) => {
|
|
16
|
-
// @ts-expect-error cerRouter is a private global
|
|
17
|
-
win.__cerRouter?.push('/this-route-does-not-exist-at-all')
|
|
18
|
-
})
|
|
19
|
-
// Page should still be functional — no JS error thrown
|
|
20
|
-
cy.get('body').should('exist')
|
|
21
|
-
})
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
if (mode === 'ssr') {
|
|
25
|
-
context('SSR — unknown route returns 404', () => {
|
|
26
|
-
it('returns HTTP 404 for an unknown path', () => {
|
|
27
|
-
cy.request({
|
|
28
|
-
url: '/completely-unknown-route-xyz',
|
|
29
|
-
failOnStatusCode: false,
|
|
30
|
-
}).then((response) => {
|
|
31
|
-
expect(response.status).to.eq(404)
|
|
32
|
-
})
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
it('does not return 500 for an unknown path', () => {
|
|
36
|
-
cy.request({
|
|
37
|
-
url: '/another-unknown-path-abc',
|
|
38
|
-
failOnStatusCode: false,
|
|
39
|
-
}).then((response) => {
|
|
40
|
-
expect(response.status).to.not.eq(500)
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
}
|
|
45
|
-
})
|