@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,214 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useFetch() — component context path.
|
|
3
|
-
*
|
|
4
|
-
* When useFetch() detects a component context via getCurrentComponentContext(),
|
|
5
|
-
* it returns reactive UseFetchReactiveReturn (ReactiveState refs) and registers
|
|
6
|
-
* useOnConnected to trigger the fetch automatically on mount.
|
|
7
|
-
*
|
|
8
|
-
* These tests mock @jasonshimmy/custom-elements-runtime to simulate a
|
|
9
|
-
* component render environment without a real DOM.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
13
|
-
|
|
14
|
-
// ─── Mock @jasonshimmy/custom-elements-runtime ────────────────────────────────
|
|
15
|
-
|
|
16
|
-
// Capture useOnConnected callbacks so tests can trigger them manually.
|
|
17
|
-
let _connectedCallbacks: Array<() => void | Promise<void>> = []
|
|
18
|
-
|
|
19
|
-
vi.mock('@jasonshimmy/custom-elements-runtime', () => ({
|
|
20
|
-
// Return a non-null context so useFetch takes the component path.
|
|
21
|
-
getCurrentComponentContext: vi.fn().mockReturnValue({ _id: 'test-component' }),
|
|
22
|
-
// createComposable: run factory immediately, ignore explicit ctx arg.
|
|
23
|
-
createComposable: (fn: () => unknown) => (_ctx?: unknown) => fn(),
|
|
24
|
-
// Minimal ref: plain object with a settable .value accessor.
|
|
25
|
-
ref: (initialValue: unknown) => {
|
|
26
|
-
let _val = initialValue
|
|
27
|
-
return {
|
|
28
|
-
get value() { return _val },
|
|
29
|
-
set value(v: unknown) { _val = v },
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
// Capture callbacks for manual triggering in tests.
|
|
33
|
-
useOnConnected: (cb: () => void | Promise<void>) => {
|
|
34
|
-
_connectedCallbacks.push(cb)
|
|
35
|
-
},
|
|
36
|
-
}))
|
|
37
|
-
|
|
38
|
-
import { useFetch } from '../../runtime/composables/use-fetch.js'
|
|
39
|
-
import type { UseFetchReactiveReturn } from '../../runtime/composables/use-fetch.js'
|
|
40
|
-
|
|
41
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
42
|
-
|
|
43
|
-
const g = globalThis as Record<string, unknown>
|
|
44
|
-
|
|
45
|
-
/** Flush all pending useOnConnected callbacks registered since last reset. */
|
|
46
|
-
async function triggerConnected() {
|
|
47
|
-
for (const cb of _connectedCallbacks) await cb()
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function mockFetchWith(response: unknown) {
|
|
51
|
-
return vi.fn(async () => response)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
describe('useFetch() — component context path', () => {
|
|
57
|
-
beforeEach(() => {
|
|
58
|
-
_connectedCallbacks = []
|
|
59
|
-
delete g['__CER_FETCH_STORE__']
|
|
60
|
-
delete g['__CER_FETCH_DATA__']
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
afterEach(() => {
|
|
64
|
-
vi.unstubAllGlobals()
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
it('returns reactive state refs (UseFetchReactiveReturn)', () => {
|
|
68
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => [] }))
|
|
69
|
-
|
|
70
|
-
const result = useFetch('/api/items') as UseFetchReactiveReturn
|
|
71
|
-
expect(result).toHaveProperty('data')
|
|
72
|
-
expect(result).toHaveProperty('pending')
|
|
73
|
-
expect(result).toHaveProperty('error')
|
|
74
|
-
expect(result).toHaveProperty('refresh')
|
|
75
|
-
// In component mode the fields are reactive state objects, not plain values.
|
|
76
|
-
expect(typeof (result.data as { value: unknown }).value).not.toBe('undefined')
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
it('initialises data to null and pending to false before mount', () => {
|
|
80
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => [1, 2] }))
|
|
81
|
-
|
|
82
|
-
const result = useFetch('/api/items') as UseFetchReactiveReturn
|
|
83
|
-
expect(result.data.value).toBeNull()
|
|
84
|
-
expect(result.pending.value).toBe(false)
|
|
85
|
-
expect(result.error.value).toBeNull()
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
it('registers useOnConnected so fetch fires on mount', async () => {
|
|
89
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => [1, 2, 3] })
|
|
90
|
-
vi.stubGlobal('fetch', mockFn)
|
|
91
|
-
|
|
92
|
-
useFetch('/api/items')
|
|
93
|
-
expect(_connectedCallbacks).toHaveLength(1)
|
|
94
|
-
|
|
95
|
-
await triggerConnected()
|
|
96
|
-
expect(mockFn).toHaveBeenCalledTimes(1)
|
|
97
|
-
})
|
|
98
|
-
|
|
99
|
-
it('populates data.value after mount fetch completes', async () => {
|
|
100
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => ['a', 'b'] }))
|
|
101
|
-
|
|
102
|
-
const result = useFetch<string[]>('/api/items') as UseFetchReactiveReturn<string[]>
|
|
103
|
-
await triggerConnected()
|
|
104
|
-
|
|
105
|
-
expect(result.data.value).toEqual(['a', 'b'])
|
|
106
|
-
expect(result.pending.value).toBe(false)
|
|
107
|
-
expect(result.error.value).toBeNull()
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('sets error.value on non-ok HTTP response', async () => {
|
|
111
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: false, status: 500, statusText: 'Server Error' }))
|
|
112
|
-
|
|
113
|
-
const result = useFetch('/api/fail') as UseFetchReactiveReturn
|
|
114
|
-
await triggerConnected()
|
|
115
|
-
|
|
116
|
-
expect(result.error.value).toBeInstanceOf(Error)
|
|
117
|
-
expect((result.error.value as Error).message).toContain('500')
|
|
118
|
-
expect(result.data.value).toBeNull()
|
|
119
|
-
})
|
|
120
|
-
|
|
121
|
-
it('lazy: true skips useOnConnected registration', () => {
|
|
122
|
-
const mockFn = vi.fn()
|
|
123
|
-
vi.stubGlobal('fetch', mockFn)
|
|
124
|
-
|
|
125
|
-
useFetch('/api/lazy', { lazy: true })
|
|
126
|
-
expect(_connectedCallbacks).toHaveLength(0)
|
|
127
|
-
expect(mockFn).not.toHaveBeenCalled()
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it('server: false skips useOnConnected registration', () => {
|
|
131
|
-
const mockFn = vi.fn()
|
|
132
|
-
vi.stubGlobal('fetch', mockFn)
|
|
133
|
-
|
|
134
|
-
useFetch('/api/client-only', { server: false })
|
|
135
|
-
expect(_connectedCallbacks).toHaveLength(0)
|
|
136
|
-
})
|
|
137
|
-
|
|
138
|
-
it('refresh() re-issues the fetch and updates data.value', async () => {
|
|
139
|
-
let callCount = 0
|
|
140
|
-
vi.stubGlobal('fetch', vi.fn(async () => {
|
|
141
|
-
callCount++
|
|
142
|
-
return { ok: true, json: async () => callCount }
|
|
143
|
-
}))
|
|
144
|
-
|
|
145
|
-
const result = useFetch<number>('/api/counter') as UseFetchReactiveReturn<number>
|
|
146
|
-
await triggerConnected()
|
|
147
|
-
expect(result.data.value).toBe(1)
|
|
148
|
-
|
|
149
|
-
await result.refresh()
|
|
150
|
-
expect(result.data.value).toBe(2)
|
|
151
|
-
})
|
|
152
|
-
|
|
153
|
-
it('applies transform option', async () => {
|
|
154
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => [1, 2, 3] }))
|
|
155
|
-
|
|
156
|
-
const result = useFetch<number>('/api/nums', {
|
|
157
|
-
transform: (d) => (d as number[]).length,
|
|
158
|
-
}) as UseFetchReactiveReturn<number>
|
|
159
|
-
await triggerConnected()
|
|
160
|
-
|
|
161
|
-
expect(result.data.value).toBe(3)
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('applies pick option', async () => {
|
|
165
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => ({ id: 1, name: 'Alice', secret: 'x' }) }))
|
|
166
|
-
|
|
167
|
-
const result = useFetch('/api/user', { pick: ['id', 'name'] }) as UseFetchReactiveReturn
|
|
168
|
-
await triggerConnected()
|
|
169
|
-
|
|
170
|
-
expect(result.data.value).toEqual({ id: 1, name: 'Alice' })
|
|
171
|
-
expect((result.data.value as Record<string, unknown>)['secret']).toBeUndefined()
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
it('default() sets initial data.value before fetch completes', () => {
|
|
175
|
-
vi.stubGlobal('fetch', vi.fn(() => new Promise(() => {})))
|
|
176
|
-
|
|
177
|
-
const result = useFetch('/api/slow', { default: () => [] }) as UseFetchReactiveReturn
|
|
178
|
-
expect(result.data.value).toEqual([])
|
|
179
|
-
})
|
|
180
|
-
|
|
181
|
-
it('appends query params to the URL', async () => {
|
|
182
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => [] })
|
|
183
|
-
vi.stubGlobal('fetch', mockFn)
|
|
184
|
-
|
|
185
|
-
useFetch('/api/search', { query: { q: 'test', page: '2' } })
|
|
186
|
-
await triggerConnected()
|
|
187
|
-
|
|
188
|
-
const calledUrl = (mockFn.mock.calls[0] as unknown[])[0] as string
|
|
189
|
-
expect(calledUrl).toContain('q=test')
|
|
190
|
-
expect(calledUrl).toContain('page=2')
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('sets error.value when fetch() itself throws (network error)', async () => {
|
|
194
|
-
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new TypeError('Component network failure'))))
|
|
195
|
-
|
|
196
|
-
const result = useFetch('/api/down') as UseFetchReactiveReturn
|
|
197
|
-
await triggerConnected()
|
|
198
|
-
|
|
199
|
-
expect(result.error.value).toBeInstanceOf(Error)
|
|
200
|
-
expect((result.error.value as Error).message).toContain('Component network failure')
|
|
201
|
-
expect(result.data.value).toBeNull()
|
|
202
|
-
})
|
|
203
|
-
|
|
204
|
-
it('accepts a url function and evaluates it on mount', async () => {
|
|
205
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => 'result' })
|
|
206
|
-
vi.stubGlobal('fetch', mockFn)
|
|
207
|
-
|
|
208
|
-
useFetch(() => '/api/computed-url')
|
|
209
|
-
await triggerConnected()
|
|
210
|
-
|
|
211
|
-
const calledUrl = (mockFn.mock.calls[0] as unknown[])[0] as string
|
|
212
|
-
expect(calledUrl).toBe('/api/computed-url')
|
|
213
|
-
})
|
|
214
|
-
})
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* useFetch() composable tests.
|
|
3
|
-
*
|
|
4
|
-
* Exercises:
|
|
5
|
-
* - SSR path (writes to __CER_FETCH_STORE__ ALS)
|
|
6
|
-
* - Client hydration path (reads from globalThis.__CER_FETCH_DATA__)
|
|
7
|
-
* - Client fetch path (native fetch)
|
|
8
|
-
* - Lazy / server:false skip
|
|
9
|
-
* - pick, transform, key, query options
|
|
10
|
-
* - Thenable (await useFetch())
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
|
|
14
|
-
import { useFetch } from '../../runtime/composables/use-fetch.js'
|
|
15
|
-
|
|
16
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
17
|
-
|
|
18
|
-
const g = globalThis as Record<string, unknown>
|
|
19
|
-
|
|
20
|
-
function mockFetchWith(response: unknown) {
|
|
21
|
-
return vi.fn(async () => response)
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/** Runs fn inside a fresh per-request fetch store. */
|
|
25
|
-
async function runWithFetchStore(fn: (map: Map<string, unknown>) => Promise<void>) {
|
|
26
|
-
const { AsyncLocalStorage } = require('node:async_hooks')
|
|
27
|
-
const store = new AsyncLocalStorage()
|
|
28
|
-
const map = new Map<string, unknown>()
|
|
29
|
-
g['__CER_FETCH_STORE__'] = store
|
|
30
|
-
try {
|
|
31
|
-
await store.run(map, () => fn(map))
|
|
32
|
-
} finally {
|
|
33
|
-
delete g['__CER_FETCH_STORE__']
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// ─── SSR path ─────────────────────────────────────────────────────────────────
|
|
38
|
-
|
|
39
|
-
describe('useFetch() — SSR path', () => {
|
|
40
|
-
afterEach(() => {
|
|
41
|
-
delete g['__CER_FETCH_STORE__']
|
|
42
|
-
vi.unstubAllGlobals()
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
it('fetches and resolves data via await', async () => {
|
|
46
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => [{ id: 1 }] }))
|
|
47
|
-
|
|
48
|
-
await runWithFetchStore(async (map) => {
|
|
49
|
-
const result = await useFetch('/api/posts')
|
|
50
|
-
expect(result.data).toEqual([{ id: 1 }])
|
|
51
|
-
expect(result.pending).toBe(false)
|
|
52
|
-
expect(result.error).toBeNull()
|
|
53
|
-
expect(map.has('/api/posts')).toBe(true)
|
|
54
|
-
})
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('caches repeated calls for the same key within one request', async () => {
|
|
58
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => ({ cached: true }) })
|
|
59
|
-
vi.stubGlobal('fetch', mockFn)
|
|
60
|
-
|
|
61
|
-
await runWithFetchStore(async () => {
|
|
62
|
-
await useFetch('/api/item')
|
|
63
|
-
await useFetch('/api/item') // second call — should hit cache
|
|
64
|
-
expect(mockFn).toHaveBeenCalledTimes(1)
|
|
65
|
-
})
|
|
66
|
-
})
|
|
67
|
-
|
|
68
|
-
it('applies pick option', async () => {
|
|
69
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => ({ id: 1, name: 'Alice', secret: 'x' }) }))
|
|
70
|
-
|
|
71
|
-
await runWithFetchStore(async () => {
|
|
72
|
-
const result = await useFetch('/api/user', { pick: ['id', 'name'] })
|
|
73
|
-
expect(result.data).toEqual({ id: 1, name: 'Alice' })
|
|
74
|
-
expect((result.data as Record<string, unknown>)['secret']).toBeUndefined()
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
it('applies transform option', async () => {
|
|
79
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => [1, 2, 3] }))
|
|
80
|
-
|
|
81
|
-
await runWithFetchStore(async () => {
|
|
82
|
-
const result = await useFetch<number>('/api/nums', { transform: (d) => (d as number[]).length })
|
|
83
|
-
expect(result.data).toBe(3)
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
it('sets error on non-ok HTTP response', async () => {
|
|
88
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: false, status: 404, statusText: 'Not Found' }))
|
|
89
|
-
|
|
90
|
-
await runWithFetchStore(async () => {
|
|
91
|
-
const result = await useFetch('/api/missing')
|
|
92
|
-
expect(result.error).toBeInstanceOf(Error)
|
|
93
|
-
expect(result.error!.message).toContain('404')
|
|
94
|
-
expect(result.data).toBeNull()
|
|
95
|
-
})
|
|
96
|
-
})
|
|
97
|
-
|
|
98
|
-
it('lazy: true skips the SSR fetch', async () => {
|
|
99
|
-
const mockFn = vi.fn()
|
|
100
|
-
vi.stubGlobal('fetch', mockFn)
|
|
101
|
-
|
|
102
|
-
await runWithFetchStore(async () => {
|
|
103
|
-
const result = await useFetch('/api/lazy', { lazy: true })
|
|
104
|
-
expect(result.data).toBeNull()
|
|
105
|
-
expect(mockFn).not.toHaveBeenCalled()
|
|
106
|
-
})
|
|
107
|
-
})
|
|
108
|
-
|
|
109
|
-
it('server: false skips the SSR fetch', async () => {
|
|
110
|
-
const mockFn = vi.fn()
|
|
111
|
-
vi.stubGlobal('fetch', mockFn)
|
|
112
|
-
|
|
113
|
-
await runWithFetchStore(async () => {
|
|
114
|
-
await useFetch('/api/client-only', { server: false })
|
|
115
|
-
expect(mockFn).not.toHaveBeenCalled()
|
|
116
|
-
})
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
it('appends query params to the URL', async () => {
|
|
120
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => [] })
|
|
121
|
-
vi.stubGlobal('fetch', mockFn)
|
|
122
|
-
|
|
123
|
-
await runWithFetchStore(async () => {
|
|
124
|
-
await useFetch('/api/search', { query: { q: 'hello', page: '1' } })
|
|
125
|
-
const calledUrl = (mockFn.mock.calls[0] as unknown[])[0] as string
|
|
126
|
-
expect(calledUrl).toContain('q=hello')
|
|
127
|
-
expect(calledUrl).toContain('page=1')
|
|
128
|
-
})
|
|
129
|
-
})
|
|
130
|
-
|
|
131
|
-
it('uses custom key option for caching', async () => {
|
|
132
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => 42 }))
|
|
133
|
-
|
|
134
|
-
await runWithFetchStore(async (map) => {
|
|
135
|
-
await useFetch('/api/value', { key: 'my-key' })
|
|
136
|
-
expect(map.has('my-key')).toBe(true)
|
|
137
|
-
expect(map.has('/api/value')).toBe(false)
|
|
138
|
-
})
|
|
139
|
-
})
|
|
140
|
-
|
|
141
|
-
it('sets error when fetch() throws during SSR (network error)', async () => {
|
|
142
|
-
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new TypeError('SSR network failure'))))
|
|
143
|
-
|
|
144
|
-
await runWithFetchStore(async () => {
|
|
145
|
-
const result = await useFetch('/api/down')
|
|
146
|
-
expect(result.error).toBeInstanceOf(Error)
|
|
147
|
-
expect(result.error!.message).toContain('SSR network failure')
|
|
148
|
-
expect(result.data).toBeNull()
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
it('sends POST with body during SSR', async () => {
|
|
153
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => ({ id: 1 }) })
|
|
154
|
-
vi.stubGlobal('fetch', mockFn)
|
|
155
|
-
|
|
156
|
-
await runWithFetchStore(async () => {
|
|
157
|
-
await useFetch('/api/create', { method: 'POST', body: { title: 'hello' } })
|
|
158
|
-
const [, init] = mockFn.mock.calls[0] as [string, RequestInit]
|
|
159
|
-
expect(init.method).toBe('POST')
|
|
160
|
-
expect(init.body).toBe(JSON.stringify({ title: 'hello' }))
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
// ─── Client hydration path ────────────────────────────────────────────────────
|
|
166
|
-
|
|
167
|
-
describe('useFetch() — client hydration path', () => {
|
|
168
|
-
beforeEach(() => {
|
|
169
|
-
delete g['__CER_FETCH_STORE__']
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
afterEach(() => {
|
|
173
|
-
delete g['__CER_FETCH_DATA__']
|
|
174
|
-
vi.unstubAllGlobals()
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('reads pre-fetched data without issuing a fetch', async () => {
|
|
178
|
-
const mockFn = vi.fn()
|
|
179
|
-
vi.stubGlobal('fetch', mockFn)
|
|
180
|
-
g['__CER_FETCH_DATA__'] = { '/api/posts': [{ id: 1 }] }
|
|
181
|
-
|
|
182
|
-
const result = await useFetch('/api/posts')
|
|
183
|
-
expect(result.data).toEqual([{ id: 1 }])
|
|
184
|
-
expect(mockFn).not.toHaveBeenCalled()
|
|
185
|
-
})
|
|
186
|
-
|
|
187
|
-
it('consumes the hydration key so a second call fetches fresh', async () => {
|
|
188
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => 'fresh' }))
|
|
189
|
-
g['__CER_FETCH_DATA__'] = { '/api/x': 'cached' }
|
|
190
|
-
|
|
191
|
-
const first = await useFetch('/api/x')
|
|
192
|
-
expect(first.data).toBe('cached')
|
|
193
|
-
|
|
194
|
-
const second = await useFetch('/api/x')
|
|
195
|
-
expect(second.data).toBe('fresh')
|
|
196
|
-
})
|
|
197
|
-
})
|
|
198
|
-
|
|
199
|
-
// ─── Client fetch path ────────────────────────────────────────────────────────
|
|
200
|
-
|
|
201
|
-
describe('useFetch() — client fetch path', () => {
|
|
202
|
-
beforeEach(() => {
|
|
203
|
-
delete g['__CER_FETCH_STORE__']
|
|
204
|
-
delete g['__CER_FETCH_DATA__']
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
afterEach(() => {
|
|
208
|
-
vi.unstubAllGlobals()
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
it('issues a fetch and resolves data', async () => {
|
|
212
|
-
vi.stubGlobal('fetch', mockFetchWith({ ok: true, json: async () => ({ value: 7 }) }))
|
|
213
|
-
|
|
214
|
-
const result = await useFetch('/api/val')
|
|
215
|
-
expect(result.data).toEqual({ value: 7 })
|
|
216
|
-
expect(result.pending).toBe(false)
|
|
217
|
-
expect(result.error).toBeNull()
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
it('lazy: true does not auto-fetch on the client', () => {
|
|
221
|
-
const mockFn = vi.fn()
|
|
222
|
-
vi.stubGlobal('fetch', mockFn)
|
|
223
|
-
|
|
224
|
-
const result = useFetch('/api/nofetch', { lazy: true })
|
|
225
|
-
expect(result.pending).toBe(false)
|
|
226
|
-
expect(mockFn).not.toHaveBeenCalled()
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
it('refresh() re-issues the fetch', async () => {
|
|
230
|
-
let callCount = 0
|
|
231
|
-
vi.stubGlobal('fetch', vi.fn(async () => {
|
|
232
|
-
callCount++
|
|
233
|
-
return { ok: true, json: async () => callCount }
|
|
234
|
-
}))
|
|
235
|
-
|
|
236
|
-
const result = await useFetch<number>('/api/counter')
|
|
237
|
-
expect(result.data).toBe(1)
|
|
238
|
-
|
|
239
|
-
await result.refresh()
|
|
240
|
-
expect(result.data).toBe(2)
|
|
241
|
-
})
|
|
242
|
-
|
|
243
|
-
it('default() provides initial value before fetch completes', async () => {
|
|
244
|
-
let resolveFetch!: (v: unknown) => void
|
|
245
|
-
vi.stubGlobal('fetch', vi.fn(() => new Promise((r) => { resolveFetch = r })))
|
|
246
|
-
|
|
247
|
-
const result = useFetch('/api/slow', { default: () => [] as unknown[] })
|
|
248
|
-
expect(result.data).toEqual([])
|
|
249
|
-
expect(result.pending).toBe(true)
|
|
250
|
-
|
|
251
|
-
resolveFetch({ ok: true, json: async () => [] })
|
|
252
|
-
await result
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
it('sets error when fetch() itself throws (network error)', async () => {
|
|
256
|
-
vi.stubGlobal('fetch', vi.fn(() => Promise.reject(new TypeError('Network failure'))))
|
|
257
|
-
|
|
258
|
-
const result = await useFetch('/api/down')
|
|
259
|
-
expect(result.error).toBeInstanceOf(Error)
|
|
260
|
-
expect(result.error!.message).toContain('Network failure')
|
|
261
|
-
expect(result.data).toBeNull()
|
|
262
|
-
expect(result.pending).toBe(false)
|
|
263
|
-
})
|
|
264
|
-
|
|
265
|
-
it('sends POST with JSON body and Content-Type header', async () => {
|
|
266
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => ({ created: true }) })
|
|
267
|
-
vi.stubGlobal('fetch', mockFn)
|
|
268
|
-
|
|
269
|
-
await useFetch('/api/items', { method: 'POST', body: { name: 'test' } })
|
|
270
|
-
|
|
271
|
-
const [, init] = mockFn.mock.calls[0] as [string, RequestInit]
|
|
272
|
-
expect(init.method).toBe('POST')
|
|
273
|
-
expect(init.body).toBe(JSON.stringify({ name: 'test' }))
|
|
274
|
-
expect((init.headers as Record<string, string>)['Content-Type']).toBe('application/json')
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
it('accepts a url function and evaluates it at call time', async () => {
|
|
278
|
-
const mockFn = mockFetchWith({ ok: true, json: async () => 42 })
|
|
279
|
-
vi.stubGlobal('fetch', mockFn)
|
|
280
|
-
|
|
281
|
-
let currentId = 1
|
|
282
|
-
await useFetch(() => `/api/items/${currentId}`)
|
|
283
|
-
|
|
284
|
-
const calledUrl = (mockFn.mock.calls[0] as unknown[])[0] as string
|
|
285
|
-
expect(calledUrl).toBe('/api/items/1')
|
|
286
|
-
})
|
|
287
|
-
|
|
288
|
-
it('refresh() on a hydrated-state result issues a new fetch', async () => {
|
|
289
|
-
// Pre-populate __CER_FETCH_DATA__ so the first call uses the hydrated path
|
|
290
|
-
g['__CER_FETCH_DATA__'] = { '/api/item': { id: 1 } }
|
|
291
|
-
|
|
292
|
-
let callCount = 0
|
|
293
|
-
vi.stubGlobal('fetch', vi.fn(async () => {
|
|
294
|
-
callCount++
|
|
295
|
-
return { ok: true, json: async () => ({ id: callCount + 10 }) }
|
|
296
|
-
}))
|
|
297
|
-
|
|
298
|
-
const result = await useFetch<{ id: number }>('/api/item')
|
|
299
|
-
// Should have consumed the hydrated value, not fetched
|
|
300
|
-
expect(result.data).toEqual({ id: 1 })
|
|
301
|
-
expect(callCount).toBe(0)
|
|
302
|
-
|
|
303
|
-
// refresh() should now issue a real fetch
|
|
304
|
-
await result.refresh()
|
|
305
|
-
expect(callCount).toBe(1)
|
|
306
|
-
expect(result.data).toEqual({ id: 11 })
|
|
307
|
-
})
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
// ─── P2-3: Client-side in-flight deduplication ────────────────────────────────
|
|
311
|
-
|
|
312
|
-
describe('useFetch() — client in-flight deduplication (P2-3)', () => {
|
|
313
|
-
beforeEach(() => {
|
|
314
|
-
delete g['__CER_FETCH_STORE__']
|
|
315
|
-
delete g['__CER_FETCH_DATA__']
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
afterEach(() => {
|
|
319
|
-
vi.unstubAllGlobals()
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
it('issues only one HTTP request when two calls with the same key are concurrent', async () => {
|
|
323
|
-
let resolveFirst!: (v: unknown) => void
|
|
324
|
-
let fetchCallCount = 0
|
|
325
|
-
vi.stubGlobal('fetch', vi.fn(() => {
|
|
326
|
-
fetchCallCount++
|
|
327
|
-
return new Promise((resolve) => { resolveFirst = resolve })
|
|
328
|
-
}))
|
|
329
|
-
|
|
330
|
-
// Start two concurrent fetches
|
|
331
|
-
const p1 = useFetch<number[]>('/api/items')
|
|
332
|
-
const p2 = useFetch<number[]>('/api/items')
|
|
333
|
-
|
|
334
|
-
// Resolve the shared in-flight request
|
|
335
|
-
resolveFirst({ ok: true, json: async () => [1, 2, 3] })
|
|
336
|
-
|
|
337
|
-
await p1
|
|
338
|
-
await p2
|
|
339
|
-
|
|
340
|
-
// Only one actual HTTP call despite two useFetch calls
|
|
341
|
-
expect(fetchCallCount).toBe(1)
|
|
342
|
-
expect(p1.data).toEqual([1, 2, 3])
|
|
343
|
-
expect(p2.data).toEqual([1, 2, 3])
|
|
344
|
-
})
|
|
345
|
-
|
|
346
|
-
it('each caller applies its own transform to the shared raw response', async () => {
|
|
347
|
-
let resolveShared!: (v: unknown) => void
|
|
348
|
-
vi.stubGlobal('fetch', vi.fn(() => new Promise((resolve) => { resolveShared = resolve })))
|
|
349
|
-
|
|
350
|
-
const p1 = useFetch<number>('/api/nums', { transform: (d) => (d as number[]).length })
|
|
351
|
-
const p2 = useFetch<number[]>('/api/nums')
|
|
352
|
-
|
|
353
|
-
resolveShared({ ok: true, json: async () => [10, 20, 30] })
|
|
354
|
-
await p1
|
|
355
|
-
await p2
|
|
356
|
-
|
|
357
|
-
// p1 transformed (count), p2 raw
|
|
358
|
-
expect(p1.data).toBe(3)
|
|
359
|
-
expect(p2.data).toEqual([10, 20, 30])
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
it('a second fetch after the first has settled issues a fresh HTTP request', async () => {
|
|
363
|
-
let callCount = 0
|
|
364
|
-
vi.stubGlobal('fetch', vi.fn(async () => {
|
|
365
|
-
callCount++
|
|
366
|
-
return { ok: true, json: async () => callCount }
|
|
367
|
-
}))
|
|
368
|
-
|
|
369
|
-
const first = await useFetch<number>('/api/seq')
|
|
370
|
-
expect(first.data).toBe(1)
|
|
371
|
-
|
|
372
|
-
// Wait a tick — first request is done, _inflight map entry is removed
|
|
373
|
-
const second = await useFetch<number>('/api/seq')
|
|
374
|
-
expect(second.data).toBe(2)
|
|
375
|
-
expect(callCount).toBe(2)
|
|
376
|
-
})
|
|
377
|
-
})
|