elliott/resources
Elliott Johnson 7 days ago
parent cc74b6dfd5
commit 85abdbdd54

@ -143,7 +143,7 @@ This restriction only applies when using the `experimental.async` option, which
### fn_unavailable_on_client ### fn_unavailable_on_client
``` ```
`%name%`(...) is unavailable on the client. `%name%`(...) is unavailable in the browser.
``` ```
### fork_discarded ### fork_discarded

@ -29,7 +29,7 @@ First set occurred at:
%stack% %stack%
``` ```
This error occurs when using `hydratable` or `setHydratableValue` multiple times with the same key. To avoid this, you can combine `hydratable` with `cache`, or check whether the value has already been set with `hasHydratableValue`. This error occurs when using `hydratable` or `hydratable.set` multiple times with the same key. To avoid this, you can combine `hydratable` with `cache`, or check whether the value has already been set with `hydratable.has`.
```svelte ```svelte
<script> <script>

@ -246,13 +246,13 @@ export function flush_sync_in_effect() {
} }
/** /**
* `%name%`(...) is unavailable on the client. * `%name%`(...) is unavailable in the browser.
* @param {string} name * @param {string} name
* @returns {never} * @returns {never}
*/ */
export function fn_unavailable_on_client(name) { export function fn_unavailable_on_client(name) {
if (DEV) { if (DEV) {
const error = new Error(`fn_unavailable_on_client\n\`${name}\`(...) is unavailable on the client.\nhttps://svelte.dev/e/fn_unavailable_on_client`); const error = new Error(`fn_unavailable_on_client\n\`${name}\`(...) is unavailable in the browser.\nhttps://svelte.dev/e/fn_unavailable_on_client`);
error.name = 'Svelte error'; error.name = 'Svelte error';

@ -1,6 +1,6 @@
/** @import { Source, Derived } from '#client' */ /** @import { Source, Derived } from '#client' */
/** @import { Resource as ResourceType } from '#shared' */ /** @import { Resource as ResourceType } from '#shared' */
import { state, derived, set, get, tick } from '../index.js'; import { state, derived, set, get, tick, proxy } from '../index.js';
import { deferred } from '../../shared/utils.js'; import { deferred } from '../../shared/utils.js';
import { async_mode_flag } from '../../flags/index.js'; import { async_mode_flag } from '../../flags/index.js';
import * as e from '../errors.js'; import * as e from '../errors.js';
@ -49,10 +49,24 @@ class Resource {
/** {@type Source<any>} */ /** {@type Source<any>} */
#error = state(undefined); #error = state(undefined);
/** @type {Source<((oldValue: T) => T)[]>} */
#overrides = state(proxy([]));
/** @type {Derived<T | undefined>} */
#current = derived(() => {
return get(this.#ready)
? get(this.#overrides).reduce((v, r) => r(v), /** @type {T} */ (get(this.#raw)))
: undefined;
});
/** @type {Derived<Promise<T>['then']>} */ /** @type {Derived<Promise<T>['then']>} */
// @ts-ignore
#then = derived(() => { #then = derived(() => {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
get(this.#overrides).length;
const p = get(this.#promise); const p = get(this.#promise);
return (resolve, reject) => p.then(resolve, reject); return (resolve, reject) =>
p.then(() => resolve?.(/** @type {T} */ (get(this.#current))), reject);
}); });
/** /**
@ -126,7 +140,7 @@ class Resource {
} }
get current() { get current() {
return get(this.#ready) ? get(this.#raw) : undefined; return get(this.#current);
} }
get error() { get error() {
@ -166,6 +180,26 @@ class Resource {
set(this.#raw, value); set(this.#raw, value);
set(this.#promise, Promise.resolve(value)); set(this.#promise, Promise.resolve(value));
}; };
/**
* @param {(oldValue: T) => T} fn
* @param {Promise<T>} promise
*/
withOverride(fn, promise) {
get(this.#overrides).push(fn);
const rollback = () => {
const i = get(this.#overrides).indexOf(fn);
if (i !== -1) {
get(this.#overrides).splice(i, 1);
}
};
promise.then((result) => {
this.set(result);
rollback();
}, rollback);
}
} }
/** @returns {Promise<void>} */ /** @returns {Promise<void>} */

@ -99,6 +99,10 @@ class Resource {
this.#current = value; this.#current = value;
this.#promise = Promise.resolve(); this.#promise = Promise.resolve();
}; };
withOverride = () => {
throw new Error('TODO Cannot override a resource on the server');
};
} }
export function refreshAll() { export function refreshAll() {

@ -40,6 +40,7 @@ export type Resource<T> = {
set: (value: Awaited<T>) => void; set: (value: Awaited<T>) => void;
loading: boolean; loading: boolean;
error: any; error: any;
withOverride(fn: (oldValue: Awaited<T>) => Awaited<T>, promise: Promise<Awaited<T>>): void;
} & ( } & (
| { | {
ready: false; ready: false;

@ -2203,6 +2203,7 @@ declare module 'svelte/reactivity' {
set: (value: Awaited<T>) => void; set: (value: Awaited<T>) => void;
loading: boolean; loading: boolean;
error: any; error: any;
withOverride(fn: (oldValue: Awaited<T>) => Awaited<T>, promise: Promise<Awaited<T>>): void;
} & ( } & (
| { | {
ready: false; ready: false;
@ -2215,8 +2216,6 @@ declare module 'svelte/reactivity' {
); );
type GetRequestInit = Omit<RequestInit, 'method' | 'body'> & { method?: 'GET' }; type GetRequestInit = Omit<RequestInit, 'method' | 'body'> & { method?: 'GET' };
type CacheEntry = { count: number; item: any };
/** /**
* 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))
@ -2480,35 +2479,22 @@ declare module 'svelte/reactivity' {
* @since 5.7.0 * @since 5.7.0
*/ */
export function createSubscriber(start: (update: () => void) => (() => void) | void): () => void; export function createSubscriber(start: (update: () => void) => (() => void) | void): () => void;
export function resource<T>(fn: () => T): Resource<T>; export class ReactiveCache<K = any, V = any> {
register(key: K, fn: () => V): V;
[Symbol.iterator](): Generator<V, void, unknown>;
#private;
}
export function fetcher<TReturn>(url: string | URL, init?: GetRequestInit | undefined): Resource<TReturn>; export function fetcher<TReturn>(url: string | URL, init?: GetRequestInit | undefined): Resource<TReturn>;
export function cache<TFn extends (...args: any[]) => any>(key: string, fn: TFn): ReturnType<TFn>; export function resource<T>(fn: () => T): Resource<T>;
export class CacheObserver<T> extends BaseCacheObserver<T> { export function refreshAll(): Promise<void>;
constructor(prefix?: string);
}
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;
} }
class BaseCacheObserver<T> implements ReadonlyMap<string, T> {
constructor(get_cache: () => Map<string, CacheEntry>, prefix?: string | undefined);
get(key: string): any;
has(key: string): boolean;
get size(): number;
forEach(cb: (item: T, key: string, map: ReadonlyMap<string, T>) => void): void;
entries(): Generator<[string, T], undefined, unknown>;
keys(): Generator<string, undefined, unknown>;
values(): Generator<T, undefined, unknown>;
[Symbol.iterator](): Generator<[string, T], undefined, unknown>;
#private;
}
export {}; export {};
} }

Loading…
Cancel
Save