@getforma/core 1.0.10 → 1.1.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-6FW5E54W.cjs → chunk-2Y5US35K.cjs} +127 -103
- package/dist/chunk-2Y5US35K.cjs.map +1 -0
- package/dist/{chunk-3G7ET4O5.js → chunk-INNOI6TG.js} +121 -94
- package/dist/chunk-INNOI6TG.js.map +1 -0
- package/dist/{chunk-T33QUD2Y.cjs → chunk-JRQNDXX7.cjs} +43 -43
- package/dist/{chunk-T33QUD2Y.cjs.map → chunk-JRQNDXX7.cjs.map} +1 -1
- package/dist/{chunk-AFRFF7XL.js → chunk-MIOMT2CB.js} +4 -4
- package/dist/{chunk-AFRFF7XL.js.map → chunk-MIOMT2CB.js.map} +1 -1
- package/dist/chunk-W7OUWVRA.cjs +106 -0
- package/dist/chunk-W7OUWVRA.cjs.map +1 -0
- package/dist/chunk-XLVBYXOU.js +100 -0
- package/dist/chunk-XLVBYXOU.js.map +1 -0
- 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 +192 -95
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +66 -66
- package/dist/index.d.cts +11 -25
- package/dist/index.d.ts +11 -25
- package/dist/index.js +6 -6
- 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,106 @@
|
|
|
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 applySignalSet(s, v, equals) {
|
|
33
|
+
if (typeof v !== "function") {
|
|
34
|
+
if (!equals) {
|
|
35
|
+
s(v);
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const prevSub2 = alienSignals.setActiveSub(void 0);
|
|
39
|
+
const prev2 = s();
|
|
40
|
+
alienSignals.setActiveSub(prevSub2);
|
|
41
|
+
if (equals(prev2, v)) return;
|
|
42
|
+
s(v);
|
|
43
|
+
if (Object.is(prev2, v)) forceNotify(s);
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const prevSub = alienSignals.setActiveSub(void 0);
|
|
47
|
+
const prev = s();
|
|
48
|
+
alienSignals.setActiveSub(prevSub);
|
|
49
|
+
const next = v(prev);
|
|
50
|
+
if (equals && equals(prev, next)) return;
|
|
51
|
+
s(next);
|
|
52
|
+
if (equals && Object.is(prev, next)) forceNotify(s);
|
|
53
|
+
}
|
|
54
|
+
function forceNotify(s) {
|
|
55
|
+
alienSignals.trigger(() => {
|
|
56
|
+
s();
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function createSignal(initialValue, options) {
|
|
60
|
+
const s = alienSignals.signal(initialValue);
|
|
61
|
+
const getter = s;
|
|
62
|
+
if (__DEV__ && options?.name) {
|
|
63
|
+
signalNames.set(getter, options.name);
|
|
64
|
+
}
|
|
65
|
+
const eq = options?.equals;
|
|
66
|
+
const setter = (v) => applySignalSet(s, v, eq);
|
|
67
|
+
return [getter, setter];
|
|
68
|
+
}
|
|
69
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
70
|
+
function isErrBox(v) {
|
|
71
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
72
|
+
}
|
|
73
|
+
function createComputed(fn) {
|
|
74
|
+
let errored = false;
|
|
75
|
+
let error;
|
|
76
|
+
let lastGood;
|
|
77
|
+
const raw = alienSignals.computed(() => {
|
|
78
|
+
try {
|
|
79
|
+
const v = fn(lastGood);
|
|
80
|
+
errored = false;
|
|
81
|
+
error = void 0;
|
|
82
|
+
lastGood = v;
|
|
83
|
+
return v;
|
|
84
|
+
} catch (e) {
|
|
85
|
+
errored = true;
|
|
86
|
+
error = e;
|
|
87
|
+
reportError(e, "computed");
|
|
88
|
+
return { [ERR]: e };
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
const reader = () => {
|
|
92
|
+
const v = raw();
|
|
93
|
+
if (errored || isErrBox(v)) throw error;
|
|
94
|
+
return v;
|
|
95
|
+
};
|
|
96
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
97
|
+
return reader;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
exports.__DEV__ = __DEV__;
|
|
101
|
+
exports.createComputed = createComputed;
|
|
102
|
+
exports.createSignal = createSignal;
|
|
103
|
+
exports.onError = onError;
|
|
104
|
+
exports.reportError = reportError;
|
|
105
|
+
//# sourceMappingURL=chunk-W7OUWVRA.cjs.map
|
|
106
|
+
//# sourceMappingURL=chunk-W7OUWVRA.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-W7OUWVRA.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"]}
|
|
@@ -0,0 +1,100 @@
|
|
|
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 applySignalSet(s, v, equals) {
|
|
31
|
+
if (typeof v !== "function") {
|
|
32
|
+
if (!equals) {
|
|
33
|
+
s(v);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const prevSub2 = setActiveSub(void 0);
|
|
37
|
+
const prev2 = s();
|
|
38
|
+
setActiveSub(prevSub2);
|
|
39
|
+
if (equals(prev2, v)) return;
|
|
40
|
+
s(v);
|
|
41
|
+
if (Object.is(prev2, v)) forceNotify(s);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const prevSub = setActiveSub(void 0);
|
|
45
|
+
const prev = s();
|
|
46
|
+
setActiveSub(prevSub);
|
|
47
|
+
const next = v(prev);
|
|
48
|
+
if (equals && equals(prev, next)) return;
|
|
49
|
+
s(next);
|
|
50
|
+
if (equals && Object.is(prev, next)) forceNotify(s);
|
|
51
|
+
}
|
|
52
|
+
function forceNotify(s) {
|
|
53
|
+
trigger(() => {
|
|
54
|
+
s();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function createSignal(initialValue, options) {
|
|
58
|
+
const s = signal(initialValue);
|
|
59
|
+
const getter = s;
|
|
60
|
+
if (__DEV__ && options?.name) {
|
|
61
|
+
signalNames.set(getter, options.name);
|
|
62
|
+
}
|
|
63
|
+
const eq = options?.equals;
|
|
64
|
+
const setter = (v) => applySignalSet(s, v, eq);
|
|
65
|
+
return [getter, setter];
|
|
66
|
+
}
|
|
67
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
68
|
+
function isErrBox(v) {
|
|
69
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
70
|
+
}
|
|
71
|
+
function createComputed(fn) {
|
|
72
|
+
let errored = false;
|
|
73
|
+
let error;
|
|
74
|
+
let lastGood;
|
|
75
|
+
const raw = computed(() => {
|
|
76
|
+
try {
|
|
77
|
+
const v = fn(lastGood);
|
|
78
|
+
errored = false;
|
|
79
|
+
error = void 0;
|
|
80
|
+
lastGood = v;
|
|
81
|
+
return v;
|
|
82
|
+
} catch (e) {
|
|
83
|
+
errored = true;
|
|
84
|
+
error = e;
|
|
85
|
+
reportError(e, "computed");
|
|
86
|
+
return { [ERR]: e };
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
const reader = () => {
|
|
90
|
+
const v = raw();
|
|
91
|
+
if (errored || isErrBox(v)) throw error;
|
|
92
|
+
return v;
|
|
93
|
+
};
|
|
94
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
95
|
+
return reader;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { __DEV__, createComputed, createSignal, onError, reportError };
|
|
99
|
+
//# sourceMappingURL=chunk-XLVBYXOU.js.map
|
|
100
|
+
//# sourceMappingURL=chunk-XLVBYXOU.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-XLVBYXOU.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"]}
|
|
@@ -438,11 +438,32 @@ var FormaRuntime = (function (exports) {
|
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
// src/reactive/
|
|
441
|
+
// src/reactive/dev.ts
|
|
442
|
+
function isDev() {
|
|
443
|
+
if (typeof process !== "undefined") {
|
|
444
|
+
return process.env?.NODE_ENV !== "production";
|
|
445
|
+
}
|
|
446
|
+
return typeof __FORMA_DEV__ !== "undefined" ? !!__FORMA_DEV__ : false;
|
|
447
|
+
}
|
|
448
|
+
isDev();
|
|
449
|
+
var _errorHandlers = /* @__PURE__ */ new Set();
|
|
450
|
+
function reportError(error, source) {
|
|
451
|
+
for (const handler of _errorHandlers) {
|
|
452
|
+
try {
|
|
453
|
+
handler(error, source ? { source } : {});
|
|
454
|
+
} catch {
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (isDev()) {
|
|
458
|
+
console.error(`[forma] ${source} error:`, error);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
442
461
|
function applySignalSet(s, v, equals) {
|
|
443
462
|
if (typeof v !== "function") {
|
|
444
|
-
|
|
445
|
-
|
|
463
|
+
{
|
|
464
|
+
s(v);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
446
467
|
}
|
|
447
468
|
const prevSub = setActiveSub(void 0);
|
|
448
469
|
const prev = s();
|
|
@@ -462,8 +483,35 @@ var FormaRuntime = (function (exports) {
|
|
|
462
483
|
}
|
|
463
484
|
|
|
464
485
|
// src/reactive/computed.ts
|
|
486
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
487
|
+
function isErrBox(v) {
|
|
488
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
489
|
+
}
|
|
465
490
|
function createComputed(fn) {
|
|
466
|
-
|
|
491
|
+
let errored = false;
|
|
492
|
+
let error;
|
|
493
|
+
let lastGood;
|
|
494
|
+
const raw = computed(() => {
|
|
495
|
+
try {
|
|
496
|
+
const v = fn(lastGood);
|
|
497
|
+
errored = false;
|
|
498
|
+
error = void 0;
|
|
499
|
+
lastGood = v;
|
|
500
|
+
return v;
|
|
501
|
+
} catch (e) {
|
|
502
|
+
errored = true;
|
|
503
|
+
error = e;
|
|
504
|
+
reportError(e, "computed");
|
|
505
|
+
return { [ERR]: e };
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
const reader = () => {
|
|
509
|
+
const v = raw();
|
|
510
|
+
if (errored || isErrBox(v)) throw error;
|
|
511
|
+
return v;
|
|
512
|
+
};
|
|
513
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
514
|
+
return reader;
|
|
467
515
|
}
|
|
468
516
|
|
|
469
517
|
// src/reactive/batch.ts
|
package/dist/forma-runtime.js
CHANGED
|
@@ -348,6 +348,33 @@ var FormaRuntime = (() => {
|
|
|
348
348
|
}
|
|
349
349
|
return effectOper.bind(e);
|
|
350
350
|
}
|
|
351
|
+
function trigger(fn) {
|
|
352
|
+
const sub = {
|
|
353
|
+
deps: void 0,
|
|
354
|
+
depsTail: void 0,
|
|
355
|
+
flags: 2
|
|
356
|
+
};
|
|
357
|
+
const prevSub = setActiveSub(sub);
|
|
358
|
+
try {
|
|
359
|
+
fn();
|
|
360
|
+
} finally {
|
|
361
|
+
activeSub = prevSub;
|
|
362
|
+
let link2 = sub.deps;
|
|
363
|
+
while (link2 !== void 0) {
|
|
364
|
+
const dep = link2.dep;
|
|
365
|
+
link2 = unlink(link2, sub);
|
|
366
|
+
const subs = dep.subs;
|
|
367
|
+
if (subs !== void 0) {
|
|
368
|
+
sub.flags = 0;
|
|
369
|
+
propagate(subs);
|
|
370
|
+
shallowPropagate(subs);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (!batchDepth) {
|
|
374
|
+
flush();
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
351
378
|
function updateComputed(c) {
|
|
352
379
|
++cycle;
|
|
353
380
|
c.depsTail = void 0;
|
|
@@ -478,16 +505,41 @@ var FormaRuntime = (() => {
|
|
|
478
505
|
}
|
|
479
506
|
}
|
|
480
507
|
|
|
508
|
+
// src/reactive/dev.ts
|
|
509
|
+
function isDev() {
|
|
510
|
+
if (typeof process !== "undefined") {
|
|
511
|
+
return process.env?.NODE_ENV !== "production";
|
|
512
|
+
}
|
|
513
|
+
return typeof __FORMA_DEV__ !== "undefined" ? !!__FORMA_DEV__ : false;
|
|
514
|
+
}
|
|
515
|
+
var __DEV__ = isDev();
|
|
516
|
+
var _errorHandlers = /* @__PURE__ */ new Set();
|
|
517
|
+
function reportError(error, source) {
|
|
518
|
+
for (const handler of _errorHandlers) {
|
|
519
|
+
try {
|
|
520
|
+
handler(error, source ? { source } : {});
|
|
521
|
+
} catch {
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
if (isDev()) {
|
|
525
|
+
console.error(`[forma] ${source ?? "Unknown"} error:`, error);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
481
529
|
// src/reactive/signal.ts
|
|
530
|
+
var signalNames = /* @__PURE__ */ new WeakMap();
|
|
482
531
|
function applySignalSet(s, v, equals) {
|
|
483
532
|
if (typeof v !== "function") {
|
|
484
|
-
if (equals) {
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
setActiveSub(prevSub2);
|
|
488
|
-
if (equals(prev2, v)) return;
|
|
533
|
+
if (!equals) {
|
|
534
|
+
s(v);
|
|
535
|
+
return;
|
|
489
536
|
}
|
|
537
|
+
const prevSub2 = setActiveSub(void 0);
|
|
538
|
+
const prev2 = s();
|
|
539
|
+
setActiveSub(prevSub2);
|
|
540
|
+
if (equals(prev2, v)) return;
|
|
490
541
|
s(v);
|
|
542
|
+
if (Object.is(prev2, v)) forceNotify(s);
|
|
491
543
|
return;
|
|
492
544
|
}
|
|
493
545
|
const prevSub = setActiveSub(void 0);
|
|
@@ -496,24 +548,33 @@ var FormaRuntime = (() => {
|
|
|
496
548
|
const next = v(prev);
|
|
497
549
|
if (equals && equals(prev, next)) return;
|
|
498
550
|
s(next);
|
|
551
|
+
if (equals && Object.is(prev, next)) forceNotify(s);
|
|
552
|
+
}
|
|
553
|
+
function forceNotify(s) {
|
|
554
|
+
trigger(() => {
|
|
555
|
+
s();
|
|
556
|
+
});
|
|
499
557
|
}
|
|
500
558
|
function createSignal(initialValue, options) {
|
|
501
559
|
const s = signal(initialValue);
|
|
502
560
|
const getter = s;
|
|
561
|
+
if (__DEV__ && options?.name) {
|
|
562
|
+
signalNames.set(getter, options.name);
|
|
563
|
+
}
|
|
503
564
|
const eq = options?.equals;
|
|
504
565
|
const setter = (v) => applySignalSet(s, v, eq);
|
|
505
566
|
return [getter, setter];
|
|
506
567
|
}
|
|
507
568
|
|
|
508
569
|
// src/reactive/root.ts
|
|
509
|
-
var
|
|
570
|
+
var currentOwner = null;
|
|
510
571
|
function registerDisposer(dispose) {
|
|
511
|
-
if (
|
|
512
|
-
|
|
572
|
+
if (currentOwner) {
|
|
573
|
+
currentOwner.disposers.push(dispose);
|
|
513
574
|
}
|
|
514
575
|
}
|
|
515
576
|
function hasActiveRoot() {
|
|
516
|
-
return
|
|
577
|
+
return currentOwner !== null;
|
|
517
578
|
}
|
|
518
579
|
|
|
519
580
|
// src/reactive/effect.ts
|
|
@@ -529,8 +590,35 @@ var FormaRuntime = (() => {
|
|
|
529
590
|
}
|
|
530
591
|
|
|
531
592
|
// src/reactive/computed.ts
|
|
593
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
594
|
+
function isErrBox(v) {
|
|
595
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
596
|
+
}
|
|
532
597
|
function createComputed(fn) {
|
|
533
|
-
|
|
598
|
+
let errored = false;
|
|
599
|
+
let error;
|
|
600
|
+
let lastGood;
|
|
601
|
+
const raw = computed(() => {
|
|
602
|
+
try {
|
|
603
|
+
const v = fn(lastGood);
|
|
604
|
+
errored = false;
|
|
605
|
+
error = void 0;
|
|
606
|
+
lastGood = v;
|
|
607
|
+
return v;
|
|
608
|
+
} catch (e) {
|
|
609
|
+
errored = true;
|
|
610
|
+
error = e;
|
|
611
|
+
reportError(e, "computed");
|
|
612
|
+
return { [ERR]: e };
|
|
613
|
+
}
|
|
614
|
+
});
|
|
615
|
+
const reader = () => {
|
|
616
|
+
const v = raw();
|
|
617
|
+
if (errored || isErrBox(v)) throw error;
|
|
618
|
+
return v;
|
|
619
|
+
};
|
|
620
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
621
|
+
return reader;
|
|
534
622
|
}
|
|
535
623
|
|
|
536
624
|
// src/reactive/batch.ts
|
|
@@ -438,11 +438,32 @@ var FormaRuntime = (function (exports) {
|
|
|
438
438
|
}
|
|
439
439
|
}
|
|
440
440
|
|
|
441
|
-
// src/reactive/
|
|
441
|
+
// src/reactive/dev.ts
|
|
442
|
+
function isDev() {
|
|
443
|
+
if (typeof process !== "undefined") {
|
|
444
|
+
return process.env?.NODE_ENV !== "production";
|
|
445
|
+
}
|
|
446
|
+
return typeof __FORMA_DEV__ !== "undefined" ? !!__FORMA_DEV__ : false;
|
|
447
|
+
}
|
|
448
|
+
isDev();
|
|
449
|
+
var _errorHandlers = /* @__PURE__ */ new Set();
|
|
450
|
+
function reportError(error, source) {
|
|
451
|
+
for (const handler of _errorHandlers) {
|
|
452
|
+
try {
|
|
453
|
+
handler(error, source ? { source } : {});
|
|
454
|
+
} catch {
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
if (isDev()) {
|
|
458
|
+
console.error(`[forma] ${source} error:`, error);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
442
461
|
function applySignalSet(s, v, equals) {
|
|
443
462
|
if (typeof v !== "function") {
|
|
444
|
-
|
|
445
|
-
|
|
463
|
+
{
|
|
464
|
+
s(v);
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
446
467
|
}
|
|
447
468
|
const prevSub = setActiveSub(void 0);
|
|
448
469
|
const prev = s();
|
|
@@ -462,8 +483,35 @@ var FormaRuntime = (function (exports) {
|
|
|
462
483
|
}
|
|
463
484
|
|
|
464
485
|
// src/reactive/computed.ts
|
|
486
|
+
var ERR = /* @__PURE__ */ Symbol("formaComputedError");
|
|
487
|
+
function isErrBox(v) {
|
|
488
|
+
return typeof v === "object" && v !== null && ERR in v;
|
|
489
|
+
}
|
|
465
490
|
function createComputed(fn) {
|
|
466
|
-
|
|
491
|
+
let errored = false;
|
|
492
|
+
let error;
|
|
493
|
+
let lastGood;
|
|
494
|
+
const raw = computed(() => {
|
|
495
|
+
try {
|
|
496
|
+
const v = fn(lastGood);
|
|
497
|
+
errored = false;
|
|
498
|
+
error = void 0;
|
|
499
|
+
lastGood = v;
|
|
500
|
+
return v;
|
|
501
|
+
} catch (e) {
|
|
502
|
+
errored = true;
|
|
503
|
+
error = e;
|
|
504
|
+
reportError(e, "computed");
|
|
505
|
+
return { [ERR]: e };
|
|
506
|
+
}
|
|
507
|
+
});
|
|
508
|
+
const reader = () => {
|
|
509
|
+
const v = raw();
|
|
510
|
+
if (errored || isErrBox(v)) throw error;
|
|
511
|
+
return v;
|
|
512
|
+
};
|
|
513
|
+
Object.defineProperty(reader, "name", { value: raw.name, configurable: true });
|
|
514
|
+
return reader;
|
|
467
515
|
}
|
|
468
516
|
|
|
469
517
|
// src/reactive/batch.ts
|