feat(session): add session: false to opt out of session support - #16871
Conversation
🦋 Changeset detectedLatest commit: de80530 The changes in this PR will be included in the next version bump. This PR includes changesets to release 406 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
ascorbic
left a comment
There was a problem hiding this comment.
I think it's a good idea to allow users to explicitly disable sessions, but I have some concerns with this implementation.
Changing the runtime behaviour so that accessing a disabled session throws, rather than making the session undefined, is a breaking change that would need a major, and I think is strictly worse DX. Why should user code need to care about whether the session configuration is false or undefined, and need to provide two code paths for it? It also leads to needing a lot of extra code in this PR. Lots of the code here seems to be devoted to threading through a signal about the difference between these, when the alternative is to just set it to undefined, which is already handled everywhere.
I think the idea of using Vite to enable tree-shaking the session module when it is disabled is good, but should also apply to session: undefined.
I would suggest the following:
- Add the code that handles replacing all the session bundle in SSR when it's not enabled
- Allow passing
falsetoconfig.session. If there is no adapter set then this should behave exactly the same as not setting it, or setting it toundefined. - Your current changes to the adapters are good, and should stay.
Adds a `session: false` config option that opts a project out of session
support entirely, and tree-shakes the session runtime (`AstroSession` +
`unstorage`) out of the SSR bundle for any project where no session driver
is wired.
- `session: false` is accepted by `SessionSchema` and threaded through so
adapters skip auto-wiring their default driver (`@astrojs/cloudflare`,
`@astrojs/netlify`, `@astrojs/node`).
- When no driver will be present at request time — `session: false`, no
`session` config at all, or a `session` object without a driver —
`Astro.session` (and `context.session`) is `undefined`, matching its
existing `AstroSession | undefined` type. This keeps the established
`if (Astro.session)` feature-detection contract instead of throwing.
- A new `astro:session-provider` Vite plugin swaps Astro's own
`core/session/provider.js` for a runtime-free `provider-disabled.js`
stub whenever no driver is wired, so Rollup drops `runtime.js` and
`unstorage` from the bundle. Adapters wire their default driver during
`astro:config:setup` (before `createVite`), so `config.session?.driver`
reflects the final decision by resolve time — the same signal the
driver virtual module uses. The swap is behavior-preserving: the real
provider already resolves the session to `undefined` when no driver
factory exists, so this only drops now-dead code.
```js title="astro.config.mjs"
import { defineConfig } from 'astro/config';
export default defineConfig({
session: false,
});
```
Projects that already wire a session driver see no behavior change; the
runtime is retained whenever a driver is configured.
|
Marking this for 7.2 |
…re builds Without a configured session driver, @astrojs/cloudflare force-enables KV-backed sessions and declares a SESSION kv_namespaces entry in the generated wrangler config, so `wrangler deploy` demands a real KV namespace nothing reads; it also defaults images to the runtime Cloudflare Images binding. Blume never reads `Astro.session` and every HTML route prerenders (the only server routes are API endpoints), so the generated Astro config now sets an inert in-memory session driver and `imageService: "compile"`, which pre-optimizes images at build time. The memory driver's entrypoint (unstorage) resolves relative to Astro's own session vite-plugin, so no dependency mirroring is needed. Once withastro/astro#16871 ships a first-class session opt-out in the supported range, the memory driver can be replaced with it. Resolves #117
|
@adamchal here some AI generated findings you might want to look at. Take them with a pinch of salt. The skill is still very new, and it's AI. Findings
|
The `astro:session-provider` plugin compared Vite's resolved id against a path derived from `import.meta.url`. Node's ESM loader canonicalizes `import.meta.url`, but Vite only resolves symlinks when `resolve.preserveSymlinks` is false (the default). Under `preserveSymlinks: true`, a pnpm or workspace link makes the two strings differ even though they name the same file, so the redirect to `provider-disabled.js` silently never happened and the session runtime stayed in the SSR bundle. Canonicalize both sides via `realpathSync` before comparing, falling back to a plain compare for ids that are not real files (bare specifiers, virtual modules). The cheap suffix prefilter now runs first so the added `realpathSync` never touches the filesystem for unrelated imports.
The `session: false` tests used the test adapter, which never supplies a default session driver, so they passed whether or not the Cloudflare, Netlify, and Node guards were present. Drive each adapter's `astro:config:setup` hook with a mock context and assert that `session: false` leaves the config untouched and wires no driver, that a user-supplied driver is still respected, and that the adapter default is still wired when sessions are left unconfigured. For Cloudflare, also assert the config customizer provisions no session KV namespace when the driver is not needed.
|
@ematipico addressed the first and third bullet: both sides of the path comparison now go through |
The `session: false` tests for `@astrojs/node` built their mock project root
from a hardcoded `file:///project/`. That is only absolute on POSIX, so on
Windows the adapter's `fileURLToPath(new URL('sessions', config.cacheDir))`
threw `ERR_INVALID_FILE_URL_PATH`, failing the default-driver test. The other
two cases short-circuit before that call, which is why only one test broke.
Derive the root with `resolve` plus `pathToFileURL` so it carries a drive
letter on Windows. Unlike the Cloudflare and Netlify tests, this hook only
reads strings off the root, so it does not need a real directory on disk.
Addressed
Astro unconditionally bundles the session runtime (
AstroSession+unstorage) into SSR output whenever the session provider is statically imported, even when no driver is ever wired andAstro.sessionis alwaysundefinedat runtime. For projects that never use sessions, this carries cold-start parse cost (especially on serverless/edge), latent attack surface in cookie/session-storage code paths, and a larger dependency tree. There was no first-class opt-out, and no tree-shaking for the common "no sessions configured" case.Changes
session: falseconfig option that opts a project out of session support entirely.AstroSession+unstorage) out of the SSR bundle for any project where no session driver is wired —session: false, nosessionconfig at all, or asessionobject without a driver.Astro.session(andcontext.session) isundefinedwhenever no driver is present, matching its existingAstroSession | undefinedtype. Existingif (Astro.session)feature-detection keeps working; there is no new throwing state to reason about.@astrojs/cloudflare,@astrojs/netlify,@astrojs/node): skip auto-wiring the default session driver whensession: false.Projects that already wire a session driver (user-configured or adapter default) see no behavior change — the runtime is retained whenever a driver is configured.
Testing
test/units/sessions/session-false.test.ts):SessionSchemaparsesfalseand rejects other falsy values;sessionConfigToManifest(false)returnsundefined; the disabled provider registers no session (leavingAstro.sessionundefined) and marks the feature used.test/session-false.test.ts): routes that never readAstro.sessionare unaffected; a route that readsAstro.sessionundersession: falsegetsundefined(200, not a 500); the user's own./session/provider.jsimport is not hijacked by the plugin; the built SSR output contains nocreateStorageand noclass AstroSession.test/session-tree-shake.test.ts): with nosessionconfig and an adapter that wires no driver,Astro.sessionisundefinedand the runtime +unstorageare tree-shaken; with a configured driver,Astro.sessionis available and the runtime +unstorageare retained (guards against over-shaking).Bundle-size measurement
Built the
session-tree-shakefixture with the test adapter (which wires no default driver), comparing no driver vs. a configured driver:unstoragein bundleAstroSessionclass in bundlesessionconfig)session: { driver: 'fs' }This is the floor of the savings measured against a fixture where no adapter auto-wires a driver. On Cloudflare/Netlify/Node with
session: false, the adapter would otherwise also wire its driver (Cloudflare KV / Netlify Blobs / fs-lite) into the bundle, so the real-world delta on those targets is larger.Docs
withastro/docs#14251