add resource

aa-coordination-resource
Dominic Gannaway 8 months ago
parent e69d85b1a0
commit ba5d219a53

@ -219,3 +219,4 @@ export { getContext, getAllContexts, hasContext, setContext } from './internal/c
export { hydrate, mount, unmount } from './internal/client/render.js';
export { tick, untrack } from './internal/client/runtime.js';
export { createRawSnippet } from './internal/client/dom/blocks/snippet.js';
export { Resource } from './internal/client/reactivity/resources.js';

@ -24,6 +24,7 @@ export const EFFECT_PRESERVED = 1 << 21; // effects with this flag should not be
// Flags used for async
export const REACTION_IS_UPDATING = 1 << 22;
export const BOUNDARY_SUSPENDED = 1 << 23;
export const IS_PENDING = 1 << 24;
export const STATE_SYMBOL = Symbol('$state');
export const STATE_SYMBOL_METADATA = Symbol('$state metadata');

@ -0,0 +1,91 @@
/** @import { Derived, Effect, Source } from '#client' */
import { UNINITIALIZED } from '../../../constants';
import { EFFECT_PRESERVED, IS_PENDING } from '../constants';
import { active_effect, captured_signals, get, handle_error } from '../runtime';
import { derived } from './deriveds';
import { block } from './effects';
import { internal_set, source } from './sources';
/**
* @template T
*/
export class Resource {
/** @type {Source<T>} */
#current = source(/** @type {T} */ (UNINITIALIZED));
/** @type {Derived<Promise<T>>} */
#fn;
/** @type {Source<boolean>} */
#pending = source(true);
/** @param {() => Promise<T>} fn */
constructor(fn) {
let parent = /** @type {Effect | null} */ (active_effect);
if (parent === null) {
throw new Error('TODO cannot create resources outside of an effect');
}
/** @type {{}} */
var current_token;
this.#current.f ^= IS_PENDING;
this.#fn = derived(() => Promise.resolve(fn()));
block(() => {
var current = this.#current;
if ((current.f & IS_PENDING) === 0) {
current.f ^= IS_PENDING;
}
var token = (current_token = {});
internal_set(this.#pending, true);
get(this.#fn).then(
(value) => {
if (current_token !== token) return;
internal_set(this.#current, value);
internal_set(this.#pending, false);
this.#current.f ^= IS_PENDING;
return value;
},
(error) => {
if (current_token !== token) return;
internal_set(this.#pending, false);
throw error;
}
).catch((e) => {
handle_error(e, parent, null, parent.ctx);
});
}, EFFECT_PRESERVED);
}
get pending() {
return get(this.#pending);
}
get current() {
var value = get(this.#current);
if (captured_signals !== null) {
get(this.#fn);
}
if (value === UNINITIALIZED) {
return this.#fn.v;
}
return value;
}
get latest() {
var current = this.#current;
var value = get(current);
var promise = get(this.#fn);
if ((current.f & IS_PENDING) === 0) {
return value;
}
return promise;
}
}

@ -1092,7 +1092,7 @@ export function capture_signals(fn) {
var signal;
try {
untrack(fn);
fn();
if (previous_captured_signals !== null) {
for (signal of captured_signals) {
previous_captured_signals.add(signal);

@ -512,6 +512,15 @@ declare module 'svelte' {
* ```
* */
export function untrack<T>(fn: () => T): T;
export function isPending(fn: () => any): boolean;
export class Resource<T> {
constructor(fn: () => Promise<T>);
get current(): T | Promise<Awaited<T>>;
get latest(): T | Promise<Awaited<T>>;
#private;
}
type Getters<T> = {
[K in keyof T]: () => T[K];
};

Loading…
Cancel
Save