Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/kit/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"@playwright/test": "^1.41.0",
"@sveltejs/vite-plugin-svelte": "^3.0.1",
"@types/connect": "^3.4.38",
"@types/node": "^18.19.3",
"@types/node": "^18.19.33",
"@types/sade": "^1.7.8",
"@types/set-cookie-parser": "^2.4.7",
"dts-buddy": "0.4.6",
Expand Down
80 changes: 80 additions & 0 deletions packages/kit/src/runtime/app/stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,83 @@ function get_store(name) {
);
}
}

/**
* @template {keyof Stores}
*/
class ClientStores {
constructor() {
this.store = {}
}

getStore() {
return this.store
}
}


/** @typedef {Record<string, any>} Stores */

const local_stores = new ClientStores();

/** @type {{ getStore: () => Stores }}} */
// eslint-disable-next-line prefer-const
export const stores_object = {
getStore() {
return local_stores.getStore()
}
}

let store_counter = 0;

/**
* @param {any} initialValue
*/
export const unshared_writable = (initialValue) => {
console.log('unshared_writable');
store_counter++;

/** @param {any} newValue */
function set(newValue) {
const stores = stores_object.getStore();
if (!stores) throw new Error('AsyncLocalStorage is not available');

stores[store_counter] ??= { value: initialValue, subscribers: [] };
stores[store_counter].value = newValue;

for (const fn of stores[store_counter].subscribers) {
fn(newValue);
}
}

/** @param {(value: any) => any} fn */
async function update(fn) {
const store = stores_object.getStore();
if (!store) throw new Error('AsyncLocalStorage is not available');
store[store_counter] ??= { value: initialValue, subscribers: [] };
set(fn(store[store_counter].value));
}

/** @param {(value: any) => void} fn */
function subscribe(fn) {
const stores = stores_object.getStore();
if (!stores) throw new Error('AsyncLocalStorage is not available');
stores[store_counter] ??= { value: initialValue, subscribers: [] };
stores[store_counter].subscribers.push(fn);

fn(stores[store_counter].value);

return () => {
const index = stores[store_counter].subscribers.indexOf(fn);
if (index !== -1) {
stores[store_counter].subscribers.splice(index, 1);
}
};
}

return {
subscribe,
set,
update
};
}
16 changes: 12 additions & 4 deletions packages/kit/src/runtime/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ import { DEV } from 'esm-env';
import { filter_private_env, filter_public_env } from '../../utils/env.js';
import { prerendering } from '__sveltekit/environment';
import { set_read_implementation, set_manifest } from '__sveltekit/server';
import { AsyncLocalStorage } from 'node:async_hooks';
import { stores_object } from '../app/stores.js';

const local_stores = new AsyncLocalStorage();

/** @type {ProxyHandler<{ type: 'public' | 'private' }>} */
const prerender_env_handler = {
Expand Down Expand Up @@ -95,10 +99,14 @@ export class Server {
* @param {import('types').RequestOptions} options
*/
async respond(request, options) {
return respond(request, this.#options, this.#manifest, {
...options,
error: false,
depth: 0
return local_stores.run({}, async () => {
stores_object.getStore = () => local_stores.getStore()

return respond(request, this.#options, this.#manifest, {
...options,
error: false,
depth: 0
});
});
}
}
12 changes: 7 additions & 5 deletions packages/kit/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1828,7 +1828,7 @@ declare module '@sveltejs/kit' {
export type NumericRange<TStart extends number, TEnd extends number> = Exclude<TEnd | LessThan<TEnd>, LessThan<TStart>>;
export const VERSION: string;
class HttpError_1 {

constructor(status: number, body: {
message: string;
} extends App.Error ? (App.Error | string | undefined) : App.Error);
Expand All @@ -1837,7 +1837,7 @@ declare module '@sveltejs/kit' {
toString(): string;
}
class Redirect_1 {

constructor(status: 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308, location: string);
status: 301 | 302 | 303 | 307 | 308 | 300 | 304 | 305 | 306;
location: string;
Expand Down Expand Up @@ -2180,11 +2180,11 @@ declare module '$app/server' {

declare module '$app/stores' {
export function getStores(): {

page: typeof page;

navigating: typeof navigating;

updated: typeof updated;
};
/**
Expand All @@ -2210,6 +2210,8 @@ declare module '$app/stores' {
export const updated: import('svelte/store').Readable<boolean> & {
check(): Promise<boolean>;
};

export const unshared_writable: (initialValue: any) => import('svelte/store').Writable<any>;
}/**
* It's possible to tell SvelteKit how to type objects inside your app by declaring the `App` namespace. By default, a new project will have a file called `src/app.d.ts` containing the following:
*
Expand Down
5 changes: 5 additions & 0 deletions playgrounds/basic/src/lib/global_store.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// import { writable } from 'svelte/store';

import { unshared_writable } from '$app/stores';

export const global_counter = unshared_writable(0);
12 changes: 10 additions & 2 deletions playgrounds/basic/src/routes/+page.svelte
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
<h1>Welcome to SvelteKit</h1>
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
<script>
import { global_counter } from '$lib/global_store';

global_counter.subscribe(count => console.log("counter updated", count));

</script>
<h1>{$global_counter}</h1>

<button on:click={() => global_counter.set(1)}>set</button>
<button on:click={() => global_counter.update((count) => count + 1)}>update</button>
6 changes: 6 additions & 0 deletions playgrounds/basic/src/routes/+page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { global_counter } from '$lib/global_store';

export async function load() {
// increment the counter on every page load, but it will always stay at 1 because requests are isolated
global_counter.update((count) => count + 1);
}