diff --git a/packages/svelte/src/internal/client/context.js b/packages/svelte/src/internal/client/context.js index be17fd5cd6..0965da2260 100644 --- a/packages/svelte/src/internal/client/context.js +++ b/packages/svelte/src/internal/client/context.js @@ -226,7 +226,10 @@ export function is_runes() { /** * @template T - * @type {Hydratable} + * @param {string} key + * @param {() => T} fn + * @param {{ transport?: Transport }} [options] + * @returns {Promise} */ export function hydratable(key, fn, { transport } = {}) { if (!hydrating) { diff --git a/packages/svelte/src/internal/client/reactivity/resources/create-fetcher.js b/packages/svelte/src/internal/client/reactivity/resources/create-fetcher.js index 4e99300f1c..bba3d46529 100644 --- a/packages/svelte/src/internal/client/reactivity/resources/create-fetcher.js +++ b/packages/svelte/src/internal/client/reactivity/resources/create-fetcher.js @@ -2,7 +2,7 @@ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */ import { compile } from 'path-to-regexp'; -import { create_resource } from './create-resource.js'; +import { create_resource } from './define-resource.js'; /** * @template {Record} TPathParams diff --git a/packages/svelte/src/internal/client/reactivity/resources/create-resource.js b/packages/svelte/src/internal/client/reactivity/resources/define-resource.js similarity index 74% rename from packages/svelte/src/internal/client/reactivity/resources/create-resource.js rename to packages/svelte/src/internal/client/reactivity/resources/define-resource.js index 1f849f9df3..25fe537d58 100644 --- a/packages/svelte/src/internal/client/reactivity/resources/create-resource.js +++ b/packages/svelte/src/internal/client/reactivity/resources/define-resource.js @@ -1,6 +1,7 @@ +/** @import { Transport } from '#shared' */ import { hydratable } from '../../context.js'; -import { tick } from '../../runtime'; -import { render_effect } from '../effects'; +import { tick } from '../../runtime.js'; +import { render_effect } from '../effects.js'; import { Resource } from './resource.js'; /** @typedef {{ count: number, resource: Resource }} Entry */ @@ -12,14 +13,14 @@ const cache = new Map(); * @template {unknown[]} [TArgs=[]] * @template {typeof Resource} [TResource=typeof Resource] * @param {string} name - * @param {(...args: TArgs) => Promise} fn - * @param {{ Resource?: TResource }} [options] + * @param {(...args: TArgs) => TReturn} fn + * @param {{ Resource?: TResource, transport?: Transport }} [options] * @returns {(...args: TArgs) => Resource} */ -export function create_resource(name, fn, options) { +export function define_resource(name, fn, options = {}) { const ResolvedResource = options?.Resource ?? Resource; return (...args) => { - const stringified_args = JSON.stringify(args); + const stringified_args = (options.transport?.stringify ?? JSON.stringify)(args); const cache_key = `${name}:${stringified_args}`; let entry = cache.get(cache_key); const maybe_remove = create_remover(cache_key); @@ -41,7 +42,9 @@ export function create_resource(name, fn, options) { let resource = entry?.resource; if (!resource) { - resource = new ResolvedResource(() => hydratable(cache_key, () => fn(...args))); + resource = new ResolvedResource(() => + hydratable(cache_key, () => fn(...args), { transport: options.transport }) + ); const entry = { resource, count: tracking ? 1 : 0 diff --git a/packages/svelte/src/internal/client/reactivity/resources/resource.js b/packages/svelte/src/internal/client/reactivity/resources/resource.js index 48e6a383ea..6fda261240 100644 --- a/packages/svelte/src/internal/client/reactivity/resources/resource.js +++ b/packages/svelte/src/internal/client/reactivity/resources/resource.js @@ -1,6 +1,6 @@ /** @import { Source, Derived } from '#client' */ import { state, derived, set, get, tick } from '../../index.js'; -import { deferred, noop } from '../../../shared/utils.js'; +import { deferred } from '../../../shared/utils.js'; /** * @template T diff --git a/packages/svelte/src/internal/server/context.js b/packages/svelte/src/internal/server/context.js index 93bd3479be..91299b12c1 100644 --- a/packages/svelte/src/internal/server/context.js +++ b/packages/svelte/src/internal/server/context.js @@ -1,6 +1,6 @@ /** @import { ALSContext, SSRContext } from '#server' */ /** @import { AsyncLocalStorage } from 'node:async_hooks' */ -/** @import { Hydratable } from '#shared' */ +/** @import { Transport } from '#shared' */ import { DEV } from 'esm-env'; import * as e from './errors.js'; @@ -127,7 +127,10 @@ export async function save(promise) { /** * @template T - * @type {Hydratable} + * @param {string} key + * @param {() => T} fn + * @param {{ transport?: Transport }} [options] + * @returns {Promise} */ export async function hydratable(key, fn, { transport } = {}) { const store = await get_render_store(); diff --git a/packages/svelte/src/internal/server/types.d.ts b/packages/svelte/src/internal/server/types.d.ts index d8698fd4a4..0c45c0adc5 100644 --- a/packages/svelte/src/internal/server/types.d.ts +++ b/packages/svelte/src/internal/server/types.d.ts @@ -20,7 +20,7 @@ export interface ALSContext { string, { value: MaybePromise; - transport: Transport | undefined; + transport: Transport | undefined; } >; } diff --git a/packages/svelte/src/internal/shared/types.d.ts b/packages/svelte/src/internal/shared/types.d.ts index 3823eec5c9..52456a814d 100644 --- a/packages/svelte/src/internal/shared/types.d.ts +++ b/packages/svelte/src/internal/shared/types.d.ts @@ -11,13 +11,7 @@ export type Snapshot = ReturnType>; export type MaybePromise = T | Promise; -export type Hydratable = ( - key: string, - fn: () => T, - options?: { transport?: Transport } -) => Promise; - -export type Transport = { - stringify: (value: T) => string; - parse: (value: string) => T; +export type Transport = { + stringify: (value: unknown) => string; + parse: (value: string) => unknown; }; diff --git a/packages/svelte/src/reactivity/index-client.js b/packages/svelte/src/reactivity/index-client.js index 42cd28658b..fe5318dae3 100644 --- a/packages/svelte/src/reactivity/index-client.js +++ b/packages/svelte/src/reactivity/index-client.js @@ -6,5 +6,5 @@ export { SvelteURLSearchParams } from './url-search-params.js'; export { MediaQuery } from './media-query.js'; export { createSubscriber } from './create-subscriber.js'; export { Resource } from '../internal/client/reactivity/resources/resource.js'; -export { create_resource as createResource } from '../internal/client/reactivity/resources/create-resource.js'; +export { define_resource as defineResource } from '../internal/client/reactivity/resources/define-resource.js'; export { create_fetcher as createFetcher } from '../internal/client/reactivity/resources/create-fetcher.js'; diff --git a/packages/svelte/src/reactivity/index-server.js b/packages/svelte/src/reactivity/index-server.js index 29da641110..1d0d5c8226 100644 --- a/packages/svelte/src/reactivity/index-server.js +++ b/packages/svelte/src/reactivity/index-server.js @@ -1,5 +1,6 @@ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */ -import { compile } from 'path-to-regexp'; +/** @import { Transport } from '#shared' */ +import { uneval } from 'devalue'; import { hydratable } from '../internal/server/context.js'; export const SvelteDate = globalThis.Date; @@ -122,20 +123,22 @@ const cache = new Map(); * @template {unknown[]} [TArgs=[]] * @template {typeof Resource} [TResource=typeof Resource] * @param {string} name - * @param {(...args: TArgs) => Promise} fn - * @param {{ Resource?: TResource }} [options] + * @param {(...args: TArgs) => TReturn} fn + * @param {{ Resource?: TResource, transport?: Transport }} [options] * @returns {(...args: TArgs) => Resource} */ -export function createResource(name, fn, options) { +export function defineResource(name, fn, options = {}) { const ResolvedResource = options?.Resource ?? Resource; return (...args) => { - const stringified_args = JSON.stringify(args); + const stringified_args = (options.transport?.stringify ?? JSON.stringify)(args); const cache_key = `${name}:${stringified_args}`; const entry = cache.get(cache_key); if (entry) { return entry; } - const resource = new ResolvedResource(() => hydratable(cache_key, () => fn(...args))); + const resource = new ResolvedResource(() => + hydratable(cache_key, () => fn(...args), { transport: options.transport }) + ); cache.set(cache_key, resource); return resource; };