chore: temporarily remove fetcher

pull/17124/head
Elliott Johnson 1 month ago
parent bca87b92bf
commit 82be3881b2

@ -1,66 +0,0 @@
/** @import { Resource } from './resource.js' */
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
import { compile } from 'path-to-regexp';
import { create_resource } from './define-resource.js';
/**
* @template {Record<string, unknown>} TPathParams
* @typedef {{ searchParams?: ConstructorParameters<typeof URLSearchParams>[0], pathParams?: TPathParams } & RequestInit} FetcherInit
*/
/**
* @template TReturn
* @template {Record<string, unknown>} TPathParams
* @typedef {(init: FetcherInit<TPathParams>) => Resource<TReturn>} Fetcher
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {typeof Resource} TResource
* @overload
* @param {string} url
* @param {{ Resource?: TResource, schema?: undefined }} [options] - TODO what options should we support?
* @returns {Fetcher<string | number | Record<string, unknown> | unknown[] | boolean | null, TPathParams>} - TODO this return type has to be gnarly unless we do schema validation, as it could be any JSON value (including string, number, etc)
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {StandardSchemaV1} TSchema
* @template {typeof Resource} TResource
* @overload
* @param {string} url
* @param {{ Resource?: TResource, schema: StandardSchemaV1 }} options
* @returns {Fetcher<StandardSchemaV1.InferOutput<TSchema>, TPathParams>} - TODO this return type has to be gnarly unless we do schema validation, as it could be any JSON value (including string, number, etc)
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {typeof Resource} TResource
* @template {StandardSchemaV1} TSchema
* @param {string} url
* @param {{ Resource?: TResource, schema?: StandardSchemaV1 }} [options]
*/
export function create_fetcher(url, options) {
const raw_pathname = url.split('//')[1].match(/(\/[^?#]*)/)?.[1] ?? '';
const populate_path = compile(raw_pathname);
/**
* @param {Parameters<Fetcher<any, any>>[0]} args
* @returns {Promise<any>}
*/
const fn = async (args) => {
const cloned_url = new URL(url);
const new_params = new URLSearchParams(args.searchParams);
const combined_params = new URLSearchParams([...cloned_url.searchParams, ...new_params]);
cloned_url.search = combined_params.toString();
cloned_url.pathname = populate_path(args.pathParams ?? {}); // TODO we definitely should get rid of this lib for launch, I just wanted to play with the API
// TODO how to populate path params
const resp = await fetch(cloned_url, args);
if (!resp.ok) {
throw new Error(`Fetch error: ${resp.status} ${resp.statusText}`);
}
const json = await resp.json();
if (options?.schema) {
return options.schema['~standard'].validate(json);
}
return json;
};
return create_resource(url.toString(), fn, options);
}

@ -132,8 +132,8 @@ export async function save(promise) {
* @param {{ transport?: Transport }} [options] * @param {{ transport?: Transport }} [options]
* @returns {Promise<T>} * @returns {Promise<T>}
*/ */
export async function hydratable(key, fn, { transport } = {}) { export function hydratable(key, fn, { transport } = {}) {
const store = await get_render_store(); const store = get_render_store();
if (store.hydratables.has(key)) { if (store.hydratables.has(key)) {
// TODO error // TODO error
@ -142,7 +142,7 @@ export async function hydratable(key, fn, { transport } = {}) {
const result = fn(); const result = fn();
store.hydratables.set(key, { value: result, transport }); store.hydratables.set(key, { value: result, transport });
return result; return Promise.resolve(result);
} }
/** @type {ALSContext | null} */ /** @type {ALSContext | null} */
@ -153,28 +153,30 @@ export function set_sync_store(store) {
sync_store = store; sync_store = store;
} }
/** @type {Promise<AsyncLocalStorage<ALSContext | null> | null>} */ /** @type {AsyncLocalStorage<ALSContext | null> | null} */
const als = import('node:async_hooks') let als = null;
.then((hooks) => new hooks.AsyncLocalStorage())
import('node:async_hooks')
.then((hooks) => (als = new hooks.AsyncLocalStorage()))
.catch(() => { .catch(() => {
// can't use ALS but can still use manual context preservation // can't use ALS but can still use manual context preservation
return null; return null;
}); });
/** @returns {Promise<ALSContext | null>} */ /** @returns {ALSContext | null} */
async function try_get_render_store() { function try_get_render_store() {
return sync_store ?? (await als)?.getStore() ?? null; return sync_store ?? als?.getStore() ?? null;
} }
/** @returns {Promise<ALSContext>} */ /** @returns {ALSContext} */
export async function get_render_store() { export function get_render_store() {
const store = await try_get_render_store(); const store = try_get_render_store();
if (!store) { if (!store) {
// TODO make this a proper e.error // TODO make this a proper e.error
let message = 'Could not get rendering context.'; let message = 'Could not get rendering context.';
if (await als) { if (als) {
message += ' This is an internal error.'; message += ' This is an internal error.';
} else { } else {
message += message +=
@ -194,10 +196,10 @@ export async function get_render_store() {
* @param {() => Promise<T>} fn * @param {() => Promise<T>} fn
* @returns {Promise<T>} * @returns {Promise<T>}
*/ */
export async function with_render_store(store, fn) { export function with_render_store(store, fn) {
try { try {
sync_store = store; sync_store = store;
const storage = await als; const storage = als;
return storage ? storage.run(store, fn) : fn(); return storage ? storage.run(store, fn) : fn();
} finally { } finally {
sync_store = null; sync_store = null;

@ -375,7 +375,7 @@ export class Renderer {
}); });
return Promise.resolve(user_result); return Promise.resolve(user_result);
} }
async ??= with_render_store({ hydratables: new Map() }, () => async ??= with_render_store({ hydratables: new Map(), resources: new Map() }, () =>
Renderer.#render_async(component, options) Renderer.#render_async(component, options)
); );
return async.then((result) => { return async.then((result) => {
@ -523,7 +523,7 @@ export class Renderer {
} }
async #collect_hydratables() { async #collect_hydratables() {
const map = (await get_render_store()).hydratables; const map = get_render_store().hydratables;
/** @type {(value: unknown) => string} */ /** @type {(value: unknown) => string} */
let default_stringify; let default_stringify;

@ -1,4 +1,5 @@
import type { MaybePromise, Transport } from '#shared'; import type { MaybePromise, Transport } from '#shared';
import type { Resource } from '../../reactivity/index-server';
import type { Element } from './dev'; import type { Element } from './dev';
import type { Renderer } from './renderer'; import type { Renderer } from './renderer';
@ -23,6 +24,7 @@ export interface ALSContext {
transport: Transport | undefined; transport: Transport | undefined;
} }
>; >;
resources: Map<string, Resource<any>>;
} }
export interface SyncRenderOutput { export interface SyncRenderOutput {

@ -7,4 +7,3 @@ export { MediaQuery } from './media-query.js';
export { createSubscriber } from './create-subscriber.js'; export { createSubscriber } from './create-subscriber.js';
export { Resource } from '../internal/client/reactivity/resources/resource.js'; export { Resource } from '../internal/client/reactivity/resources/resource.js';
export { define_resource as defineResource } from '../internal/client/reactivity/resources/define-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';

@ -1,7 +1,7 @@
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */ /** @import { StandardSchemaV1 } from '@standard-schema/spec' */
/** @import { Transport } from '#shared' */ /** @import { Transport } from '#shared' */
import { uneval } from 'devalue'; import { uneval } from 'devalue';
import { hydratable } from '../internal/server/context.js'; import { get_render_store, hydratable } from '../internal/server/context.js';
export const SvelteDate = globalThis.Date; export const SvelteDate = globalThis.Date;
export const SvelteSet = globalThis.Set; export const SvelteSet = globalThis.Set;
@ -114,10 +114,6 @@ export class Resource {
}; };
} }
/** @type {Map<string, Resource<any>>} */
// TODO scope to render, clear after render
const cache = new Map();
/** /**
* @template TReturn * @template TReturn
* @template {unknown[]} [TArgs=[]] * @template {unknown[]} [TArgs=[]]
@ -130,6 +126,7 @@ const cache = new Map();
export function defineResource(name, fn, options = {}) { export function defineResource(name, fn, options = {}) {
const ResolvedResource = options?.Resource ?? Resource; const ResolvedResource = options?.Resource ?? Resource;
return (...args) => { return (...args) => {
const cache = get_render_store().resources;
const stringified_args = (options.transport?.stringify ?? JSON.stringify)(args); const stringified_args = (options.transport?.stringify ?? JSON.stringify)(args);
const cache_key = `${name}:${stringified_args}`; const cache_key = `${name}:${stringified_args}`;
const entry = cache.get(cache_key); const entry = cache.get(cache_key);
@ -143,64 +140,3 @@ export function defineResource(name, fn, options = {}) {
return resource; return resource;
}; };
} }
/**
* @template {Record<string, unknown>} TPathParams
* @typedef {{ searchParams?: ConstructorParameters<typeof URLSearchParams>[0], pathParams?: TPathParams } & RequestInit} FetcherInit
*/
/**
* @template TReturn
* @template {Record<string, unknown>} TPathParams
* @typedef {(init: FetcherInit<TPathParams>) => Resource<TReturn>} Fetcher
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {typeof Resource} TResource
* @overload
* @param {string} url
* @param {{ Resource?: TResource, schema?: undefined }} [options] - TODO what options should we support?
* @returns {Fetcher<string | number | Record<string, unknown> | unknown[] | boolean | null, TPathParams>} - TODO this return type has to be gnarly unless we do schema validation, as it could be any JSON value (including string, number, etc)
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {StandardSchemaV1} TSchema
* @template {typeof Resource} TResource
* @overload
* @param {string} url
* @param {{ Resource?: TResource, schema: StandardSchemaV1 }} options
* @returns {Fetcher<StandardSchemaV1.InferOutput<TSchema>, TPathParams>} - TODO this return type has to be gnarly unless we do schema validation, as it could be any JSON value (including string, number, etc)
*/
/**
* @template {Record<string, unknown>} TPathParams
* @template {typeof Resource} TResource
* @template {StandardSchemaV1} TSchema
* @param {string} url
* @param {{ Resource?: TResource, schema?: StandardSchemaV1 }} [options]
*/
export function createFetcher(url, options) {
const raw_pathname = url.split('//')[1].match(/(\/[^?#]*)/)?.[1] ?? '';
const populate_path = compile(raw_pathname);
/**
* @param {Parameters<Fetcher<any, any>>[0]} args
* @returns {Promise<any>}
*/
const fn = async (args) => {
const cloned_url = new URL(url);
const new_params = new URLSearchParams(args.searchParams);
const combined_params = new URLSearchParams([...cloned_url.searchParams, ...new_params]);
cloned_url.search = combined_params.toString();
cloned_url.pathname = populate_path(args.pathParams ?? {}); // TODO we definitely should get rid of this lib for launch, I just wanted to play with the API
// TODO how to populate path params
const resp = await fetch(cloned_url, args);
if (!resp.ok) {
throw new Error(`Fetch error: ${resp.status} ${resp.statusText}`);
}
const json = await resp.json();
if (options?.schema) {
return options.schema['~standard'].validate(json);
}
return json;
};
return createResource(url.toString(), fn, options);
}

@ -448,8 +448,6 @@ declare module 'svelte' {
}): Snippet<Params>; }): Snippet<Params>;
/** Anything except a function */ /** Anything except a function */
type NotFunction<T> = T extends Function ? never : T; type NotFunction<T> = T extends Function ? never : T;
type MaybePromise<T> = T | Promise<T>;
/** /**
* Returns a `[get, set]` pair of functions for working with context in a type-safe way. * Returns a `[get, set]` pair of functions for working with context in a type-safe way.
* *
@ -491,7 +489,9 @@ declare module 'svelte' {
* */ * */
export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T; export function getAllContexts<T extends Map<any, any> = Map<any, any>>(): T;
export function hydratable<T>(key: string, fn: () => T): MaybePromise<T>; export function hydratable<T>(key: string, fn: () => T, { transport }?: {
transport?: Transport;
} | undefined): Promise<T>;
/** /**
* Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component. * Mounts a component to the given target and returns the exports and potentially the props (if compiled with `accessors: true`) of the component.
* Transitions will play during the initial render unless the `intro` option is set to `false`. * Transitions will play during the initial render unless the `intro` option is set to `false`.
@ -565,6 +565,11 @@ declare module 'svelte' {
[K in keyof T]: () => T[K]; [K in keyof T]: () => T[K];
}; };
type Transport = {
stringify: (value: unknown) => string;
parse: (value: string) => unknown;
};
export {}; export {};
} }
@ -2143,7 +2148,6 @@ declare module 'svelte/motion' {
} }
declare module 'svelte/reactivity' { declare module 'svelte/reactivity' {
import type { StandardSchemaV1 } from '@standard-schema/spec';
/** /**
* A reactive version of the built-in [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object. * A reactive version of the built-in [`Date`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) object.
* Reading the date (whether with methods like `date.getTime()` or `date.toString()`, or via things like [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat)) * Reading the date (whether with methods like `date.getTime()` or `date.toString()`, or via things like [`Intl.DateTimeFormat`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat))
@ -2429,29 +2433,20 @@ declare module 'svelte/reactivity' {
set: (value: T) => void; set: (value: T) => void;
#private; #private;
} }
export function createResource<TReturn, TArgs extends unknown[] = [], TResource extends typeof Resource = typeof Resource>(name: string, fn: (...args: TArgs) => Promise<TReturn>, options?: { export function defineResource<TReturn, TArgs extends unknown[] = [], TResource extends typeof Resource = typeof Resource>(name: string, fn: (...args: TArgs) => TReturn, options?: {
Resource?: TResource; Resource?: TResource;
transport?: Transport;
} | undefined): (...args: TArgs) => Resource<TReturn>; } | undefined): (...args: TArgs) => Resource<TReturn>;
export function createFetcher<TPathParams extends Record<string, unknown>, TResource extends typeof Resource>(url: string, options?: {
Resource?: TResource;
schema?: undefined;
} | undefined): Fetcher<string | number | Record<string, unknown> | unknown[] | boolean | null, TPathParams>;
export function createFetcher<TPathParams extends Record<string, unknown>, TSchema extends StandardSchemaV1, TResource extends typeof Resource>(url: string, options: {
Resource?: TResource;
schema: StandardSchemaV1;
}): Fetcher<StandardSchemaV1.InferOutput<TSchema>, TPathParams>;
type FetcherInit<TPathParams extends Record<string, unknown>> = {
searchParams?: ConstructorParameters<typeof URLSearchParams>[0];
pathParams?: TPathParams;
} & RequestInit;
type Fetcher<TReturn, TPathParams extends Record<string, unknown>> = (init: FetcherInit<TPathParams>) => Resource<TReturn>;
class ReactiveValue<T> { class ReactiveValue<T> {
constructor(fn: () => T, onsubscribe: (update: () => void) => void); constructor(fn: () => T, onsubscribe: (update: () => void) => void);
get current(): T; get current(): T;
#private; #private;
} }
type Transport = {
stringify: (value: unknown) => string;
parse: (value: string) => unknown;
};
export {}; export {};
} }

Loading…
Cancel
Save