Proposal for Svelte 5: Universal Reactivity & Safe SSR Global State? #12947
Replies: 3 comments 3 replies
-
Currently usable sample implementationI'll probably be turning this into some sort of
export const request_store = {
getStore(): symbol | undefined {
throw new Error("AsyncLocalStorage not initialized")
}
}
import { browser } from "$app/environment"
import { request_store } from "$lib/request"
export const app_state = $state(ssr_proxy({
counter: 0,
}))
function ssr_proxy<T extends object>(initial: T) {
if (browser) return { inner: initial }
const map = new WeakMap<symbol, T>()
return {
get inner() {
const req_symbol = request_store.getStore()
if (!req_symbol) throw new Error("Request symbol not initialized")
let value = map.get(req_symbol)
if (!value) {
value = structuredClone(initial)
map.set(req_symbol, value)
}
return value
}
}
}
import { AsyncLocalStorage } from "node:async_hooks"
import { request_store } from "$lib/request"
import { app_state } from "$lib/app_state.svelte"
// app_state.inner -> Error: AsyncLocalStorage not initialized
const local_storage = new AsyncLocalStorage<symbol>()
request_store.getStore = () => local_storage.getStore()
export async function handle({
event,
resolve,
}){
// app_state.inner -> Error: Request store not initialized
return local_storage.run(Symbol(), async () => {
// should always be 0 at the start of a new request
console.log(app_state.inner.counter)
app_state.inner.counter += 1 // works, unique per request
})
}
<script lang="ts">
import { app_state } from "$lib/app_state.svelte"
</script>
<!-- will be 1 on load, and then switch to 0 on client-side hydration -->
{ app_state.inner.counter } |
Beta Was this translation helpful? Give feedback.
-
This is great. I would very much love to not have to wrap everything in |
Beta Was this translation helpful? Give feedback.
-
My two cents on this:
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Important
I have managed to implement most of this proposal in user-land: Check out the
npm
package i've created to help solve this problem https://github.com/AlbertMarashi/safe-ssrContext
The unsafe behavior of global variables during SSR (particularly global stores) has been one of the most persistently complained about issues with SvelteKit (sveltejs/kit#4339).
The way stores are currently implemented result in state being leaked across requests which presents a security risk especially for users that are unaware of this difference of behavior in client-side vs server-side apps.
Solution
Using
AsyncLocalStorage
(node:async_hooks
) to we can implement cross-request isolation for stores, which is supported by every major server runtime.AsyncLocalStorage
is not requirednode:async_hooks
node:async_hooks
node:async_hooks
node:async_hooks
node:async_hooks
Rough Proposal / Idea
My idea is that we might be able to use this feature to have Svelte 5 runes (eg:
$state
) work in any javascript modules, including in+page.ts
,+layout.ts
,[Component].svelte
, or any javascript/typescript files such that the data within is guaranteed to be uniquely isolated between requests.Example usage
In practice, I don't expect anything much to change in behavior, except massive improvements in developer ergonomics as it would bridge the gap between client-side and server-side behavior, and make everything a bit more universal.
$lib/foo.ts
+page.ts
+page.svelte
Rough internal mechanism
svelte/internal/state.ts
hooks.server.ts
example to explain the point (although this would be moved into some other internal server-side only module)Benefits
Cannot access 'x' before initialization
Potential caveats & Open Questions
$state
(when used in normal ts/js files) should only support proxy-able types (primitive types like strings and numbers can't be proxied) Pretty sure this is already a limitation with.svelte.ts
files anyways[name].svelte.(ts|js)
files be deprecated?$derived
and other runes?Allowing classes & other non-structured-clonable types to be cloned
Related
Beta Was this translation helpful? Give feedback.
All reactions