@getforma/core 1.3.0 → 1.5.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-AJP4OVCN.js → chunk-3U2IQIKB.js} +4 -4
- package/dist/{chunk-AJP4OVCN.js.map → chunk-3U2IQIKB.js.map} +1 -1
- package/dist/{chunk-FBM7V4NE.cjs → chunk-D46EJ3VS.cjs} +43 -43
- package/dist/{chunk-FBM7V4NE.cjs.map → chunk-D46EJ3VS.cjs.map} +1 -1
- package/dist/{chunk-Y3A2EVVS.js → chunk-F5QUU76U.js} +4 -4
- package/dist/{chunk-Y3A2EVVS.js.map → chunk-F5QUU76U.js.map} +1 -1
- package/dist/{chunk-ETPJTPYD.js → chunk-FAIWRZKJ.js} +6 -3
- package/dist/chunk-FAIWRZKJ.js.map +1 -0
- package/dist/{chunk-H7MDUHS5.cjs → chunk-HAULTMNX.cjs} +6 -2
- package/dist/chunk-HAULTMNX.cjs.map +1 -0
- package/dist/{chunk-KUXNZ5MG.cjs → chunk-PAKPQRVE.cjs} +14 -12
- package/dist/{chunk-KUXNZ5MG.cjs.map → chunk-PAKPQRVE.cjs.map} +1 -1
- package/dist/formajs.global.js +69 -11
- package/dist/formajs.global.js.map +1 -1
- package/dist/http.cjs +11 -11
- package/dist/http.js +2 -2
- package/dist/index.cjs +143 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +39 -5
- package/dist/index.d.ts +39 -5
- package/dist/index.js +67 -16
- package/dist/index.js.map +1 -1
- package/dist/{resource-D6HfVgiA.d.cts → resource-CBTcorBJ.d.ts} +2 -2
- package/dist/{resource-ljs2X5NM.d.ts → resource-CaFfIB5i.d.cts} +2 -2
- package/dist/runtime.cjs +28 -28
- package/dist/runtime.js +3 -3
- package/dist/server.cjs +69 -16
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +31 -5
- package/dist/server.d.ts +31 -5
- package/dist/server.js +64 -12
- package/dist/server.js.map +1 -1
- package/dist/{signal-CRBJYQ6B.d.cts → signal-CIXTR4Qz.d.cts} +29 -1
- package/dist/{signal-CRBJYQ6B.d.ts → signal-CIXTR4Qz.d.ts} +29 -1
- package/dist/ssr/index.cjs +22 -4
- package/dist/ssr/index.cjs.map +1 -1
- package/dist/ssr/index.d.cts +7 -0
- package/dist/ssr/index.d.ts +7 -0
- package/dist/ssr/index.js +22 -4
- package/dist/ssr/index.js.map +1 -1
- package/dist/storage.cjs +66 -16
- package/dist/storage.cjs.map +1 -1
- package/dist/storage.js +66 -16
- package/dist/storage.js.map +1 -1
- package/dist/tc39-compat.cjs +3 -3
- package/dist/tc39-compat.d.cts +1 -1
- package/dist/tc39-compat.d.ts +1 -1
- package/dist/tc39-compat.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-ETPJTPYD.js.map +0 -1
- package/dist/chunk-H7MDUHS5.cjs.map +0 -1
package/dist/storage.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/storage/local.ts","../src/storage/session.ts","../src/storage/indexed.ts"],"names":[],"mappings":"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","file":"storage.js","sourcesContent":["/**\n * Forma Storage - Local\n *\n * Typed localStorage wrapper with graceful error handling.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { TypedStorage, StorageOptions } from './types.js';\n\n/**\n * Create a typed localStorage wrapper for the given key.\n *\n * ```ts\n * const store = createLocalStorage<{ name: string }>('user');\n * store.set({ name: 'Alice' });\n * store.get(); // { name: 'Alice' }\n * store.remove();\n * ```\n */\nexport function createLocalStorage<T>(\n key: string,\n options?: StorageOptions<T>,\n): TypedStorage<T> {\n const serialize = options?.serialize ?? JSON.stringify;\n const deserialize = options?.deserialize ?? JSON.parse;\n const validate = options?.validate;\n\n return {\n key,\n\n get(): T | null {\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return null;\n const value = deserialize(raw);\n if (validate && !validate(value)) return null;\n return value as T;\n } catch {\n return null;\n }\n },\n\n set(value: T): void {\n try {\n const serialized = serialize(value);\n localStorage.setItem(key, serialized);\n } catch { /* QuotaExceededError or storage disabled — silently ignore */ }\n },\n\n remove(): void {\n localStorage.removeItem(key);\n },\n };\n}\n","/**\n * Forma Storage - Session\n *\n * Typed sessionStorage wrapper with graceful error handling.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { TypedStorage, StorageOptions } from './types.js';\n\n/**\n * Create a typed sessionStorage wrapper for the given key.\n *\n * ```ts\n * const store = createSessionStorage<{ token: string }>('auth');\n * store.set({ token: 'abc123' });\n * store.get(); // { token: 'abc123' }\n * store.remove();\n * ```\n */\nexport function createSessionStorage<T>(\n key: string,\n options?: StorageOptions<T>,\n): TypedStorage<T> {\n const serialize = options?.serialize ?? JSON.stringify;\n const deserialize = options?.deserialize ?? JSON.parse;\n const validate = options?.validate;\n\n return {\n key,\n\n get(): T | null {\n try {\n const raw = sessionStorage.getItem(key);\n if (raw === null) return null;\n const value = deserialize(raw);\n if (validate && !validate(value)) return null;\n return value as T;\n } catch {\n return null;\n }\n },\n\n set(value: T): void {\n try {\n const serialized = serialize(value);\n sessionStorage.setItem(key, serialized);\n } catch { /* QuotaExceededError or storage disabled — silently ignore */ }\n },\n\n remove(): void {\n sessionStorage.removeItem(key);\n },\n };\n}\n","/**\n * Forma Storage - IndexedDB\n *\n * Simplified IndexedDB wrapper with lazy connection and promise-based API.\n * Zero dependencies — native browser APIs only.\n */\n\nexport interface IDBStore<T> {\n get(key: string): Promise<T | undefined>;\n set(key: string, value: T): Promise<void>;\n delete(key: string): Promise<void>;\n getAll(): Promise<T[]>;\n keys(): Promise<string[]>;\n clear(): Promise<void>;\n}\n\n/** Cached database connections keyed by \"dbName::storeName\". */\nconst dbCache = new Map<string, Promise<IDBDatabase>>();\n\n/**\n * Open (or retrieve a cached) IndexedDB database, ensuring the\n * requested object store exists.\n */\nfunction openDB(dbName: string, storeName: string): Promise<IDBDatabase> {\n const cacheKey = `${dbName}::${storeName}`;\n const cached = dbCache.get(cacheKey);\n if (cached) return cached;\n\n const promise = new Promise<IDBDatabase>((resolve, reject) => {\n // First, try opening at the current (or default) version.\n const probe = indexedDB.open(dbName);\n\n probe.onerror = () => reject(probe.error);\n\n probe.onsuccess = () => {\n const db = probe.result;\n if (db.objectStoreNames.contains(storeName)) {\n resolve(db);\n return;\n }\n // Store doesn't exist yet — close and reopen with a version bump.\n const nextVersion = db.version + 1;\n db.close();\n\n const upgrade = indexedDB.open(dbName, nextVersion);\n upgrade.onerror = () => reject(upgrade.error);\n upgrade.onupgradeneeded = () => {\n const upgradedDB = upgrade.result;\n if (!upgradedDB.objectStoreNames.contains(storeName)) {\n upgradedDB.createObjectStore(storeName);\n }\n };\n upgrade.onsuccess = () => resolve(upgrade.result);\n };\n\n probe.onupgradeneeded = () => {\n const db = probe.result;\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n };\n });\n\n dbCache.set(cacheKey, promise);\n\n // If the open fails, remove from cache so the next call retries.\n promise.catch(() => {\n dbCache.delete(cacheKey);\n });\n\n return promise;\n}\n\n/**\n * Create a simplified IndexedDB store.\n *\n * ```ts\n * const store = createIndexedDB<User>('myApp', 'users');\n * await store.set('u1', { name: 'Alice' });\n * const user = await store.get('u1'); // { name: 'Alice' }\n * ```\n */\nexport function createIndexedDB<T>(\n dbName: string,\n storeName: string = 'default',\n): IDBStore<T> {\n /** Helper: run a single read/write transaction and return the request result. */\n function withStore<R>(\n mode: IDBTransactionMode,\n fn: (store: IDBObjectStore) => IDBRequest<R>,\n ): Promise<R> {\n return openDB(dbName, storeName).then(\n (db) =>\n new Promise<R>((resolve, reject) => {\n const tx = db.transaction(storeName, mode);\n const store = tx.objectStore(storeName);\n const request = fn(store);\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n }),\n );\n }\n\n return {\n get(key: string): Promise<T | undefined> {\n return withStore('readonly', (store) => store.get(key)) as Promise<T | undefined>;\n },\n\n set(key: string, value: T): Promise<void> {\n return withStore('readwrite', (store) => store.put(value, key)).then(\n () => undefined,\n );\n },\n\n delete(key: string): Promise<void> {\n return withStore('readwrite', (store) => store.delete(key)).then(\n () => undefined,\n );\n },\n\n getAll(): Promise<T[]> {\n return withStore('readonly', (store) => store.getAll()) as Promise<T[]>;\n },\n\n keys(): Promise<string[]> {\n return withStore('readonly', (store) => store.getAllKeys()).then(\n (keys) => keys.map(String),\n );\n },\n\n clear(): Promise<void> {\n return withStore('readwrite', (store) => store.clear()).then(\n () => undefined,\n );\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/storage/local.ts","../src/storage/session.ts","../src/storage/indexed.ts"],"names":[],"mappings":"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","file":"storage.js","sourcesContent":["/**\n * Forma Storage - Local\n *\n * Typed localStorage wrapper with graceful error handling.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { TypedStorage, StorageOptions } from './types.js';\n\n/**\n * Create a typed localStorage wrapper for the given key.\n *\n * ```ts\n * const store = createLocalStorage<{ name: string }>('user');\n * store.set({ name: 'Alice' });\n * store.get(); // { name: 'Alice' }\n * store.remove();\n * ```\n */\nexport function createLocalStorage<T>(\n key: string,\n options?: StorageOptions<T>,\n): TypedStorage<T> {\n const serialize = options?.serialize ?? JSON.stringify;\n const deserialize = options?.deserialize ?? JSON.parse;\n const validate = options?.validate;\n\n return {\n key,\n\n get(): T | null {\n try {\n const raw = localStorage.getItem(key);\n if (raw === null) return null;\n const value = deserialize(raw);\n if (validate && !validate(value)) return null;\n return value as T;\n } catch {\n return null;\n }\n },\n\n set(value: T): void {\n try {\n const serialized = serialize(value);\n localStorage.setItem(key, serialized);\n } catch { /* QuotaExceededError or storage disabled — silently ignore */ }\n },\n\n remove(): void {\n try {\n localStorage.removeItem(key);\n } catch { /* storage disabled — silently ignore, matching get/set */ }\n },\n };\n}\n","/**\n * Forma Storage - Session\n *\n * Typed sessionStorage wrapper with graceful error handling.\n * Zero dependencies — native browser APIs only.\n */\n\nimport { TypedStorage, StorageOptions } from './types.js';\n\n/**\n * Create a typed sessionStorage wrapper for the given key.\n *\n * ```ts\n * const store = createSessionStorage<{ token: string }>('auth');\n * store.set({ token: 'abc123' });\n * store.get(); // { token: 'abc123' }\n * store.remove();\n * ```\n */\nexport function createSessionStorage<T>(\n key: string,\n options?: StorageOptions<T>,\n): TypedStorage<T> {\n const serialize = options?.serialize ?? JSON.stringify;\n const deserialize = options?.deserialize ?? JSON.parse;\n const validate = options?.validate;\n\n return {\n key,\n\n get(): T | null {\n try {\n const raw = sessionStorage.getItem(key);\n if (raw === null) return null;\n const value = deserialize(raw);\n if (validate && !validate(value)) return null;\n return value as T;\n } catch {\n return null;\n }\n },\n\n set(value: T): void {\n try {\n const serialized = serialize(value);\n sessionStorage.setItem(key, serialized);\n } catch { /* QuotaExceededError or storage disabled — silently ignore */ }\n },\n\n remove(): void {\n try {\n sessionStorage.removeItem(key);\n } catch { /* storage disabled — silently ignore, matching get/set */ }\n },\n };\n}\n","/**\n * Forma Storage - IndexedDB\n *\n * Simplified IndexedDB wrapper with lazy connection and promise-based API.\n * Zero dependencies — native browser APIs only.\n */\n\nexport interface IDBStore<T> {\n get(key: string): Promise<T | undefined>;\n set(key: string, value: T): Promise<void>;\n delete(key: string): Promise<void>;\n getAll(): Promise<T[]>;\n keys(): Promise<string[]>;\n clear(): Promise<void>;\n}\n\n/** Cached database connections keyed by \"dbName::storeName\". */\nconst dbCache = new Map<string, Promise<IDBDatabase>>();\n\n/**\n * Per-database open serialization. Concurrent createIndexedDB(db, storeA/B/...)\n * calls each bump db.version+1; without serialization they compute the version\n * from a stale probe and race, so some stores are never created and their\n * connections are permanently wedged. Chaining opens for the same db name makes\n * each see the previous open's committed version.\n */\nconst openLocks = new Map<string, Promise<unknown>>();\n\n/**\n * Open (or retrieve a cached) IndexedDB database, ensuring the\n * requested object store exists.\n */\nfunction openDB(dbName: string, storeName: string): Promise<IDBDatabase> {\n const cacheKey = `${dbName}::${storeName}`;\n const cached = dbCache.get(cacheKey);\n if (cached) return cached;\n\n // Serialize this open behind any in-flight open for the same db name.\n const prevLock = openLocks.get(dbName) ?? Promise.resolve();\n const promise = prevLock.then(() => doOpen(dbName, storeName, cacheKey), () => doOpen(dbName, storeName, cacheKey));\n\n dbCache.set(cacheKey, promise);\n // Advance the lock (never rejects, so the chain keeps flowing).\n openLocks.set(dbName, promise.then(() => undefined, () => undefined));\n\n // If the open fails, remove from cache so the next call retries.\n promise.catch(() => {\n dbCache.delete(cacheKey);\n });\n\n return promise;\n}\n\nfunction doOpen(dbName: string, storeName: string, cacheKey: string): Promise<IDBDatabase> {\n return new Promise<IDBDatabase>((resolve, reject) => {\n // Wire a resolved connection so it closes + evicts on a version change\n // (another tab/store bumping the version) or a forced close. Without this a\n // later bump blocks forever and a stale connection is never dropped.\n const wire = (db: IDBDatabase) => {\n db.onversionchange = () => { db.close(); dbCache.delete(cacheKey); };\n db.onclose = () => { dbCache.delete(cacheKey); };\n resolve(db);\n };\n\n // First, try opening at the current (or default) version.\n const probe = indexedDB.open(dbName);\n\n probe.onerror = () => reject(probe.error);\n\n probe.onsuccess = () => {\n const db = probe.result;\n if (db.objectStoreNames.contains(storeName)) {\n wire(db);\n return;\n }\n // Store doesn't exist yet — close and reopen with a version bump. Opens\n // are serialized per db, so this version is current, not stale.\n const nextVersion = db.version + 1;\n db.close();\n\n const upgrade = indexedDB.open(dbName, nextVersion);\n upgrade.onerror = () => reject(upgrade.error);\n // Backstop: if another connection blocks the bump, settle (reject) so the\n // promise does not leak; the next call retries.\n upgrade.onblocked = () =>\n reject(upgrade.error ?? new DOMException('Upgrade blocked: another connection is open', 'AbortError'));\n upgrade.onupgradeneeded = () => {\n const upgradedDB = upgrade.result;\n if (!upgradedDB.objectStoreNames.contains(storeName)) {\n upgradedDB.createObjectStore(storeName);\n }\n };\n upgrade.onsuccess = () => {\n const upgradedDB = upgrade.result;\n if (!upgradedDB.objectStoreNames.contains(storeName)) {\n // Should not happen with serialized opens; reject (do not cache a\n // store-less connection) so the next call retries cleanly.\n upgradedDB.close();\n reject(new DOMException(`Object store \"${storeName}\" was not created`, 'NotFoundError'));\n return;\n }\n wire(upgradedDB);\n };\n };\n\n probe.onupgradeneeded = () => {\n const db = probe.result;\n if (!db.objectStoreNames.contains(storeName)) {\n db.createObjectStore(storeName);\n }\n };\n });\n}\n\n/**\n * Create a simplified IndexedDB store.\n *\n * ```ts\n * const store = createIndexedDB<User>('myApp', 'users');\n * await store.set('u1', { name: 'Alice' });\n * const user = await store.get('u1'); // { name: 'Alice' }\n * ```\n */\nexport function createIndexedDB<T>(\n dbName: string,\n storeName: string = 'default',\n): IDBStore<T> {\n const cacheKey = `${dbName}::${storeName}`;\n\n /** Helper: run a single read/write transaction and return the request result. */\n function withStore<R>(\n mode: IDBTransactionMode,\n fn: (store: IDBObjectStore) => IDBRequest<R>,\n ): Promise<R> {\n return openDB(dbName, storeName).then(\n (db) =>\n new Promise<R>((resolve, reject) => {\n let request: IDBRequest<R>;\n let tx: IDBTransaction;\n try {\n tx = db.transaction(storeName, mode);\n request = fn(tx.objectStore(storeName));\n } catch (err) {\n // A transaction on a closing connection throws synchronously.\n reject(err);\n return;\n }\n let result: R;\n request.onsuccess = () => {\n result = request.result;\n // Reads are durable at request success; writes only at commit.\n if (mode === 'readonly') resolve(result);\n };\n // A readwrite value is only durable once the transaction COMMITS —\n // resolving on request.onsuccess would falsely report success for a\n // write that later aborts (e.g. QuotaExceededError at commit).\n tx.oncomplete = () => resolve(result);\n tx.onerror = () => reject(tx.error ?? request.error);\n tx.onabort = () => reject(tx.error ?? request.error ?? new DOMException('Transaction aborted', 'AbortError'));\n }),\n ).catch((err: unknown) => {\n // Evict a broken/closing/store-less connection so the next call reopens\n // cleanly (NotFoundError = the cached connection lacks the store).\n const name = (err as { name?: string } | null)?.name;\n if (name === 'InvalidStateError' || name === 'AbortError' || name === 'NotFoundError') {\n dbCache.delete(cacheKey);\n }\n throw err;\n });\n }\n\n return {\n get(key: string): Promise<T | undefined> {\n return withStore('readonly', (store) => store.get(key)) as Promise<T | undefined>;\n },\n\n set(key: string, value: T): Promise<void> {\n return withStore('readwrite', (store) => store.put(value, key)).then(\n () => undefined,\n );\n },\n\n delete(key: string): Promise<void> {\n return withStore('readwrite', (store) => store.delete(key)).then(\n () => undefined,\n );\n },\n\n getAll(): Promise<T[]> {\n return withStore('readonly', (store) => store.getAll()) as Promise<T[]>;\n },\n\n keys(): Promise<string[]> {\n return withStore('readonly', (store) => store.getAllKeys()).then(\n (keys) => keys.map(String),\n );\n },\n\n clear(): Promise<void> {\n return withStore('readwrite', (store) => store.clear()).then(\n () => undefined,\n );\n },\n };\n}\n"]}
|
package/dist/tc39-compat.cjs
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var chunkHAULTMNX_cjs = require('./chunk-HAULTMNX.cjs');
|
|
4
4
|
|
|
5
5
|
// src/reactive/tc39-compat.ts
|
|
6
6
|
var State = class {
|
|
7
7
|
_get;
|
|
8
8
|
_set;
|
|
9
9
|
constructor(initialValue, options) {
|
|
10
|
-
const [getter, setter] =
|
|
10
|
+
const [getter, setter] = chunkHAULTMNX_cjs.createSignal(initialValue, options);
|
|
11
11
|
this._get = getter;
|
|
12
12
|
this._set = setter;
|
|
13
13
|
}
|
|
@@ -21,7 +21,7 @@ var State = class {
|
|
|
21
21
|
var Computed = class {
|
|
22
22
|
_get;
|
|
23
23
|
constructor(fn) {
|
|
24
|
-
this._get =
|
|
24
|
+
this._get = chunkHAULTMNX_cjs.createComputed(fn);
|
|
25
25
|
}
|
|
26
26
|
get() {
|
|
27
27
|
return this._get();
|
package/dist/tc39-compat.d.cts
CHANGED
package/dist/tc39-compat.d.ts
CHANGED
package/dist/tc39-compat.js
CHANGED
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@getforma/core",
|
|
3
3
|
"author": "Forma <victor@getforma.dev>",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.5.0",
|
|
5
5
|
"description": "Real DOM reactive library — fine-grained signals, islands architecture, SSR hydration. No virtual DOM, no diffing. Core entry ~8KB gzipped; full HTML runtime ~24KB gzipped.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.cjs",
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|
|
@@ -1 +0,0 @@
|
|
|
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"]}
|