@getforma/core 1.0.10 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-T33QUD2Y.cjs → chunk-E3FV2VSU.cjs} +43 -43
- package/dist/{chunk-T33QUD2Y.cjs.map → chunk-E3FV2VSU.cjs.map} +1 -1
- package/dist/chunk-ETPJTPYD.js +103 -0
- package/dist/chunk-ETPJTPYD.js.map +1 -0
- package/dist/chunk-H7MDUHS5.cjs +110 -0
- package/dist/chunk-H7MDUHS5.cjs.map +1 -0
- package/dist/{chunk-6FW5E54W.cjs → chunk-KUXNZ5MG.cjs} +127 -103
- package/dist/chunk-KUXNZ5MG.cjs.map +1 -0
- package/dist/{chunk-3G7ET4O5.js → chunk-Y3A2EVVS.js} +121 -94
- package/dist/chunk-Y3A2EVVS.js.map +1 -0
- package/dist/{chunk-AFRFF7XL.js → chunk-YRNYOZF3.js} +4 -4
- package/dist/{chunk-AFRFF7XL.js.map → chunk-YRNYOZF3.js.map} +1 -1
- package/dist/forma-runtime-csp.js +52 -4
- package/dist/forma-runtime.js +98 -10
- package/dist/formajs-runtime-hardened.global.js +52 -4
- package/dist/formajs-runtime-hardened.global.js.map +1 -1
- package/dist/formajs-runtime.global.js +98 -10
- package/dist/formajs-runtime.global.js.map +1 -1
- package/dist/formajs.global.js +368 -147
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +262 -141
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +32 -27
- package/dist/index.d.ts +32 -27
- package/dist/index.js +204 -83
- package/dist/index.js.map +1 -1
- package/dist/{resource-DeEzxUz6.d.cts → resource-D6HfVgiA.d.cts} +7 -2
- package/dist/{resource-BHsgURy0.d.ts → resource-ljs2X5NM.d.ts} +7 -2
- package/dist/runtime-hardened.cjs +53 -3
- package/dist/runtime-hardened.cjs.map +1 -1
- package/dist/runtime-hardened.js +53 -3
- package/dist/runtime-hardened.js.map +1 -1
- package/dist/runtime.cjs +29 -29
- package/dist/runtime.js +3 -3
- package/dist/server.cjs +7 -7
- package/dist/server.d.cts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +2 -2
- package/dist/{signal-C9v4akyJ.d.cts → signal-CRBJYQ6B.d.cts} +0 -8
- package/dist/{signal-C9v4akyJ.d.ts → signal-CRBJYQ6B.d.ts} +0 -8
- package/dist/tc39-compat.cjs +3 -6
- package/dist/tc39-compat.cjs.map +1 -1
- package/dist/tc39-compat.d.cts +1 -1
- package/dist/tc39-compat.d.ts +1 -1
- package/dist/tc39-compat.js +1 -4
- package/dist/tc39-compat.js.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-3G7ET4O5.js.map +0 -1
- package/dist/chunk-6FW5E54W.cjs.map +0 -1
- package/dist/chunk-HLM5BZZQ.js +0 -35
- package/dist/chunk-HLM5BZZQ.js.map +0 -1
- package/dist/chunk-QLPCVK7C.cjs +0 -38
- package/dist/chunk-QLPCVK7C.cjs.map +0 -1
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { signal, computed, setActiveSub, trigger } from 'alien-signals';
|
|
2
|
+
|
|
3
|
+
// src/reactive/dev.ts
|
|
4
|
+
function isDev() {
|
|
5
|
+
if (typeof process !== "undefined") {
|
|
6
|
+
return process.env?.NODE_ENV !== "production";
|
|
7
|
+
}
|
|
8
|
+
return typeof __FORMA_DEV__ !== "undefined" ? !!__FORMA_DEV__ : false;
|
|
9
|
+
}
|
|
10
|
+
var __DEV__ = isDev();
|
|
11
|
+
var _errorHandlers = /* @__PURE__ */ new Set();
|
|
12
|
+
function onError(handler) {
|
|
13
|
+
_errorHandlers.add(handler);
|
|
14
|
+
return () => {
|
|
15
|
+
_errorHandlers.delete(handler);
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function reportError(error, source) {
|
|
19
|
+
for (const handler of _errorHandlers) {
|
|
20
|
+
try {
|
|
21
|
+
handler(error, source ? { source } : {});
|
|
22
|
+
} catch {
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
if (isDev()) {
|
|
26
|
+
console.error(`[forma] ${source ?? "Unknown"} error:`, error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
30
|
+
function value(v) {
|
|
31
|
+
return () => v;
|
|
32
|
+
}
|
|
33
|
+
function applySignalSet(s, v, equals) {
|
|
34
|
+
if (typeof v !== "function") {
|
|
35
|
+
if (!equals) {
|
|
36
|
+
s(v);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const prevSub2 = setActiveSub(void 0);
|
|
40
|
+
const prev2 = s();
|
|
41
|
+
setActiveSub(prevSub2);
|
|
42
|
+
if (equals(prev2, v)) return;
|
|
43
|
+
s(v);
|
|
44
|
+
if (Object.is(prev2, v)) forceNotify(s);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const prevSub = setActiveSub(void 0);
|
|
48
|
+
const prev = s();
|
|
49
|
+
setActiveSub(prevSub);
|
|
50
|
+
const next = v(prev);
|
|
51
|
+
if (equals && equals(prev, next)) return;
|
|
52
|
+
s(next);
|
|
53
|
+
if (equals && Object.is(prev, next)) forceNotify(s);
|
|
54
|
+
}
|
|
55
|
+
function forceNotify(s) {
|
|
56
|
+
trigger(() => {
|
|
57
|
+
s();
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
function createSignal(initialValue, options) {
|
|
61
|
+
const s = signal(initialValue);
|
|
62
|
+
const getter = s;
|
|
63
|
+
if (__DEV__ && options?.name) {
|
|
64
|
+
signalNames.set(getter, options.name);
|
|
65
|
+
}
|
|
66
|
+
const eq = options?.equals;
|
|
67
|
+
const setter = (v) => applySignalSet(s, v, eq);
|
|
68
|
+
return [getter, setter];
|
|
69
|
+
}
|
|
70
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
71
|
+
function isErrBox(v) {
|
|
72
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
73
|
+
}
|
|
74
|
+
function createComputed(fn) {
|
|
75
|
+
let errored = false;
|
|
76
|
+
let error;
|
|
77
|
+
let lastGood;
|
|
78
|
+
const raw = computed(() => {
|
|
79
|
+
try {
|
|
80
|
+
const v = fn(lastGood);
|
|
81
|
+
errored = false;
|
|
82
|
+
error = void 0;
|
|
83
|
+
lastGood = v;
|
|
84
|
+
return v;
|
|
85
|
+
} catch (e) {
|
|
86
|
+
errored = true;
|
|
87
|
+
error = e;
|
|
88
|
+
reportError(e, "computed");
|
|
89
|
+
return { [ERR]: e };
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
const reader = () => {
|
|
93
|
+
const v = raw();
|
|
94
|
+
if (errored || isErrBox(v)) throw error;
|
|
95
|
+
return v;
|
|
96
|
+
};
|
|
97
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
98
|
+
return reader;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export { __DEV__, createComputed, createSignal, onError, reportError, value };
|
|
102
|
+
//# sourceMappingURL=chunk-ETPJTPYD.js.map
|
|
103
|
+
//# sourceMappingURL=chunk-ETPJTPYD.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reactive/dev.ts","../src/reactive/signal.ts","../src/reactive/computed.ts"],"names":["prevSub","prev","createRawSignal","rawComputed"],"mappings":"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","file":"chunk-ETPJTPYD.js","sourcesContent":["/**\n * Forma Reactive - Dev Mode\n *\n * Development utilities stripped from production builds via __DEV__ flag.\n * Bundlers replace __DEV__ with false → dead-code elimination removes all dev paths.\n */\n\n// __DEV__ is replaced by the bundler (tsup define) for dead-code elimination in\n// dist. For unbundled/raw-source usage it derives from NODE_ENV, and — when the\n// environment is indeterminate — defaults to *false* (production-safe: no console\n// noise) unless a consumer opts in via a global __FORMA_DEV__.\ndeclare const process: { env?: Record<string, string | undefined> } | undefined;\ndeclare const __FORMA_DEV__: boolean | undefined;\n\n/**\n * Lazily evaluate whether we're in a dev environment. Read per-call (not frozen\n * at import) so runtime NODE_ENV changes are honored and so official prod dist\n * builds — where tsup hard-defines __DEV__ = false — stay quiet regardless.\n */\nfunction isDev(): boolean {\n if (typeof process !== 'undefined') {\n return process!.env?.NODE_ENV !== 'production';\n }\n return typeof __FORMA_DEV__ !== 'undefined' ? !!__FORMA_DEV__ : false;\n}\n\nexport const __DEV__: boolean = isDev();\n\n// ---------------------------------------------------------------------------\n// Global error handlers\n// ---------------------------------------------------------------------------\n\n/** Callback signature for a global {@link onError} handler. */\nexport type ErrorHandler = (error: unknown, info?: { source?: string }) => void;\n\nconst _errorHandlers = new Set<ErrorHandler>();\n\n/**\n * Install a global error handler for FormaJS reactive errors. Called when\n * effects, computeds, or event handlers throw. Multiple handlers may be\n * registered; returns an unsubscribe function that removes only this handler.\n *\n * ```ts\n * const off = onError((err, info) => {\n * Sentry.captureException(err, { tags: { source: info?.source } });\n * });\n * // later: off();\n * ```\n */\nexport function onError(handler: ErrorHandler): () => void {\n _errorHandlers.add(handler);\n return () => { _errorHandlers.delete(handler); };\n}\n\n/** @internal */\nexport function reportError(error: unknown, source?: string): void {\n for (const handler of _errorHandlers) {\n try { handler(error, source ? { source } : {}); } catch { /* prevent infinite loop */ }\n }\n if (isDev()) {\n console.error(`[forma] ${source ?? 'Unknown'} error:`, error);\n }\n}\n","/**\n * Forma Reactive - Signal\n *\n * Fine-grained reactive primitive backed by alien-signals.\n * API: createSignal returns [getter, setter] tuple following SolidJS conventions.\n *\n * TC39 Signals equivalent: Signal.State\n */\n\nimport { signal as createRawSignal, setActiveSub, trigger } from 'alien-signals';\nimport { __DEV__ } from './dev.js';\n\n// Debug names live in a side table rather than on the getter's `.name`, so\n// alien-signals' isSignal() (which keys off the bound function name) still\n// recognizes named signals.\nconst signalNames = new WeakMap<object, string>();\n\n/** Debug name of a signal getter, if one was provided (dev-only). */\nexport function getSignalName(fn: unknown): string | undefined {\n return typeof fn === 'function' ? signalNames.get(fn as object) : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** A function that reads the current value of a signal. */\nexport type SignalGetter<T> = () => T;\n/** A function that updates a signal — accepts a value or an updater function. */\nexport type SignalSetter<T> = (v: T | ((prev: T) => T)) => void;\n\nexport interface SignalOptions<T> {\n /** Debug name — attached to getter in dev mode for devtools inspection. */\n name?: string;\n /**\n * Custom equality check. When provided, the setter will read the current\n * value and only update the signal if `equals(prev, next)` returns `false`.\n *\n * Default: none (alien-signals uses strict inequality `!==` internally).\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n *\n * setPos({ x: 0, y: 0 }); // skipped — equals returns true\n * setPos({ x: 1, y: 0 }); // applied — equals returns false\n * ```\n */\n equals?: (prev: T, next: T) => boolean;\n}\n\n/**\n * Wrap a value so the setter treats it as a literal value, not a functional updater.\n *\n * When `T` is itself a function type, passing a function to the setter is\n * ambiguous -- it looks like a functional update (`prev => next`). Use\n * `value()` to disambiguate:\n *\n * ```ts\n * const [getFn, setFn] = createSignal<() => void>(() => console.log('a'));\n *\n * // BUG: interpreted as a functional update -- calls the arrow with prev\n * // setFn(() => console.log('b'));\n *\n * // Correct: wraps in a thunk so the setter stores it as-is\n * setFn(value(() => console.log('b')));\n * ```\n */\nexport function value<T>(v: T): () => T {\n return () => v;\n}\n\ntype RawSignal<T> = {\n (): T;\n (value: T): void;\n};\n\nfunction applySignalSet<T>(\n s: RawSignal<T>,\n v: T | ((prev: T) => T),\n equals?: (prev: T, next: T) => boolean,\n): void {\n // Fast path: plain value, no equals — no prev read needed. (A running effect\n // writing its own dependency is detected at the effect level via the Pending\n // flag, so signal.ts does not need to do anything special here.)\n if (typeof v !== 'function') {\n if (!equals) {\n s(v);\n return;\n }\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n if (equals(prev, v)) return; // suppressed by custom equality\n s(v);\n if (Object.is(prev, v)) forceNotify(s);\n return;\n }\n\n // Functional update: read prev without tracking.\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n const next = (v as (prev: T) => T)(prev);\n if (equals && equals(prev, next)) return; // suppressed by custom equality\n s(next);\n if (equals && Object.is(prev, next)) forceNotify(s);\n}\n\n/**\n * When a custom `equals` decided to APPLY (returned false) but the value is the\n * identical reference, alien-signals suppresses propagation (its guard is\n * `pendingValue !== value`). Force-notify subscribers so `equals: () => false`\n * (\"always notify\") works for mutate-in-place patterns.\n */\nfunction forceNotify<T>(s: RawSignal<T>): void {\n trigger(() => { s(); });\n}\n\n/**\n * Create a reactive signal.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * console.log(count()); // 0\n * setCount(1);\n * setCount(prev => prev + 1);\n * ```\n *\n * With custom equality:\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n * ```\n */\nexport function createSignal<T>(initialValue: T, options?: SignalOptions<T>): [get: SignalGetter<T>, set: SignalSetter<T>] {\n const s = createRawSignal<T>(initialValue) as RawSignal<T>;\n const getter = s as unknown as SignalGetter<T>;\n if (__DEV__ && options?.name) {\n signalNames.set(getter as object, options.name);\n }\n const eq = options?.equals;\n const setter: SignalSetter<T> = (v: T | ((prev: T) => T)) => applySignalSet(s, v, eq);\n\n return [getter, setter];\n}\n","/**\n * Forma Reactive - Computed\n *\n * Lazy, cached derived value that participates in the reactive graph.\n * Backed by alien-signals for automatic dependency tracking\n * and cache invalidation.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { computed as rawComputed } from 'alien-signals';\nimport { reportError } from './dev.js';\n\n/** Unique per-throw marker so alien-signals sees a value change and propagates. */\nconst ERR = Symbol('formaComputedError');\ninterface ErrBox { [ERR]: unknown; }\nfunction isErrBox(v: unknown): v is ErrBox {\n return typeof v === 'object' && v !== null && ERR in (v as object);\n}\n\n/**\n * Create a lazy, cached computed value.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), this is a lazy cached derivation — equivalent to\n * SolidJS's createMemo. Both createComputed and createMemo in FormaJS\n * are identical.\n *\n * The getter receives the previous value as an argument, enabling\n * efficient diffing patterns without a separate signal:\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createComputed(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n *\n * With previous value (for diffing):\n *\n * ```ts\n * const changes = createComputed((prev) => {\n * const next = items();\n * if (prev) console.log(`changed from ${prev.length} to ${next.length} items`);\n * return next;\n * });\n * ```\n */\nexport function createComputed<T>(fn: (previousValue?: T) => T): () => T {\n // alien-signals' updateComputed leaves a throwing computed flagged clean with a\n // stale value, so a later read silently returns the stale value and never\n // rethrows. We cache the error in closure and rethrow on read until a\n // dependency changes (TC39/Solid). On throw we return a FRESH sentinel object so\n // alien-signals sees a value change and propagates the error to subscribers; the\n // user getter only ever sees the last GOOD value as `previousValue`.\n let errored = false;\n let error: unknown;\n let lastGood: T | undefined;\n\n const raw = rawComputed<T | ErrBox>(() => {\n try {\n const v = fn(lastGood);\n errored = false;\n error = undefined;\n lastGood = v;\n return v;\n } catch (e) {\n errored = true;\n error = e;\n reportError(e, 'computed');\n return { [ERR]: e };\n }\n });\n\n const reader = () => {\n const v = raw();\n if (errored || isErrBox(v)) throw error;\n return v as T;\n };\n // Preserve alien-signals' isComputed() recognition, which keys off the bound\n // function name (`bound computedOper`). Our reader wraps the raw computed, so\n // copy the raw name onto it.\n Object.defineProperty(reader, 'name', { value: raw.name, configurable: true });\n return reader;\n}\n"]}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var alienSignals = require('alien-signals');
|
|
4
|
+
|
|
5
|
+
// src/reactive/dev.ts
|
|
6
|
+
function isDev() {
|
|
7
|
+
if (typeof process !== "undefined") {
|
|
8
|
+
return process.env?.NODE_ENV !== "production";
|
|
9
|
+
}
|
|
10
|
+
return typeof __FORMA_DEV__ !== "undefined" ? !!__FORMA_DEV__ : false;
|
|
11
|
+
}
|
|
12
|
+
var __DEV__ = isDev();
|
|
13
|
+
var _errorHandlers = /* @__PURE__ */ new Set();
|
|
14
|
+
function onError(handler) {
|
|
15
|
+
_errorHandlers.add(handler);
|
|
16
|
+
return () => {
|
|
17
|
+
_errorHandlers.delete(handler);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function reportError(error, source) {
|
|
21
|
+
for (const handler of _errorHandlers) {
|
|
22
|
+
try {
|
|
23
|
+
handler(error, source ? { source } : {});
|
|
24
|
+
} catch {
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (isDev()) {
|
|
28
|
+
console.error(`[forma] ${source ?? "Unknown"} error:`, error);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
function value(v) {
|
|
33
|
+
return () => v;
|
|
34
|
+
}
|
|
35
|
+
function applySignalSet(s, v, equals) {
|
|
36
|
+
if (typeof v !== "function") {
|
|
37
|
+
if (!equals) {
|
|
38
|
+
s(v);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const prevSub2 = alienSignals.setActiveSub(void 0);
|
|
42
|
+
const prev2 = s();
|
|
43
|
+
alienSignals.setActiveSub(prevSub2);
|
|
44
|
+
if (equals(prev2, v)) return;
|
|
45
|
+
s(v);
|
|
46
|
+
if (Object.is(prev2, v)) forceNotify(s);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const prevSub = alienSignals.setActiveSub(void 0);
|
|
50
|
+
const prev = s();
|
|
51
|
+
alienSignals.setActiveSub(prevSub);
|
|
52
|
+
const next = v(prev);
|
|
53
|
+
if (equals && equals(prev, next)) return;
|
|
54
|
+
s(next);
|
|
55
|
+
if (equals && Object.is(prev, next)) forceNotify(s);
|
|
56
|
+
}
|
|
57
|
+
function forceNotify(s) {
|
|
58
|
+
alienSignals.trigger(() => {
|
|
59
|
+
s();
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function createSignal(initialValue, options) {
|
|
63
|
+
const s = alienSignals.signal(initialValue);
|
|
64
|
+
const getter = s;
|
|
65
|
+
if (__DEV__ && options?.name) {
|
|
66
|
+
signalNames.set(getter, options.name);
|
|
67
|
+
}
|
|
68
|
+
const eq = options?.equals;
|
|
69
|
+
const setter = (v) => applySignalSet(s, v, eq);
|
|
70
|
+
return [getter, setter];
|
|
71
|
+
}
|
|
72
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
73
|
+
function isErrBox(v) {
|
|
74
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
75
|
+
}
|
|
76
|
+
function createComputed(fn) {
|
|
77
|
+
let errored = false;
|
|
78
|
+
let error;
|
|
79
|
+
let lastGood;
|
|
80
|
+
const raw = alienSignals.computed(() => {
|
|
81
|
+
try {
|
|
82
|
+
const v = fn(lastGood);
|
|
83
|
+
errored = false;
|
|
84
|
+
error = void 0;
|
|
85
|
+
lastGood = v;
|
|
86
|
+
return v;
|
|
87
|
+
} catch (e) {
|
|
88
|
+
errored = true;
|
|
89
|
+
error = e;
|
|
90
|
+
reportError(e, "computed");
|
|
91
|
+
return { [ERR]: e };
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
const reader = () => {
|
|
95
|
+
const v = raw();
|
|
96
|
+
if (errored || isErrBox(v)) throw error;
|
|
97
|
+
return v;
|
|
98
|
+
};
|
|
99
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
100
|
+
return reader;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
exports.__DEV__ = __DEV__;
|
|
104
|
+
exports.createComputed = createComputed;
|
|
105
|
+
exports.createSignal = createSignal;
|
|
106
|
+
exports.onError = onError;
|
|
107
|
+
exports.reportError = reportError;
|
|
108
|
+
exports.value = value;
|
|
109
|
+
//# sourceMappingURL=chunk-H7MDUHS5.cjs.map
|
|
110
|
+
//# sourceMappingURL=chunk-H7MDUHS5.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/reactive/dev.ts","../src/reactive/signal.ts","../src/reactive/computed.ts"],"names":["prevSub","setActiveSub","prev","trigger","createRawSignal","rawComputed"],"mappings":"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","file":"chunk-H7MDUHS5.cjs","sourcesContent":["/**\n * Forma Reactive - Dev Mode\n *\n * Development utilities stripped from production builds via __DEV__ flag.\n * Bundlers replace __DEV__ with false → dead-code elimination removes all dev paths.\n */\n\n// __DEV__ is replaced by the bundler (tsup define) for dead-code elimination in\n// dist. For unbundled/raw-source usage it derives from NODE_ENV, and — when the\n// environment is indeterminate — defaults to *false* (production-safe: no console\n// noise) unless a consumer opts in via a global __FORMA_DEV__.\ndeclare const process: { env?: Record<string, string | undefined> } | undefined;\ndeclare const __FORMA_DEV__: boolean | undefined;\n\n/**\n * Lazily evaluate whether we're in a dev environment. Read per-call (not frozen\n * at import) so runtime NODE_ENV changes are honored and so official prod dist\n * builds — where tsup hard-defines __DEV__ = false — stay quiet regardless.\n */\nfunction isDev(): boolean {\n if (typeof process !== 'undefined') {\n return process!.env?.NODE_ENV !== 'production';\n }\n return typeof __FORMA_DEV__ !== 'undefined' ? !!__FORMA_DEV__ : false;\n}\n\nexport const __DEV__: boolean = isDev();\n\n// ---------------------------------------------------------------------------\n// Global error handlers\n// ---------------------------------------------------------------------------\n\n/** Callback signature for a global {@link onError} handler. */\nexport type ErrorHandler = (error: unknown, info?: { source?: string }) => void;\n\nconst _errorHandlers = new Set<ErrorHandler>();\n\n/**\n * Install a global error handler for FormaJS reactive errors. Called when\n * effects, computeds, or event handlers throw. Multiple handlers may be\n * registered; returns an unsubscribe function that removes only this handler.\n *\n * ```ts\n * const off = onError((err, info) => {\n * Sentry.captureException(err, { tags: { source: info?.source } });\n * });\n * // later: off();\n * ```\n */\nexport function onError(handler: ErrorHandler): () => void {\n _errorHandlers.add(handler);\n return () => { _errorHandlers.delete(handler); };\n}\n\n/** @internal */\nexport function reportError(error: unknown, source?: string): void {\n for (const handler of _errorHandlers) {\n try { handler(error, source ? { source } : {}); } catch { /* prevent infinite loop */ }\n }\n if (isDev()) {\n console.error(`[forma] ${source ?? 'Unknown'} error:`, error);\n }\n}\n","/**\n * Forma Reactive - Signal\n *\n * Fine-grained reactive primitive backed by alien-signals.\n * API: createSignal returns [getter, setter] tuple following SolidJS conventions.\n *\n * TC39 Signals equivalent: Signal.State\n */\n\nimport { signal as createRawSignal, setActiveSub, trigger } from 'alien-signals';\nimport { __DEV__ } from './dev.js';\n\n// Debug names live in a side table rather than on the getter's `.name`, so\n// alien-signals' isSignal() (which keys off the bound function name) still\n// recognizes named signals.\nconst signalNames = new WeakMap<object, string>();\n\n/** Debug name of a signal getter, if one was provided (dev-only). */\nexport function getSignalName(fn: unknown): string | undefined {\n return typeof fn === 'function' ? signalNames.get(fn as object) : undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/** A function that reads the current value of a signal. */\nexport type SignalGetter<T> = () => T;\n/** A function that updates a signal — accepts a value or an updater function. */\nexport type SignalSetter<T> = (v: T | ((prev: T) => T)) => void;\n\nexport interface SignalOptions<T> {\n /** Debug name — attached to getter in dev mode for devtools inspection. */\n name?: string;\n /**\n * Custom equality check. When provided, the setter will read the current\n * value and only update the signal if `equals(prev, next)` returns `false`.\n *\n * Default: none (alien-signals uses strict inequality `!==` internally).\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n *\n * setPos({ x: 0, y: 0 }); // skipped — equals returns true\n * setPos({ x: 1, y: 0 }); // applied — equals returns false\n * ```\n */\n equals?: (prev: T, next: T) => boolean;\n}\n\n/**\n * Wrap a value so the setter treats it as a literal value, not a functional updater.\n *\n * When `T` is itself a function type, passing a function to the setter is\n * ambiguous -- it looks like a functional update (`prev => next`). Use\n * `value()` to disambiguate:\n *\n * ```ts\n * const [getFn, setFn] = createSignal<() => void>(() => console.log('a'));\n *\n * // BUG: interpreted as a functional update -- calls the arrow with prev\n * // setFn(() => console.log('b'));\n *\n * // Correct: wraps in a thunk so the setter stores it as-is\n * setFn(value(() => console.log('b')));\n * ```\n */\nexport function value<T>(v: T): () => T {\n return () => v;\n}\n\ntype RawSignal<T> = {\n (): T;\n (value: T): void;\n};\n\nfunction applySignalSet<T>(\n s: RawSignal<T>,\n v: T | ((prev: T) => T),\n equals?: (prev: T, next: T) => boolean,\n): void {\n // Fast path: plain value, no equals — no prev read needed. (A running effect\n // writing its own dependency is detected at the effect level via the Pending\n // flag, so signal.ts does not need to do anything special here.)\n if (typeof v !== 'function') {\n if (!equals) {\n s(v);\n return;\n }\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n if (equals(prev, v)) return; // suppressed by custom equality\n s(v);\n if (Object.is(prev, v)) forceNotify(s);\n return;\n }\n\n // Functional update: read prev without tracking.\n const prevSub = setActiveSub(undefined);\n const prev = s();\n setActiveSub(prevSub);\n const next = (v as (prev: T) => T)(prev);\n if (equals && equals(prev, next)) return; // suppressed by custom equality\n s(next);\n if (equals && Object.is(prev, next)) forceNotify(s);\n}\n\n/**\n * When a custom `equals` decided to APPLY (returned false) but the value is the\n * identical reference, alien-signals suppresses propagation (its guard is\n * `pendingValue !== value`). Force-notify subscribers so `equals: () => false`\n * (\"always notify\") works for mutate-in-place patterns.\n */\nfunction forceNotify<T>(s: RawSignal<T>): void {\n trigger(() => { s(); });\n}\n\n/**\n * Create a reactive signal.\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * console.log(count()); // 0\n * setCount(1);\n * setCount(prev => prev + 1);\n * ```\n *\n * With custom equality:\n *\n * ```ts\n * const [pos, setPos] = createSignal(\n * { x: 0, y: 0 },\n * { equals: (a, b) => a.x === b.x && a.y === b.y },\n * );\n * ```\n */\nexport function createSignal<T>(initialValue: T, options?: SignalOptions<T>): [get: SignalGetter<T>, set: SignalSetter<T>] {\n const s = createRawSignal<T>(initialValue) as RawSignal<T>;\n const getter = s as unknown as SignalGetter<T>;\n if (__DEV__ && options?.name) {\n signalNames.set(getter as object, options.name);\n }\n const eq = options?.equals;\n const setter: SignalSetter<T> = (v: T | ((prev: T) => T)) => applySignalSet(s, v, eq);\n\n return [getter, setter];\n}\n","/**\n * Forma Reactive - Computed\n *\n * Lazy, cached derived value that participates in the reactive graph.\n * Backed by alien-signals for automatic dependency tracking\n * and cache invalidation.\n *\n * TC39 Signals equivalent: Signal.Computed\n */\n\nimport { computed as rawComputed } from 'alien-signals';\nimport { reportError } from './dev.js';\n\n/** Unique per-throw marker so alien-signals sees a value change and propagates. */\nconst ERR = Symbol('formaComputedError');\ninterface ErrBox { [ERR]: unknown; }\nfunction isErrBox(v: unknown): v is ErrBox {\n return typeof v === 'object' && v !== null && ERR in (v as object);\n}\n\n/**\n * Create a lazy, cached computed value.\n *\n * Note: Unlike SolidJS's createComputed (which is an eager synchronous\n * side effect), this is a lazy cached derivation — equivalent to\n * SolidJS's createMemo. Both createComputed and createMemo in FormaJS\n * are identical.\n *\n * The getter receives the previous value as an argument, enabling\n * efficient diffing patterns without a separate signal:\n *\n * ```ts\n * const [count, setCount] = createSignal(0);\n * const doubled = createComputed(() => count() * 2);\n * console.log(doubled()); // 0\n * setCount(5);\n * console.log(doubled()); // 10\n * ```\n *\n * With previous value (for diffing):\n *\n * ```ts\n * const changes = createComputed((prev) => {\n * const next = items();\n * if (prev) console.log(`changed from ${prev.length} to ${next.length} items`);\n * return next;\n * });\n * ```\n */\nexport function createComputed<T>(fn: (previousValue?: T) => T): () => T {\n // alien-signals' updateComputed leaves a throwing computed flagged clean with a\n // stale value, so a later read silently returns the stale value and never\n // rethrows. We cache the error in closure and rethrow on read until a\n // dependency changes (TC39/Solid). On throw we return a FRESH sentinel object so\n // alien-signals sees a value change and propagates the error to subscribers; the\n // user getter only ever sees the last GOOD value as `previousValue`.\n let errored = false;\n let error: unknown;\n let lastGood: T | undefined;\n\n const raw = rawComputed<T | ErrBox>(() => {\n try {\n const v = fn(lastGood);\n errored = false;\n error = undefined;\n lastGood = v;\n return v;\n } catch (e) {\n errored = true;\n error = e;\n reportError(e, 'computed');\n return { [ERR]: e };\n }\n });\n\n const reader = () => {\n const v = raw();\n if (errored || isErrBox(v)) throw error;\n return v as T;\n };\n // Preserve alien-signals' isComputed() recognition, which keys off the bound\n // function name (`bound computedOper`). Our reader wraps the raw computed, so\n // copy the raw name onto it.\n Object.defineProperty(reader, 'name', { value: raw.name, configurable: true });\n return reader;\n}\n"]}
|
|
@@ -1,33 +1,77 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkH7MDUHS5_cjs = require('./chunk-H7MDUHS5.cjs');
|
|
4
4
|
var alienSignals = require('alien-signals');
|
|
5
5
|
|
|
6
6
|
var currentRoot = null;
|
|
7
7
|
var rootStack = [];
|
|
8
|
+
var currentOwner = null;
|
|
9
|
+
function getOwner() {
|
|
10
|
+
return currentOwner;
|
|
11
|
+
}
|
|
12
|
+
function runWithOwner(owner, fn) {
|
|
13
|
+
const prev = currentOwner;
|
|
14
|
+
currentOwner = owner;
|
|
15
|
+
try {
|
|
16
|
+
return fn();
|
|
17
|
+
} finally {
|
|
18
|
+
currentOwner = prev;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function registerOwnerDisposer(owner, dispose) {
|
|
22
|
+
owner.disposers.push(dispose);
|
|
23
|
+
}
|
|
24
|
+
function createChildOwner() {
|
|
25
|
+
return { disposers: [] };
|
|
26
|
+
}
|
|
27
|
+
function disposeOwner(owner) {
|
|
28
|
+
const ds = owner.disposers;
|
|
29
|
+
for (const d of ds) {
|
|
30
|
+
try {
|
|
31
|
+
d();
|
|
32
|
+
} catch {
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
ds.length = 0;
|
|
36
|
+
}
|
|
8
37
|
function createRootImpl(fn, owned) {
|
|
9
38
|
const scope = { disposers: [], scopeDispose: null };
|
|
10
39
|
const parentRoot = owned ? currentRoot : null;
|
|
11
40
|
rootStack.push(currentRoot);
|
|
12
41
|
currentRoot = scope;
|
|
42
|
+
const prevOwner = currentOwner;
|
|
43
|
+
currentOwner = scope;
|
|
13
44
|
let disposed = false;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
45
|
+
let setupComplete = false;
|
|
46
|
+
let disposeRequestedDuringSetup = false;
|
|
47
|
+
const runDisposers = () => {
|
|
48
|
+
for (const d of scope.disposers) {
|
|
18
49
|
try {
|
|
19
|
-
|
|
50
|
+
d();
|
|
20
51
|
} catch {
|
|
21
52
|
}
|
|
22
|
-
scope.scopeDispose = null;
|
|
23
53
|
}
|
|
24
|
-
|
|
54
|
+
scope.disposers.length = 0;
|
|
55
|
+
};
|
|
56
|
+
const runTeardown = () => {
|
|
57
|
+
if (scope.scopeDispose) {
|
|
25
58
|
try {
|
|
26
|
-
|
|
59
|
+
scope.scopeDispose();
|
|
27
60
|
} catch {
|
|
28
61
|
}
|
|
62
|
+
scope.scopeDispose = null;
|
|
29
63
|
}
|
|
30
|
-
|
|
64
|
+
runDisposers();
|
|
65
|
+
};
|
|
66
|
+
const dispose = () => {
|
|
67
|
+
if (disposed) return;
|
|
68
|
+
disposed = true;
|
|
69
|
+
if (!setupComplete) {
|
|
70
|
+
disposeRequestedDuringSetup = true;
|
|
71
|
+
runDisposers();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
runTeardown();
|
|
31
75
|
};
|
|
32
76
|
if (parentRoot) {
|
|
33
77
|
parentRoot.disposers.push(dispose);
|
|
@@ -48,8 +92,13 @@ function createRootImpl(fn, owned) {
|
|
|
48
92
|
alienSignals.setActiveSub(prevSub);
|
|
49
93
|
}
|
|
50
94
|
}
|
|
95
|
+
setupComplete = true;
|
|
96
|
+
if (disposeRequestedDuringSetup) {
|
|
97
|
+
runTeardown();
|
|
98
|
+
}
|
|
51
99
|
} finally {
|
|
52
100
|
currentRoot = rootStack.pop() ?? null;
|
|
101
|
+
currentOwner = prevOwner;
|
|
53
102
|
}
|
|
54
103
|
return result;
|
|
55
104
|
}
|
|
@@ -60,12 +109,12 @@ function createUnownedRoot(fn) {
|
|
|
60
109
|
return createRootImpl(fn, false);
|
|
61
110
|
}
|
|
62
111
|
function registerDisposer(dispose) {
|
|
63
|
-
if (
|
|
64
|
-
|
|
112
|
+
if (currentOwner) {
|
|
113
|
+
currentOwner.disposers.push(dispose);
|
|
65
114
|
}
|
|
66
115
|
}
|
|
67
116
|
function hasActiveRoot() {
|
|
68
|
-
return
|
|
117
|
+
return currentOwner !== null;
|
|
69
118
|
}
|
|
70
119
|
|
|
71
120
|
// src/reactive/cleanup.ts
|
|
@@ -78,29 +127,12 @@ function setCleanupCollector(collector) {
|
|
|
78
127
|
currentCleanupCollector = collector;
|
|
79
128
|
return prev;
|
|
80
129
|
}
|
|
81
|
-
|
|
82
|
-
// src/reactive/dev.ts
|
|
83
|
-
var __DEV__ = typeof process !== "undefined" ? process.env?.NODE_ENV !== "production" : true;
|
|
84
|
-
var _errorHandler = null;
|
|
85
|
-
function onError(handler) {
|
|
86
|
-
_errorHandler = handler;
|
|
87
|
-
}
|
|
88
|
-
function reportError(error, source) {
|
|
89
|
-
if (_errorHandler) {
|
|
90
|
-
try {
|
|
91
|
-
_errorHandler(error, source ? { source } : {});
|
|
92
|
-
} catch {
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
if (__DEV__) {
|
|
96
|
-
console.error(`[forma] ${source ?? "Unknown"} error:`, error);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
130
|
var POOL_SIZE = 32;
|
|
100
131
|
var MAX_REENTRANT_RUNS = 100;
|
|
101
132
|
var pool = [];
|
|
102
133
|
for (let i = 0; i < POOL_SIZE; i++) pool.push([]);
|
|
103
134
|
var poolIdx = POOL_SIZE;
|
|
135
|
+
var PENDING = 32;
|
|
104
136
|
function acquireArray() {
|
|
105
137
|
if (poolIdx > 0) {
|
|
106
138
|
const arr = pool[--poolIdx];
|
|
@@ -120,7 +152,7 @@ function runCleanup(fn) {
|
|
|
120
152
|
try {
|
|
121
153
|
fn();
|
|
122
154
|
} catch (e) {
|
|
123
|
-
reportError(e, "effect cleanup");
|
|
155
|
+
chunkH7MDUHS5_cjs.reportError(e, "effect cleanup");
|
|
124
156
|
}
|
|
125
157
|
}
|
|
126
158
|
function runCleanups(bag) {
|
|
@@ -129,7 +161,7 @@ function runCleanups(bag) {
|
|
|
129
161
|
try {
|
|
130
162
|
bag[i]();
|
|
131
163
|
} catch (e) {
|
|
132
|
-
reportError(e, "effect cleanup");
|
|
164
|
+
chunkH7MDUHS5_cjs.reportError(e, "effect cleanup");
|
|
133
165
|
}
|
|
134
166
|
}
|
|
135
167
|
}
|
|
@@ -141,7 +173,8 @@ function internalEffect(fn) {
|
|
|
141
173
|
return dispose;
|
|
142
174
|
}
|
|
143
175
|
function createEffect(fn) {
|
|
144
|
-
const
|
|
176
|
+
const ownerAtCreate = getOwner();
|
|
177
|
+
const childOwner = createChildOwner();
|
|
145
178
|
let cleanup;
|
|
146
179
|
let cleanupBag;
|
|
147
180
|
let nextCleanup;
|
|
@@ -160,11 +193,8 @@ function createEffect(fn) {
|
|
|
160
193
|
}
|
|
161
194
|
nextCleanup = cb;
|
|
162
195
|
};
|
|
163
|
-
let skipCleanupInfra = false;
|
|
164
|
-
let firstRun = true;
|
|
165
|
-
let running = false;
|
|
166
|
-
let rerunRequested = false;
|
|
167
196
|
const runOnce = () => {
|
|
197
|
+
disposeOwner(childOwner);
|
|
168
198
|
if (cleanup !== void 0) {
|
|
169
199
|
runCleanup(cleanup);
|
|
170
200
|
cleanup = void 0;
|
|
@@ -174,68 +204,46 @@ function createEffect(fn) {
|
|
|
174
204
|
releaseArray(cleanupBag);
|
|
175
205
|
cleanupBag = void 0;
|
|
176
206
|
}
|
|
177
|
-
if (skipCleanupInfra) {
|
|
178
|
-
try {
|
|
179
|
-
fn();
|
|
180
|
-
} catch (e) {
|
|
181
|
-
reportError(e, "effect");
|
|
182
|
-
}
|
|
183
|
-
return;
|
|
184
|
-
}
|
|
185
207
|
nextCleanup = void 0;
|
|
186
208
|
nextCleanupBag = void 0;
|
|
187
209
|
const prevCollector = setCleanupCollector(addCleanup);
|
|
188
210
|
try {
|
|
189
|
-
const result = fn
|
|
211
|
+
const result = runWithOwner(childOwner, fn);
|
|
190
212
|
if (typeof result === "function") {
|
|
191
213
|
addCleanup(result);
|
|
192
214
|
}
|
|
193
|
-
if (nextCleanup === void 0 && nextCleanupBag === void 0) {
|
|
194
|
-
if (firstRun) skipCleanupInfra = true;
|
|
195
|
-
return;
|
|
196
|
-
}
|
|
197
215
|
if (nextCleanupBag !== void 0) {
|
|
198
216
|
cleanupBag = nextCleanupBag;
|
|
199
|
-
} else {
|
|
217
|
+
} else if (nextCleanup !== void 0) {
|
|
200
218
|
cleanup = nextCleanup;
|
|
201
219
|
}
|
|
202
220
|
} catch (e) {
|
|
203
|
-
reportError(e, "effect");
|
|
221
|
+
chunkH7MDUHS5_cjs.reportError(e, "effect");
|
|
204
222
|
if (nextCleanupBag !== void 0) {
|
|
205
223
|
cleanupBag = nextCleanupBag;
|
|
206
|
-
} else {
|
|
224
|
+
} else if (nextCleanup !== void 0) {
|
|
207
225
|
cleanup = nextCleanup;
|
|
208
226
|
}
|
|
209
227
|
} finally {
|
|
210
228
|
setCleanupCollector(prevCollector);
|
|
211
|
-
firstRun = false;
|
|
212
229
|
}
|
|
213
230
|
};
|
|
214
231
|
const safeFn = () => {
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
"effect"
|
|
231
|
-
);
|
|
232
|
-
rerunRequested = false;
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
} while (rerunRequested);
|
|
236
|
-
} finally {
|
|
237
|
-
running = false;
|
|
238
|
-
}
|
|
232
|
+
const node = alienSignals.getActiveSub();
|
|
233
|
+
let reentrantRuns = 0;
|
|
234
|
+
do {
|
|
235
|
+
if (node) node.flags &= ~PENDING;
|
|
236
|
+
runOnce();
|
|
237
|
+
if (node === void 0 || (node.flags & PENDING) === 0) break;
|
|
238
|
+
if (++reentrantRuns >= MAX_REENTRANT_RUNS) {
|
|
239
|
+
node.flags &= ~PENDING;
|
|
240
|
+
chunkH7MDUHS5_cjs.reportError(
|
|
241
|
+
new Error(`createEffect exceeded ${MAX_REENTRANT_RUNS} self-triggered re-runs (cycle?)`),
|
|
242
|
+
"effect"
|
|
243
|
+
);
|
|
244
|
+
break;
|
|
245
|
+
}
|
|
246
|
+
} while (true);
|
|
239
247
|
};
|
|
240
248
|
const dispose = alienSignals.effect(safeFn);
|
|
241
249
|
let disposed = false;
|
|
@@ -243,6 +251,7 @@ function createEffect(fn) {
|
|
|
243
251
|
if (disposed) return;
|
|
244
252
|
disposed = true;
|
|
245
253
|
dispose();
|
|
254
|
+
disposeOwner(childOwner);
|
|
246
255
|
if (cleanup !== void 0) {
|
|
247
256
|
runCleanup(cleanup);
|
|
248
257
|
cleanup = void 0;
|
|
@@ -253,14 +262,14 @@ function createEffect(fn) {
|
|
|
253
262
|
cleanupBag = void 0;
|
|
254
263
|
}
|
|
255
264
|
};
|
|
256
|
-
if (
|
|
257
|
-
|
|
265
|
+
if (ownerAtCreate) {
|
|
266
|
+
registerOwnerDisposer(ownerAtCreate, wrappedDispose);
|
|
258
267
|
}
|
|
259
268
|
return wrappedDispose;
|
|
260
269
|
}
|
|
261
270
|
|
|
262
271
|
// src/reactive/memo.ts
|
|
263
|
-
var createMemo =
|
|
272
|
+
var createMemo = chunkH7MDUHS5_cjs.createComputed;
|
|
264
273
|
function batch(fn) {
|
|
265
274
|
alienSignals.startBatch();
|
|
266
275
|
try {
|
|
@@ -302,7 +311,7 @@ function createRef(initialValue) {
|
|
|
302
311
|
|
|
303
312
|
// src/reactive/reducer.ts
|
|
304
313
|
function createReducer(reducer, initialState) {
|
|
305
|
-
const [state, setState] =
|
|
314
|
+
const [state, setState] = chunkH7MDUHS5_cjs.createSignal(initialState);
|
|
306
315
|
const dispatch = (action) => {
|
|
307
316
|
setState((prev) => reducer(prev, action));
|
|
308
317
|
};
|
|
@@ -325,9 +334,9 @@ function getSuspenseContext() {
|
|
|
325
334
|
|
|
326
335
|
// src/reactive/resource.ts
|
|
327
336
|
function createResource(source, fetcher, options) {
|
|
328
|
-
const [data, setData] =
|
|
329
|
-
const [loading, setLoading] =
|
|
330
|
-
const [error, setError] =
|
|
337
|
+
const [data, setData] = chunkH7MDUHS5_cjs.createSignal(options?.initialValue);
|
|
338
|
+
const [loading, setLoading] = chunkH7MDUHS5_cjs.createSignal(false);
|
|
339
|
+
const [error, setError] = chunkH7MDUHS5_cjs.createSignal(void 0);
|
|
331
340
|
const suspenseCtx = getSuspenseContext();
|
|
332
341
|
let abortController = null;
|
|
333
342
|
let fetchVersion = 0;
|
|
@@ -345,25 +354,34 @@ function createResource(source, fetcher, options) {
|
|
|
345
354
|
suspenseCtx.increment();
|
|
346
355
|
suspensePending = true;
|
|
347
356
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
357
|
+
batch(() => {
|
|
358
|
+
setLoading(true);
|
|
359
|
+
setError(void 0);
|
|
360
|
+
});
|
|
361
|
+
let promise;
|
|
362
|
+
try {
|
|
363
|
+
promise = Promise.resolve(fetcher(sourceValue, { signal: controller.signal }));
|
|
364
|
+
} catch (err) {
|
|
365
|
+
promise = Promise.reject(err);
|
|
366
|
+
}
|
|
367
|
+
promise.then((result) => {
|
|
351
368
|
if (isLatest() && !controller.signal.aborted) {
|
|
352
|
-
|
|
369
|
+
batch(() => {
|
|
370
|
+
setData(() => result);
|
|
371
|
+
setLoading(false);
|
|
372
|
+
});
|
|
353
373
|
}
|
|
354
374
|
}).catch((err) => {
|
|
355
|
-
if (isLatest() && !controller.signal.aborted) {
|
|
356
|
-
|
|
375
|
+
if (isLatest() && !controller.signal.aborted && err?.name !== "AbortError") {
|
|
376
|
+
batch(() => {
|
|
357
377
|
setError(err);
|
|
358
|
-
|
|
378
|
+
setLoading(false);
|
|
379
|
+
});
|
|
359
380
|
}
|
|
360
381
|
}).finally(() => {
|
|
361
382
|
if (suspensePending) suspenseCtx?.decrement();
|
|
362
|
-
if (isLatest()) {
|
|
363
|
-
|
|
364
|
-
if (abortController === controller) {
|
|
365
|
-
abortController = null;
|
|
366
|
-
}
|
|
383
|
+
if (isLatest() && abortController === controller) {
|
|
384
|
+
abortController = null;
|
|
367
385
|
}
|
|
368
386
|
});
|
|
369
387
|
};
|
|
@@ -371,6 +389,15 @@ function createResource(source, fetcher, options) {
|
|
|
371
389
|
source();
|
|
372
390
|
doFetch();
|
|
373
391
|
});
|
|
392
|
+
if (hasActiveRoot()) {
|
|
393
|
+
registerDisposer(() => {
|
|
394
|
+
fetchVersion++;
|
|
395
|
+
if (abortController) {
|
|
396
|
+
abortController.abort();
|
|
397
|
+
abortController = null;
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
}
|
|
374
401
|
const resource = (() => data());
|
|
375
402
|
resource.loading = loading;
|
|
376
403
|
resource.error = error;
|
|
@@ -403,7 +430,6 @@ Object.defineProperty(exports, "trigger", {
|
|
|
403
430
|
enumerable: true,
|
|
404
431
|
get: function () { return alienSignals.trigger; }
|
|
405
432
|
});
|
|
406
|
-
exports.__DEV__ = __DEV__;
|
|
407
433
|
exports.batch = batch;
|
|
408
434
|
exports.createEffect = createEffect;
|
|
409
435
|
exports.createMemo = createMemo;
|
|
@@ -415,11 +441,9 @@ exports.createUnownedRoot = createUnownedRoot;
|
|
|
415
441
|
exports.internalEffect = internalEffect;
|
|
416
442
|
exports.on = on;
|
|
417
443
|
exports.onCleanup = onCleanup;
|
|
418
|
-
exports.onError = onError;
|
|
419
444
|
exports.popSuspenseContext = popSuspenseContext;
|
|
420
445
|
exports.pushSuspenseContext = pushSuspenseContext;
|
|
421
446
|
exports.registerDisposer = registerDisposer;
|
|
422
|
-
exports.reportError = reportError;
|
|
423
447
|
exports.untrack = untrack;
|
|
424
|
-
//# sourceMappingURL=chunk-
|
|
425
|
-
//# sourceMappingURL=chunk-
|
|
448
|
+
//# sourceMappingURL=chunk-KUXNZ5MG.cjs.map
|
|
449
|
+
//# sourceMappingURL=chunk-KUXNZ5MG.cjs.map
|