@mongez/atom
Advanced tools
+48
| # Changelog — @mongez/atom | ||
| ## [6.0.9] — 2026-05-27 | ||
| ### Fixed | ||
| - **Watcher leak / wrong-removal in `atom.watch(key, cb)`**. The unsubscribe captured an index from the subscription site, and that index drifted as earlier callbacks unsubscribed — splicing the wrong (or out-of-range) callback. Unsubscribe is now identity-based: it removes the callback by reference. | ||
| - **Watcher diff used the raw `newValue` argument**. When callers passed an updater function to `update(...)`, the watcher comparison ran `get(newValue, key)` on the function itself, so every watcher fired (or none did) regardless of what changed. Now uses the resolved `updatedValue`. | ||
| - **`reset()` returned dead code**. The function assigned `const update = this.update(...)` (returns `void`) and then `return update`. The local was always `undefined`. Removed. | ||
| - **`reset()` didn't deep-clone the default**. After the first `update` that mutated `currentValue`, subsequent `reset()` calls handed callers a reference into shared default state. Now clones via `@mongez/reinforcements`' `clone`. | ||
| - **`clone()` collisions on heavy use**. The 4-digit `Random.int(1000, 9999)` suffix collided in birthday-paradox territory after a few thousand clones. Replaced with a monotonic counter (`.clone.1`, `.clone.2`, …). | ||
| - **`get(key)` auto-bound any function-valued property**. Every function has a `.bind`, so the implementation rebound *every* function returned from `get()` to `this.currentValue`. That broke pre-bound methods and generated a new function identity per read. Removed entirely. | ||
| - **`change` / `silentChange` on primitive atoms silently corrupted state**. Calling `atom.change("foo", "bar")` on a `boolean` atom spread the primitive (`{...true, foo: "bar"}` → `{foo: "bar"}`) and replaced the boolean with an object. Now a compile error at the type level (see "Changed"). | ||
| - **Getter-based actions crashed `createAtom`**. `Object.keys(actions).forEach(k => actions[k].bind(atom))` invoked any getter eagerly; if the getter referenced `this.value` (which wasn't on the atom yet) it returned `undefined`, and `.bind` blew up. The action installer now uses `Object.getOwnPropertyDescriptor` and forwards getters as getters. | ||
| ### Added | ||
| - **`derive(key, get => …)`** — derived atoms with auto-tracked dependencies. The compute function runs eagerly on creation and on every dependency change. Dynamic dependency graphs are supported (conditional reads add/drop deps across runs). Errors inside `compute` are surfaced asynchronously without breaking the source atom's update cycle. | ||
| - **`persist: true | PersistAdapter`** — atom-level persistence. `true` uses the built-in localStorage adapter (no-ops on the server); a `PersistAdapter` object lets you plug in cookies, IndexedDB, `@mongez/cache`, etc. The adapter is called for the initial read (sync or async), every update writes through, and `reset()` removes the entry. Sync errors and async rejections from the adapter are swallowed — a transient storage error (quota, private-mode block) never crashes the atom. | ||
| - **`AtomStore` + `createAtomStore`**. Per-request isolation primitive for SSR. Each store holds scoped clones of atom templates, exposes `use(template)`, `get(key)`, `hydrate(snapshot)`, `snapshot()`, and `destroy()`. The React-side glue (`AtomStoreProvider`, `useAtom`, `useAtomStore`) lives in `@mongez/react-atom`. | ||
| - **`clone({ register: false })` option**. Lets the store create scoped clones without polluting the module-level `atoms` registry. | ||
| - **`enableAtomDevtools(options?)`**. Redux DevTools bridge with initial-snapshot, per-update timeline entries, `JUMP_TO_STATE` / `JUMP_TO_ACTION` time-travel via `silentUpdate`, ignore-patterns, configurable scan interval for late-registered atoms. | ||
| - **AI kit**. `llms.txt`, `llms-full.txt`, and `skills/` folder (`README`, `overview`, `atoms`, `collections`, `stores`, `actions`, `devtools`, `recipes`) for tool-assisted development. | ||
| - **Test suite**. 52 unit tests across `atom`, `atom-collection`, `atom-store`, and `devtools`. | ||
| - **CI**. GitHub Actions workflow: Node 18/20/22 × Ubuntu, plus Node 20 × Windows. | ||
| ### Changed | ||
| - **`Atom<V, A>` is now a conditional type**. Object-only methods (`merge`, `change`, `silentChange`, `get(key)`, `watch(key, cb)`) are removed from the type when `V` is a primitive. `Atom<boolean>.change(...)` is a compile error. `Atom<any>` keeps both surfaces (legacy permissive default). | ||
| - **`AtomActions<V>` no longer includes `| any`**. Was `[key: string]: (...) => any | any`. The `| any` collapsed the entire type to `any` and defeated per-action type safety. Now just `(this: Atom<V>, ...args: any[]) => any`. | ||
| - **`AtomOptions.default` is `V`, not `V | Partial<V>`**. `Partial<V>` accidentally allowed incomplete defaults that the type didn't reflect at runtime. | ||
| - **`change` / `silentChange` signatures are `(key: T, newValue: V[T])`**, not `(key, newValue: any)`. The wider `any` defeated type safety for known-shape atoms. | ||
| - **`clone()` accepts `{ register?: boolean }`**. Pure addition for stores; existing callers (`atom.clone()`) keep working. | ||
| ### Removed | ||
| - **Auto-`bind` in `atom.get(key)`**. Functions returned from `get()` are no longer rebound to `currentValue`. If your code relied on this, wrap the call: `atom.get("fn")?.bind(atom.value)`. | ||
| - **Random-suffix clone keys** (`...Cloned9123`). Replaced by deterministic counter (`...clone.1`, `...clone.2`). | ||
| ### Dependency bumps | ||
| - **`@mongez/reinforcements: ^2.3.10` → `^3.1.0`**. Compatible API for the surfaces atom uses (`clone`, `get`). See [reinforcements v3 MIGRATION](../reinforcements/MIGRATION.md) for the full diff. | ||
| ### Tests | ||
| ``` | ||
| 46 + 6 devtools + 7 derive + 12 persist = 71 passing | ||
| ``` |
+6
-6
@@ -33,3 +33,3 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); | ||
| //#region ../@mongez/atom/src/persist.ts | ||
| //#region ../atom/src/persist.ts | ||
| /** | ||
@@ -108,3 +108,3 @@ * Built-in adapter backed by `window.localStorage`. JSON-encodes the | ||
| //#endregion | ||
| //#region ../@mongez/atom/src/atom.ts | ||
| //#region ../atom/src/atom.ts | ||
| const atoms = {}; | ||
@@ -281,3 +281,3 @@ let cloneCounter = 0; | ||
| //#endregion | ||
| //#region ../@mongez/atom/src/atom-store.ts | ||
| //#region ../atom/src/atom-store.ts | ||
| /** | ||
@@ -379,3 +379,3 @@ * A store is an isolated registry of atom instances. | ||
| //#endregion | ||
| //#region ../@mongez/atom/src/derive.ts | ||
| //#region ../atom/src/derive.ts | ||
| /** | ||
@@ -462,3 +462,3 @@ * Create a derived atom. | ||
| //#endregion | ||
| //#region ../@mongez/atom/src/devtools.ts | ||
| //#region ../atom/src/devtools.ts | ||
| /** | ||
@@ -559,3 +559,3 @@ * @fileoverview Redux DevTools bridge for `@mongez/atom`. | ||
| //#endregion | ||
| //#region ../@mongez/atom/src/atom-collection.ts | ||
| //#region ../atom/src/atom-collection.ts | ||
| /** | ||
@@ -562,0 +562,0 @@ * Create an atom collection |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.cjs","names":["events"],"sources":["../../../../@mongez/atom/src/persist.ts","../../../../@mongez/atom/src/atom.ts","../../../../@mongez/atom/src/atom-store.ts","../../../../@mongez/atom/src/derive.ts","../../../../@mongez/atom/src/devtools.ts","../../../../@mongez/atom/src/atom-collection.ts"],"sourcesContent":["/**\n * @fileoverview Persistence adapters for atoms.\n *\n * Plug in any store-shaped object (cache, localStorage wrapper, cookie\n * helper, IndexedDB layer) and atoms will:\n *\n * 1. Load their initial value from the adapter at creation (sync or async).\n * 2. Write every subsequent update through to the adapter.\n * 3. Remove the entry on `reset()`.\n *\n * The default adapter is a thin localStorage wrapper for the client; it\n * silently no-ops on the server (no `window`). For SSR-safe per-request\n * persistence, supply your own cookie-aware adapter.\n */\nimport type { Atom, AtomOptions } from \"./types\";\n\n/**\n * Shape of an external store. Methods may be sync or async; the\n * persistence layer handles both transparently.\n */\nexport type PersistAdapter<V = unknown> = {\n /** Read the persisted value for `key`. `undefined` means \"not present\". */\n get(key: string): V | undefined | Promise<V | undefined>;\n /** Write `value` to the store under `key`. */\n set(key: string, value: V): void | Promise<void>;\n /** Drop the entry for `key`. Called on `reset()`. */\n remove(key: string): void | Promise<void>;\n};\n\n/**\n * The shape that goes on `AtomOptions.persist`.\n *\n * - `true` → use the built-in localStorage adapter (client-only).\n * - `false` / omitted → no persistence.\n * - Any object matching `PersistAdapter` → use that adapter.\n */\nexport type PersistOption<V = unknown> =\n | boolean\n | PersistAdapter<V>;\n\n/**\n * Built-in adapter backed by `window.localStorage`. JSON-encodes the\n * value on write, decodes on read. No-ops on the server.\n */\nexport const localStorageAdapter: PersistAdapter = {\n get(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const raw = window.localStorage.getItem(key);\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n // Corrupt entry — pretend it doesn't exist.\n return undefined;\n }\n },\n set(key, value) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // QuotaExceededError or private-mode storage block — silently drop.\n }\n },\n remove(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n window.localStorage.removeItem(key);\n },\n};\n\n/**\n * Resolve a `PersistOption` to an actual adapter, or `undefined` if\n * persistence is disabled.\n */\nexport function resolvePersistAdapter<V>(\n option: PersistOption<V> | undefined,\n): PersistAdapter<V> | undefined {\n if (!option) return undefined;\n if (option === true) return localStorageAdapter as PersistAdapter<V>;\n return option;\n}\n\n/**\n * Wire an atom up to a persistence adapter.\n *\n * On creation, asynchronously reads the stored value and applies it via\n * `silentUpdate` (so subscribers see it on next render but no `update`\n * event fires). On every update afterwards, writes through to the\n * adapter. On `reset`, removes the entry.\n *\n * This is internal — `createAtom` calls it when `options.persist` is\n * truthy. Consumers don't call it directly.\n */\nexport function attachPersist<V, A extends Record<string, any>>(\n atom: Atom<V, A>,\n adapter: PersistAdapter<V>,\n options: AtomOptions<V, any>,\n): void {\n // Bootstrap: read the stored value. We don't block the constructor on\n // an async adapter — the consumer sees the default until the read\n // resolves, then a silentUpdate flips the value in place.\n // Both sync throws and async rejections are caught so a broken\n // adapter never crashes atom creation.\n try {\n const stored = adapter.get(atom.key);\n if (stored instanceof Promise) {\n stored\n .then(value => {\n if (value !== undefined) atom.silentUpdate(value);\n })\n .catch(() => {\n /* keep default */\n });\n } else if (stored !== undefined) {\n atom.silentUpdate(stored);\n }\n } catch {\n /* sync throw on read — keep default */\n }\n\n // Write-through on every update. Using onChange instead of replacing\n // beforeUpdate so we don't fight the user's own beforeUpdate hook.\n // Sync throws are swallowed so a transient storage error (quota,\n // private-mode block, etc.) doesn't break the consumer's update flow;\n // async rejections are caught the same way.\n const updateSub = atom.onChange(newValue => {\n try {\n const result = adapter.set(atom.key, newValue);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* adapter blew up — keep going */\n }\n });\n\n // Drop the entry on reset so the next session starts fresh.\n const resetSub = atom.onReset(() => {\n try {\n const result = adapter.remove(atom.key);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* same — non-fatal */\n }\n });\n\n // Clean up subscriptions when the atom dies.\n atom.onDestroy(() => {\n updateSub.unsubscribe();\n resetSub.unsubscribe();\n });\n\n // Suppress unused-options lint when not consumed by future logic.\n void options;\n}\n","/* eslint-disable no-multi-assign */\n/* eslint-disable guard-for-in */\n/* eslint-disable @typescript-eslint/no-shadow */\n/* eslint-disable prefer-template */\nimport events, { EventSubscription } from \"@mongez/events\";\nimport { clone, get } from \"@mongez/reinforcements\";\nimport { attachPersist, resolvePersistAdapter } from \"./persist\";\nimport {\n Atom,\n AtomActions,\n AtomOptions,\n AtomPartialChangeCallback,\n AtomValue,\n} from \"./types\";\n\nexport const atoms: Record<string, Atom<any>> = {};\n\nlet cloneCounter = 0;\n\n/**\n * Get atom by name\n */\nexport function getAtom<T>(name: string): Atom<T> | undefined {\n return atoms[name];\n}\n\n/**\n * Options that control how an atom is constructed.\n * Internal-only; used by store-scoped clones to opt out of the global registry.\n */\nexport type CreateAtomOptions = {\n /**\n * When false, the new atom will NOT be inserted into the module-level\n * `atoms` registry. Used by `AtomStore` to create per-store clones that\n * stay isolated from the global lookup table.\n *\n * Defaults to true.\n */\n register?: boolean;\n};\n\n/**\n * Create a new atom\n */\nexport function createAtom<\n Value = any,\n Actions extends AtomActions<Value> = AtomActions<Value>\n>(\n data: AtomOptions<AtomValue<Value>, Actions>,\n options: CreateAtomOptions = {}\n): Atom<Value, Actions> {\n let defaultValue = data.default;\n let atomValue = data.default;\n\n let atomValueIsObject = false;\n\n if (defaultValue && typeof defaultValue === \"object\") {\n atomValue = defaultValue = clone(defaultValue);\n atomValueIsObject = true;\n }\n\n const atomType = Array.isArray(defaultValue) ? \"array\" : typeof defaultValue;\n\n const atomEvent = `atoms.${data.key}`;\n\n const event = (type: string): string => `${atomEvent}.${type}`;\n\n const watchers: any = {};\n\n const atomKey = data.key;\n\n const atom: Atom<Value, Actions> = {\n default: defaultValue,\n currentValue: atomValue,\n key: atomKey,\n get type() {\n return atomType;\n },\n watch<T extends keyof Value>(\n key: T,\n callback: AtomPartialChangeCallback\n ): EventSubscription {\n if (!watchers[key]) {\n watchers[key] = [];\n }\n\n watchers[key].push(callback);\n\n return {\n unsubscribe: () => {\n watchers[key] = watchers[key].filter(\n (cb: AtomPartialChangeCallback) => cb !== callback\n );\n },\n } as EventSubscription;\n },\n get defaultValue(): Value {\n return this.default;\n },\n get value(): Value {\n return this.currentValue;\n },\n change<T extends keyof Value>(key: T, newValue: any) {\n this.update({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n silentChange<T extends keyof Value>(key: T, newValue: any) {\n this.silentUpdate({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n merge(newValue: Partial<Value>) {\n this.update({\n ...this.currentValue,\n ...newValue,\n });\n },\n update(newValue: (oldValue: Value, atom: Atom<Value, Actions>) => Value) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = newValue(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n events.trigger(event(\"update\"), this.currentValue, oldValue, this);\n if (atomValueIsObject) {\n for (const key in watchers) {\n const keyOldValue = get(oldValue, key);\n const keyNewValue = get(updatedValue, key);\n\n if (keyOldValue !== keyNewValue) {\n watchers[key].forEach(\n (callback: AtomPartialChangeCallback) =>\n callback(keyNewValue, keyOldValue, this)\n );\n }\n }\n }\n },\n silentUpdate(\n newValue: ((oldValue: Value, atom: Atom<Value>) => Value) | Value\n ) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = (newValue as any)(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n },\n onChange(\n callback: (newValue: Value, oldValue: Value, atom: Atom<Value>) => void\n ): EventSubscription {\n return events.subscribe(event(\"update\"), callback);\n },\n onReset(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(event(\"reset\"), callback);\n },\n get<T extends keyof Value>(key: T, defaultValue?: any): Value[T] {\n if (data.get) {\n return data.get(\n key as string,\n defaultValue,\n this.currentValue\n ) as Value[T];\n }\n\n return get(this.currentValue, key as string, defaultValue);\n },\n destroy() {\n events.trigger(event(\"delete\"), this);\n\n events.unsubscribeNamespace(atomEvent);\n delete atoms[this.key];\n },\n onDestroy(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(`atoms.${this.key}.delete`, callback);\n },\n reset() {\n this.update(clone(this.defaultValue));\n events.trigger(event(\"reset\"), this);\n },\n /**\n * Reset the value without triggering the update event\n * But this will trigger the reset event\n */\n silentReset() {\n this.currentValue = clone(this.defaultValue);\n events.trigger(event(\"reset\"), this);\n },\n clone(cloneOptions?: CreateAtomOptions) {\n return createAtom(\n {\n key: this.key + \".clone.\" + (++cloneCounter),\n default: clone(this.currentValue),\n beforeUpdate: data.beforeUpdate,\n get: data.get,\n onUpdate: data.onUpdate,\n actions: data.actions,\n },\n { register: cloneOptions?.register ?? true }\n );\n },\n } as any;\n\n // Install actions on the atom instance.\n //\n // Three kinds of entries can appear in `actions`:\n // 1. Plain functions — bound to the atom so `this` refers to it.\n // 2. Property getters (e.g. `atomCollection`'s `length`) — forwarded\n // as getters bound to the atom; calling `.bind(...)` on them would\n // blow up because the getter is invoked the moment we touch it.\n // 3. Anything else — assigned by value as a fallback.\n if (data.actions) {\n const actions = data.actions as Record<string, unknown>;\n for (const actionKey of Object.keys(actions)) {\n const descriptor = Object.getOwnPropertyDescriptor(actions, actionKey);\n if (descriptor?.get) {\n Object.defineProperty(atom, actionKey, {\n get: descriptor.get.bind(atom),\n set: descriptor.set ? descriptor.set.bind(atom) : undefined,\n enumerable: true,\n configurable: true,\n });\n continue;\n }\n const value = actions[actionKey];\n if (typeof value === \"function\") {\n (atom as any)[actionKey] = (value as Function).bind(atom);\n } else {\n (atom as any)[actionKey] = value;\n }\n }\n }\n\n if (data.onUpdate) {\n events.subscribe(event(\"update\"), data.onUpdate.bind(atom));\n }\n\n if (options.register !== false) {\n atoms[atomKey] = atom;\n }\n\n // Persistence wiring. Resolve the adapter (boolean → built-in\n // localStorage, object → as-is, falsy → skip). The adapter handles\n // the initial read and the write-through; we just hand it the atom.\n const adapter = resolvePersistAdapter(data.persist);\n if (adapter) {\n attachPersist(atom, adapter, data);\n }\n\n return atom;\n}\n\n/**\n * Get all atoms list\n */\nexport function atomsList(): Atom<any>[] {\n return Object.values(atoms);\n}\n\n/**\n * Return atoms in object format\n */\nexport function atomsObject(): Record<string, Atom<any>> {\n return atoms;\n}\n","import type { Atom } from \"./types\";\n\n/**\n * A store is an isolated registry of atom instances.\n *\n * Each store creates and holds its own clones of atom templates so that\n * concurrent consumers (e.g. server-rendered requests) do not share state.\n *\n * Stores are looked up via React context by `<AtomStoreProvider>` in\n * `@mongez/react-atom`, but the class itself is framework-agnostic and can\n * be used directly outside React.\n */\nexport class AtomStore {\n /**\n * Scoped atom clones, keyed by the ORIGINAL atom's key (not the clone key).\n */\n private store = new Map<string, Atom<any>>();\n\n /**\n * Values applied to atoms the moment they enter the store. Useful for\n * SSR hydration when atoms register lazily.\n */\n private pendingValues = new Map<string, unknown>();\n\n /**\n * Get or lazily create a store-scoped clone of the given atom template.\n * The clone shares the template's options (actions, beforeUpdate, get,\n * onUpdate) but owns its own state and event topic.\n */\n use<V, A extends Record<string, any> = {}>(template: Atom<V, A>): Atom<V, A> {\n const existing = this.store.get(template.key);\n if (existing) return existing as Atom<V, A>;\n\n const scoped = template.clone({ register: false }) as Atom<V, A>;\n\n if (this.pendingValues.has(template.key)) {\n scoped.silentUpdate(this.pendingValues.get(template.key) as V);\n this.pendingValues.delete(template.key);\n }\n\n this.store.set(template.key, scoped);\n return scoped;\n }\n\n /**\n * Look up a scoped atom by its original key. Returns undefined when the\n * atom has not been used in this store yet.\n */\n get<V = any>(key: string): Atom<V> | undefined {\n return this.store.get(key) as Atom<V> | undefined;\n }\n\n /**\n * True when the given key has a scoped atom in this store.\n */\n has(key: string): boolean {\n return this.store.has(key);\n }\n\n /**\n * All scoped atoms currently in this store.\n */\n list(): Atom<any>[] {\n return Array.from(this.store.values());\n }\n\n /**\n * Apply initial values to atoms in the store. Atoms not yet registered\n * have their values queued until first `use(template)` call.\n */\n hydrate(snapshot: Record<string, unknown>): void {\n for (const key in snapshot) {\n const value = snapshot[key];\n const atom = this.store.get(key);\n if (atom) {\n atom.silentUpdate(value);\n } else {\n this.pendingValues.set(key, value);\n }\n }\n }\n\n /**\n * Serialize the current values of every scoped atom as a plain object.\n * Intended for SSR payloads that the client will pass back via\n * `<AtomStoreProvider initialValues={...}>`.\n */\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, atom] of this.store.entries()) {\n result[key] = atom.value;\n }\n return result;\n }\n\n /**\n * Destroy every scoped atom and clear the store. Call this at the end of\n * a request lifecycle to release event-bus subscriptions and let the\n * scoped atoms be garbage collected.\n */\n destroy(): void {\n for (const atom of this.store.values()) {\n atom.destroy();\n }\n this.store.clear();\n this.pendingValues.clear();\n }\n}\n\n/**\n * Convenience factory; equivalent to `new AtomStore()`.\n */\nexport function createAtomStore(): AtomStore {\n return new AtomStore();\n}\n","/**\n * @fileoverview Derived atoms.\n *\n * A derived atom holds a value computed from one or more other atoms.\n * Dependencies are auto-tracked: whichever atoms the compute function\n * reads via the `get` argument become dependencies. When any of those\n * change, the derived value recomputes and notifies its subscribers.\n *\n * Conceptually similar to Jotai's `atom(get => ...)` and MobX's\n * `computed`. Returns a normal `Atom<T>`, so every consumer pattern in\n * `@mongez/react-atom` (useValue, useState, watch, onChange, …) works.\n *\n * @example\n * ```ts\n * const first = createAtom({ key: \"first\", default: \"Ada\" });\n * const last = createAtom({ key: \"last\", default: \"Lovelace\" });\n *\n * const fullName = derive(\"fullName\", get => `${get(first)} ${get(last)}`);\n *\n * fullName.value; // \"Ada Lovelace\"\n * first.update(\"Grace\");\n * fullName.value; // \"Grace Lovelace\"\n * ```\n */\nimport { type EventSubscription } from \"@mongez/events\";\nimport { createAtom } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * The reader passed to a derive compute function. Calling `get(atom)`\n * registers that atom as a dependency and returns its current value.\n */\nexport type DeriveGetter = <V>(atom: Atom<V, any>) => V;\n\nexport type DeriveOptions = {\n /**\n * Skip the global `atoms` registry. Used by `AtomStore` clones — most\n * consumers should leave this alone.\n * @default true\n */\n register?: boolean;\n};\n\n/**\n * Create a derived atom.\n *\n * The compute function runs once eagerly on creation to seed the initial\n * value and to discover dependencies. After that, it re-runs every time\n * any tracked dependency changes.\n *\n * Conditional reads work: an `if` branch inside the compute function\n * that reads a different atom on a later run picks up the new dep and\n * drops the old one. This handles the \"dynamic dependency graph\" case\n * (e.g. `if (get(currentRoute) === \"users\") return get(usersAtom)`).\n *\n * Calling `update`, `silentUpdate`, `change`, `merge` directly on the\n * returned atom works but is discouraged — the next dependency change\n * will overwrite anything you wrote. Use a regular atom if you need\n * writable state.\n */\nexport function derive<T>(\n key: string,\n compute: (get: DeriveGetter) => T,\n options: DeriveOptions = {},\n): Atom<T> {\n /**\n * Active subscriptions to dependencies, keyed by the source atom.\n * Replaced wholesale on each recompute so dynamic dependency graphs\n * don't accumulate stale subscriptions.\n */\n let depSubs = new Map<Atom<any>, EventSubscription>();\n\n /**\n * A scratch set used during a recompute to mark which atoms were\n * touched on this run. After compute finishes we diff against\n * `depSubs`, drop stale ones, and add new ones.\n */\n let trackedThisRun: Set<Atom<any>> | undefined;\n\n const trackingGet: DeriveGetter = atom => {\n if (trackedThisRun) trackedThisRun.add(atom);\n return atom.value;\n };\n\n /**\n * Recompute the derived value and reconcile the dependency set.\n * Updates the derived atom via the normal `update` flow so all\n * downstream subscribers see the change.\n *\n * Errors thrown inside `compute` are caught and re-thrown\n * asynchronously: we don't want a single broken derivation to take\n * down the atom-bus subscriber. The atom's previous value is kept.\n */\n const recompute = () => {\n trackedThisRun = new Set();\n let next: T;\n try {\n next = compute(trackingGet);\n } catch (err) {\n trackedThisRun = undefined;\n // Surface the error without breaking the source-atom's update cycle.\n queueMicrotask(() => {\n throw err;\n });\n return;\n }\n\n // Reconcile: subscribe to newly-seen deps, drop deps no longer read.\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n for (const dep of seen) {\n if (!depSubs.has(dep)) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n }\n for (const [dep, sub] of depSubs) {\n if (!seen.has(dep)) {\n sub.unsubscribe();\n depSubs.delete(dep);\n }\n }\n\n // Push the new value through the standard update path. If the value\n // is structurally unchanged the atom's update() will short-circuit\n // for primitives; for objects we always send a new reference\n // because `compute` builds one each call.\n derivedAtom.update(next);\n };\n\n // Bootstrap: compute the initial value and the initial dep set.\n trackedThisRun = new Set();\n const initialValue = compute(trackingGet);\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n const derivedAtom = createAtom<T>(\n {\n key,\n default: initialValue,\n },\n { register: options.register !== false },\n );\n\n // Wire up dependency subscriptions now that the derived atom exists.\n for (const dep of seen) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n\n // Tear down dependency subs when the atom is destroyed so they don't\n // outlive the consumer and leak memory.\n derivedAtom.onDestroy(() => {\n for (const sub of depSubs.values()) sub.unsubscribe();\n depSubs.clear();\n });\n\n return derivedAtom;\n}\n","/**\n * @fileoverview Redux DevTools bridge for `@mongez/atom`.\n *\n * Opt-in, browser-only, zero-cost when not enabled (tree-shaken if you\n * never import `enableAtomDevtools`).\n *\n * Pipes every atom update into the Redux DevTools extension so you get:\n *\n * - A live list of every registered atom and its current value.\n * - A timeline of updates with diffs.\n * - Time-travel: jumping back in the timeline restores the matching\n * state via `silentUpdate` on every atom.\n *\n * @example\n * ```ts\n * // app entry, dev only\n * if (process.env.NODE_ENV !== \"production\") {\n * enableAtomDevtools({ name: \"MyApp\" });\n * }\n * ```\n */\nimport events from \"@mongez/events\";\nimport { atoms } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * Subset of the Redux DevTools instance API we use. Typed locally so we\n * don't need to take a dep on `@redux-devtools/extension`.\n */\ntype DevtoolsInstance = {\n init(state: unknown): void;\n send(action: { type: string; payload?: unknown }, state: unknown): void;\n subscribe(\n listener: (message: {\n type: string;\n payload?: { type?: string };\n state?: string;\n }) => void,\n ): () => void;\n disconnect?(): void;\n};\n\ntype DevtoolsExtension = {\n connect(options?: {\n name?: string;\n features?: Record<string, unknown>;\n }): DevtoolsInstance;\n};\n\nexport type EnableDevtoolsOptions = {\n /** Label shown in the extension UI. */\n name?: string;\n /**\n * Skip atoms whose key matches any of these patterns. Useful for\n * silencing high-frequency atoms (mouse position, scroll, etc.) that\n * would otherwise spam the timeline.\n */\n ignore?: Array<RegExp | string>;\n /**\n * How often (ms) to look for newly-registered atoms. Apps that\n * register every atom at startup never need this; the default of\n * 1000ms is fine for hot-reload and code-splitting cases.\n * @default 1000\n */\n scanInterval?: number;\n};\n\n/**\n * Connect every registered atom to the Redux DevTools extension.\n *\n * Returns a teardown function that disconnects and stops the registry\n * scan. Safe to call when the extension isn't installed — it returns\n * a no-op teardown without doing any work.\n */\nexport function enableAtomDevtools(\n options: EnableDevtoolsOptions = {},\n): () => void {\n const win =\n typeof window !== \"undefined\" ? (window as unknown as Window & {\n __REDUX_DEVTOOLS_EXTENSION__?: DevtoolsExtension;\n }) : undefined;\n\n const ext = win?.__REDUX_DEVTOOLS_EXTENSION__;\n if (!ext) return () => {};\n\n const devtools = ext.connect({\n name: options.name ?? \"@mongez/atom\",\n });\n\n const ignored = (key: string): boolean =>\n !!options.ignore?.some(pat =>\n typeof pat === \"string\" ? pat === key : pat.test(key),\n );\n\n const snapshot = (): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [k, a] of Object.entries(atoms)) {\n if (!ignored(k)) out[k] = a.value;\n }\n return out;\n };\n\n devtools.init(snapshot());\n\n // Per-atom subscriptions we own (so we can tear them down).\n const subscribed = new Map<string, () => void>();\n\n const subscribeAtom = (atom: Atom<any>) => {\n if (ignored(atom.key)) return;\n if (subscribed.has(atom.key)) return;\n const sub = atom.onChange(newValue => {\n devtools.send(\n { type: `${atom.key}/update`, payload: newValue },\n snapshot(),\n );\n });\n const onResetSub = atom.onReset(() => {\n devtools.send({ type: `${atom.key}/reset` }, snapshot());\n });\n const onDestroySub = atom.onDestroy(() => {\n devtools.send({ type: `${atom.key}/destroy` }, snapshot());\n // Drop our own subscription bookkeeping.\n subscribed.delete(atom.key);\n });\n subscribed.set(atom.key, () => {\n sub.unsubscribe();\n onResetSub.unsubscribe();\n onDestroySub.unsubscribe();\n });\n };\n\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n\n // Pick up atoms registered AFTER enableAtomDevtools fires. Apps that\n // import all their atoms at boot will never trigger this; lazy-loaded\n // routes that register atoms on demand will. Poll-based for simplicity.\n const interval = options.scanInterval ?? 1000;\n const scanTimer: ReturnType<typeof setInterval> = setInterval(() => {\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n }, interval);\n\n // Time-travel: when the user jumps to a snapshot in the extension,\n // restore every atom's value via silentUpdate so consumers see the\n // restored state on the next read.\n const unsubDevtools = devtools.subscribe(message => {\n if (message.type !== \"DISPATCH\") return;\n const payloadType = message.payload?.type;\n if (\n payloadType !== \"JUMP_TO_STATE\" &&\n payloadType !== \"JUMP_TO_ACTION\"\n ) {\n return;\n }\n if (!message.state) return;\n let restored: Record<string, unknown>;\n try {\n restored = JSON.parse(message.state);\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(restored)) {\n const atom = atoms[key];\n if (!atom) continue;\n atom.silentUpdate(value);\n // Synthesise an update event so React subscribers re-render.\n events.trigger(`atoms.${key}.update`, value, atom.currentValue, atom);\n }\n });\n\n return () => {\n clearInterval(scanTimer);\n for (const unsub of subscribed.values()) unsub();\n subscribed.clear();\n unsubDevtools();\n devtools.disconnect?.();\n };\n}\n","import { createAtom } from './atom';\nimport type { Atom, AtomActions, AtomOptions } from './types';\n\nexport type IndexOrCallback<Value> =\n | number\n | ((value: Value, index: number, list: Value[]) => boolean);\n\nexport interface AtomCollectionActions<Value> extends AtomActions<Value[]> {\n /**\n * Add items to the end of the array\n */\n push(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Add items to the beginning of the array\n */\n unshift(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Remove the last item from the array\n */\n pop(this: Atom<Value[]>): void;\n /**\n * Remove the first item from the array\n */\n shift(this: Atom<Value[]>): void;\n /**\n * Remove item from array either by index or callback\n */\n remove(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): void;\n /**\n * Remove item from array by value\n */\n removeItem(this: Atom<Value[]>, item: Value): void;\n /**\n * Remove all occurrences of an item from the array\n */\n removeAll(this: Atom<Value[]>, item: Value): void;\n /**\n * Get item from array either by index or callback\n */\n get(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): Value | undefined;\n /**\n * Find index of item in array by callback\n */\n index(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => boolean,\n ): number;\n /**\n * Map array items\n * This will update the array with the new mapped array and trigger update\n */\n map(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => Value,\n ): Value[];\n /**\n * Loop through array items\n */\n forEach(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => void,\n ): void;\n /**\n * Replace item in array by index\n */\n replace(this: Atom<Value[]>, index: number, item: Value): void;\n /**\n * Get array length\n */\n length: number; // As a property\n}\n\nexport type CollectionOptions<Value> = Omit<\n AtomOptions<Value[], AtomCollectionActions<Value>>,\n 'default'\n> & {\n default?: Value[];\n};\n\n/**\n * Create an atom collection\n */\nexport function atomCollection<Value = any>(\n options: CollectionOptions<Value>,\n): Atom<Value[], AtomCollectionActions<Value>> {\n return createAtom<Value[], AtomCollectionActions<Value>>({\n key: options.key,\n default: options.default ?? [],\n actions: {\n ...options.actions,\n push(...items: Value[]) {\n this.update([...this.currentValue, ...items]);\n },\n pop() {\n this.update(this.currentValue.slice(0, -1));\n },\n shift() {\n this.update(this.currentValue.slice(1));\n },\n unshift(...items: Value[]) {\n this.update([...items, ...this.currentValue]);\n },\n remove(indexOrCallback: IndexOrCallback<Value>) {\n const index =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n if (index === -1) return;\n\n this.update(this.value.filter((_, i) => i !== index));\n },\n removeItem(item: Value) {\n const index = this.value.indexOf(item);\n\n if (index === -1) return;\n\n // using splice\n this.value.splice(index, 1);\n\n this.update([...this.value]);\n },\n removeAll(item: Value) {\n this.update(this.value.filter((value) => value !== item));\n },\n get(indexOrCallback: IndexOrCallback<Value>) {\n const index: number =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n return this.value[index];\n },\n index(callback: (item: Value, index: number, array: Value[]) => boolean) {\n return this.value.findIndex(callback);\n },\n map(callback: (item: Value, index: number, array: Value[]) => Value) {\n const value = this.value.map(callback);\n\n this.update(value);\n\n return value;\n },\n forEach(callback: (item: Value, index: number, array: Value[]) => void) {\n this.value.forEach(callback);\n },\n get length() {\n return this.value?.length;\n },\n replace(index: number, item: Value) {\n this.update(\n this.value.map((value, i) => {\n if (i === index) {\n return item;\n }\n\n return value;\n }),\n );\n },\n },\n });\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"index.cjs","names":["events"],"sources":["../../../../../../atom/src/persist.ts","../../../../../../atom/src/atom.ts","../../../../../../atom/src/atom-store.ts","../../../../../../atom/src/derive.ts","../../../../../../atom/src/devtools.ts","../../../../../../atom/src/atom-collection.ts"],"sourcesContent":["/**\n * @fileoverview Persistence adapters for atoms.\n *\n * Plug in any store-shaped object (cache, localStorage wrapper, cookie\n * helper, IndexedDB layer) and atoms will:\n *\n * 1. Load their initial value from the adapter at creation (sync or async).\n * 2. Write every subsequent update through to the adapter.\n * 3. Remove the entry on `reset()`.\n *\n * The default adapter is a thin localStorage wrapper for the client; it\n * silently no-ops on the server (no `window`). For SSR-safe per-request\n * persistence, supply your own cookie-aware adapter.\n */\nimport type { Atom, AtomOptions } from \"./types\";\n\n/**\n * Shape of an external store. Methods may be sync or async; the\n * persistence layer handles both transparently.\n */\nexport type PersistAdapter<V = unknown> = {\n /** Read the persisted value for `key`. `undefined` means \"not present\". */\n get(key: string): V | undefined | Promise<V | undefined>;\n /** Write `value` to the store under `key`. */\n set(key: string, value: V): void | Promise<void>;\n /** Drop the entry for `key`. Called on `reset()`. */\n remove(key: string): void | Promise<void>;\n};\n\n/**\n * The shape that goes on `AtomOptions.persist`.\n *\n * - `true` → use the built-in localStorage adapter (client-only).\n * - `false` / omitted → no persistence.\n * - Any object matching `PersistAdapter` → use that adapter.\n */\nexport type PersistOption<V = unknown> =\n | boolean\n | PersistAdapter<V>;\n\n/**\n * Built-in adapter backed by `window.localStorage`. JSON-encodes the\n * value on write, decodes on read. No-ops on the server.\n */\nexport const localStorageAdapter: PersistAdapter = {\n get(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const raw = window.localStorage.getItem(key);\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n // Corrupt entry — pretend it doesn't exist.\n return undefined;\n }\n },\n set(key, value) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // QuotaExceededError or private-mode storage block — silently drop.\n }\n },\n remove(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n window.localStorage.removeItem(key);\n },\n};\n\n/**\n * Resolve a `PersistOption` to an actual adapter, or `undefined` if\n * persistence is disabled.\n */\nexport function resolvePersistAdapter<V>(\n option: PersistOption<V> | undefined,\n): PersistAdapter<V> | undefined {\n if (!option) return undefined;\n if (option === true) return localStorageAdapter as PersistAdapter<V>;\n return option;\n}\n\n/**\n * Wire an atom up to a persistence adapter.\n *\n * On creation, asynchronously reads the stored value and applies it via\n * `silentUpdate` (so subscribers see it on next render but no `update`\n * event fires). On every update afterwards, writes through to the\n * adapter. On `reset`, removes the entry.\n *\n * This is internal — `createAtom` calls it when `options.persist` is\n * truthy. Consumers don't call it directly.\n */\nexport function attachPersist<V, A extends Record<string, any>>(\n atom: Atom<V, A>,\n adapter: PersistAdapter<V>,\n options: AtomOptions<V, any>,\n): void {\n // Bootstrap: read the stored value. We don't block the constructor on\n // an async adapter — the consumer sees the default until the read\n // resolves, then a silentUpdate flips the value in place.\n // Both sync throws and async rejections are caught so a broken\n // adapter never crashes atom creation.\n try {\n const stored = adapter.get(atom.key);\n if (stored instanceof Promise) {\n stored\n .then(value => {\n if (value !== undefined) atom.silentUpdate(value);\n })\n .catch(() => {\n /* keep default */\n });\n } else if (stored !== undefined) {\n atom.silentUpdate(stored);\n }\n } catch {\n /* sync throw on read — keep default */\n }\n\n // Write-through on every update. Using onChange instead of replacing\n // beforeUpdate so we don't fight the user's own beforeUpdate hook.\n // Sync throws are swallowed so a transient storage error (quota,\n // private-mode block, etc.) doesn't break the consumer's update flow;\n // async rejections are caught the same way.\n const updateSub = atom.onChange(newValue => {\n try {\n const result = adapter.set(atom.key, newValue);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* adapter blew up — keep going */\n }\n });\n\n // Drop the entry on reset so the next session starts fresh.\n const resetSub = atom.onReset(() => {\n try {\n const result = adapter.remove(atom.key);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* same — non-fatal */\n }\n });\n\n // Clean up subscriptions when the atom dies.\n atom.onDestroy(() => {\n updateSub.unsubscribe();\n resetSub.unsubscribe();\n });\n\n // Suppress unused-options lint when not consumed by future logic.\n void options;\n}\n","/* eslint-disable no-multi-assign */\n/* eslint-disable guard-for-in */\n/* eslint-disable @typescript-eslint/no-shadow */\n/* eslint-disable prefer-template */\nimport events, { EventSubscription } from \"@mongez/events\";\nimport { clone, get } from \"@mongez/reinforcements\";\nimport { attachPersist, resolvePersistAdapter } from \"./persist\";\nimport {\n Atom,\n AtomActions,\n AtomOptions,\n AtomPartialChangeCallback,\n AtomValue,\n} from \"./types\";\n\nexport const atoms: Record<string, Atom<any>> = {};\n\nlet cloneCounter = 0;\n\n/**\n * Get atom by name\n */\nexport function getAtom<T>(name: string): Atom<T> | undefined {\n return atoms[name];\n}\n\n/**\n * Options that control how an atom is constructed.\n * Internal-only; used by store-scoped clones to opt out of the global registry.\n */\nexport type CreateAtomOptions = {\n /**\n * When false, the new atom will NOT be inserted into the module-level\n * `atoms` registry. Used by `AtomStore` to create per-store clones that\n * stay isolated from the global lookup table.\n *\n * Defaults to true.\n */\n register?: boolean;\n};\n\n/**\n * Create a new atom\n */\nexport function createAtom<\n Value = any,\n Actions extends AtomActions<Value> = AtomActions<Value>\n>(\n data: AtomOptions<AtomValue<Value>, Actions>,\n options: CreateAtomOptions = {}\n): Atom<Value, Actions> {\n let defaultValue = data.default;\n let atomValue = data.default;\n\n let atomValueIsObject = false;\n\n if (defaultValue && typeof defaultValue === \"object\") {\n atomValue = defaultValue = clone(defaultValue);\n atomValueIsObject = true;\n }\n\n const atomType = Array.isArray(defaultValue) ? \"array\" : typeof defaultValue;\n\n const atomEvent = `atoms.${data.key}`;\n\n const event = (type: string): string => `${atomEvent}.${type}`;\n\n const watchers: any = {};\n\n const atomKey = data.key;\n\n const atom: Atom<Value, Actions> = {\n default: defaultValue,\n currentValue: atomValue,\n key: atomKey,\n get type() {\n return atomType;\n },\n watch<T extends keyof Value>(\n key: T,\n callback: AtomPartialChangeCallback\n ): EventSubscription {\n if (!watchers[key]) {\n watchers[key] = [];\n }\n\n watchers[key].push(callback);\n\n return {\n unsubscribe: () => {\n watchers[key] = watchers[key].filter(\n (cb: AtomPartialChangeCallback) => cb !== callback\n );\n },\n } as EventSubscription;\n },\n get defaultValue(): Value {\n return this.default;\n },\n get value(): Value {\n return this.currentValue;\n },\n change<T extends keyof Value>(key: T, newValue: any) {\n this.update({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n silentChange<T extends keyof Value>(key: T, newValue: any) {\n this.silentUpdate({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n merge(newValue: Partial<Value>) {\n this.update({\n ...this.currentValue,\n ...newValue,\n });\n },\n update(newValue: (oldValue: Value, atom: Atom<Value, Actions>) => Value) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = newValue(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n events.trigger(event(\"update\"), this.currentValue, oldValue, this);\n if (atomValueIsObject) {\n for (const key in watchers) {\n const keyOldValue = get(oldValue, key);\n const keyNewValue = get(updatedValue, key);\n\n if (keyOldValue !== keyNewValue) {\n watchers[key].forEach(\n (callback: AtomPartialChangeCallback) =>\n callback(keyNewValue, keyOldValue, this)\n );\n }\n }\n }\n },\n silentUpdate(\n newValue: ((oldValue: Value, atom: Atom<Value>) => Value) | Value\n ) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = (newValue as any)(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n },\n onChange(\n callback: (newValue: Value, oldValue: Value, atom: Atom<Value>) => void\n ): EventSubscription {\n return events.subscribe(event(\"update\"), callback);\n },\n onReset(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(event(\"reset\"), callback);\n },\n get<T extends keyof Value>(key: T, defaultValue?: any): Value[T] {\n if (data.get) {\n return data.get(\n key as string,\n defaultValue,\n this.currentValue\n ) as Value[T];\n }\n\n return get(this.currentValue, key as string, defaultValue);\n },\n destroy() {\n events.trigger(event(\"delete\"), this);\n\n events.unsubscribeNamespace(atomEvent);\n delete atoms[this.key];\n },\n onDestroy(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(`atoms.${this.key}.delete`, callback);\n },\n reset() {\n this.update(clone(this.defaultValue));\n events.trigger(event(\"reset\"), this);\n },\n /**\n * Reset the value without triggering the update event\n * But this will trigger the reset event\n */\n silentReset() {\n this.currentValue = clone(this.defaultValue);\n events.trigger(event(\"reset\"), this);\n },\n clone(cloneOptions?: CreateAtomOptions) {\n return createAtom(\n {\n key: this.key + \".clone.\" + (++cloneCounter),\n default: clone(this.currentValue),\n beforeUpdate: data.beforeUpdate,\n get: data.get,\n onUpdate: data.onUpdate,\n actions: data.actions,\n },\n { register: cloneOptions?.register ?? true }\n );\n },\n } as any;\n\n // Install actions on the atom instance.\n //\n // Three kinds of entries can appear in `actions`:\n // 1. Plain functions — bound to the atom so `this` refers to it.\n // 2. Property getters (e.g. `atomCollection`'s `length`) — forwarded\n // as getters bound to the atom; calling `.bind(...)` on them would\n // blow up because the getter is invoked the moment we touch it.\n // 3. Anything else — assigned by value as a fallback.\n if (data.actions) {\n const actions = data.actions as Record<string, unknown>;\n for (const actionKey of Object.keys(actions)) {\n const descriptor = Object.getOwnPropertyDescriptor(actions, actionKey);\n if (descriptor?.get) {\n Object.defineProperty(atom, actionKey, {\n get: descriptor.get.bind(atom),\n set: descriptor.set ? descriptor.set.bind(atom) : undefined,\n enumerable: true,\n configurable: true,\n });\n continue;\n }\n const value = actions[actionKey];\n if (typeof value === \"function\") {\n (atom as any)[actionKey] = (value as Function).bind(atom);\n } else {\n (atom as any)[actionKey] = value;\n }\n }\n }\n\n if (data.onUpdate) {\n events.subscribe(event(\"update\"), data.onUpdate.bind(atom));\n }\n\n if (options.register !== false) {\n atoms[atomKey] = atom;\n }\n\n // Persistence wiring. Resolve the adapter (boolean → built-in\n // localStorage, object → as-is, falsy → skip). The adapter handles\n // the initial read and the write-through; we just hand it the atom.\n const adapter = resolvePersistAdapter(data.persist);\n if (adapter) {\n attachPersist(atom, adapter, data);\n }\n\n return atom;\n}\n\n/**\n * Get all atoms list\n */\nexport function atomsList(): Atom<any>[] {\n return Object.values(atoms);\n}\n\n/**\n * Return atoms in object format\n */\nexport function atomsObject(): Record<string, Atom<any>> {\n return atoms;\n}\n","import type { Atom } from \"./types\";\n\n/**\n * A store is an isolated registry of atom instances.\n *\n * Each store creates and holds its own clones of atom templates so that\n * concurrent consumers (e.g. server-rendered requests) do not share state.\n *\n * Stores are looked up via React context by `<AtomStoreProvider>` in\n * `@mongez/react-atom`, but the class itself is framework-agnostic and can\n * be used directly outside React.\n */\nexport class AtomStore {\n /**\n * Scoped atom clones, keyed by the ORIGINAL atom's key (not the clone key).\n */\n private store = new Map<string, Atom<any>>();\n\n /**\n * Values applied to atoms the moment they enter the store. Useful for\n * SSR hydration when atoms register lazily.\n */\n private pendingValues = new Map<string, unknown>();\n\n /**\n * Get or lazily create a store-scoped clone of the given atom template.\n * The clone shares the template's options (actions, beforeUpdate, get,\n * onUpdate) but owns its own state and event topic.\n */\n use<V, A extends Record<string, any> = {}>(template: Atom<V, A>): Atom<V, A> {\n const existing = this.store.get(template.key);\n if (existing) return existing as Atom<V, A>;\n\n const scoped = template.clone({ register: false }) as Atom<V, A>;\n\n if (this.pendingValues.has(template.key)) {\n scoped.silentUpdate(this.pendingValues.get(template.key) as V);\n this.pendingValues.delete(template.key);\n }\n\n this.store.set(template.key, scoped);\n return scoped;\n }\n\n /**\n * Look up a scoped atom by its original key. Returns undefined when the\n * atom has not been used in this store yet.\n */\n get<V = any>(key: string): Atom<V> | undefined {\n return this.store.get(key) as Atom<V> | undefined;\n }\n\n /**\n * True when the given key has a scoped atom in this store.\n */\n has(key: string): boolean {\n return this.store.has(key);\n }\n\n /**\n * All scoped atoms currently in this store.\n */\n list(): Atom<any>[] {\n return Array.from(this.store.values());\n }\n\n /**\n * Apply initial values to atoms in the store. Atoms not yet registered\n * have their values queued until first `use(template)` call.\n */\n hydrate(snapshot: Record<string, unknown>): void {\n for (const key in snapshot) {\n const value = snapshot[key];\n const atom = this.store.get(key);\n if (atom) {\n atom.silentUpdate(value);\n } else {\n this.pendingValues.set(key, value);\n }\n }\n }\n\n /**\n * Serialize the current values of every scoped atom as a plain object.\n * Intended for SSR payloads that the client will pass back via\n * `<AtomStoreProvider initialValues={...}>`.\n */\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, atom] of this.store.entries()) {\n result[key] = atom.value;\n }\n return result;\n }\n\n /**\n * Destroy every scoped atom and clear the store. Call this at the end of\n * a request lifecycle to release event-bus subscriptions and let the\n * scoped atoms be garbage collected.\n */\n destroy(): void {\n for (const atom of this.store.values()) {\n atom.destroy();\n }\n this.store.clear();\n this.pendingValues.clear();\n }\n}\n\n/**\n * Convenience factory; equivalent to `new AtomStore()`.\n */\nexport function createAtomStore(): AtomStore {\n return new AtomStore();\n}\n","/**\n * @fileoverview Derived atoms.\n *\n * A derived atom holds a value computed from one or more other atoms.\n * Dependencies are auto-tracked: whichever atoms the compute function\n * reads via the `get` argument become dependencies. When any of those\n * change, the derived value recomputes and notifies its subscribers.\n *\n * Conceptually similar to Jotai's `atom(get => ...)` and MobX's\n * `computed`. Returns a normal `Atom<T>`, so every consumer pattern in\n * `@mongez/react-atom` (useValue, useState, watch, onChange, …) works.\n *\n * @example\n * ```ts\n * const first = createAtom({ key: \"first\", default: \"Ada\" });\n * const last = createAtom({ key: \"last\", default: \"Lovelace\" });\n *\n * const fullName = derive(\"fullName\", get => `${get(first)} ${get(last)}`);\n *\n * fullName.value; // \"Ada Lovelace\"\n * first.update(\"Grace\");\n * fullName.value; // \"Grace Lovelace\"\n * ```\n */\nimport { type EventSubscription } from \"@mongez/events\";\nimport { createAtom } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * The reader passed to a derive compute function. Calling `get(atom)`\n * registers that atom as a dependency and returns its current value.\n */\nexport type DeriveGetter = <V>(atom: Atom<V, any>) => V;\n\nexport type DeriveOptions = {\n /**\n * Skip the global `atoms` registry. Used by `AtomStore` clones — most\n * consumers should leave this alone.\n * @default true\n */\n register?: boolean;\n};\n\n/**\n * Create a derived atom.\n *\n * The compute function runs once eagerly on creation to seed the initial\n * value and to discover dependencies. After that, it re-runs every time\n * any tracked dependency changes.\n *\n * Conditional reads work: an `if` branch inside the compute function\n * that reads a different atom on a later run picks up the new dep and\n * drops the old one. This handles the \"dynamic dependency graph\" case\n * (e.g. `if (get(currentRoute) === \"users\") return get(usersAtom)`).\n *\n * Calling `update`, `silentUpdate`, `change`, `merge` directly on the\n * returned atom works but is discouraged — the next dependency change\n * will overwrite anything you wrote. Use a regular atom if you need\n * writable state.\n */\nexport function derive<T>(\n key: string,\n compute: (get: DeriveGetter) => T,\n options: DeriveOptions = {},\n): Atom<T> {\n /**\n * Active subscriptions to dependencies, keyed by the source atom.\n * Replaced wholesale on each recompute so dynamic dependency graphs\n * don't accumulate stale subscriptions.\n */\n let depSubs = new Map<Atom<any>, EventSubscription>();\n\n /**\n * A scratch set used during a recompute to mark which atoms were\n * touched on this run. After compute finishes we diff against\n * `depSubs`, drop stale ones, and add new ones.\n */\n let trackedThisRun: Set<Atom<any>> | undefined;\n\n const trackingGet: DeriveGetter = atom => {\n if (trackedThisRun) trackedThisRun.add(atom);\n return atom.value;\n };\n\n /**\n * Recompute the derived value and reconcile the dependency set.\n * Updates the derived atom via the normal `update` flow so all\n * downstream subscribers see the change.\n *\n * Errors thrown inside `compute` are caught and re-thrown\n * asynchronously: we don't want a single broken derivation to take\n * down the atom-bus subscriber. The atom's previous value is kept.\n */\n const recompute = () => {\n trackedThisRun = new Set();\n let next: T;\n try {\n next = compute(trackingGet);\n } catch (err) {\n trackedThisRun = undefined;\n // Surface the error without breaking the source-atom's update cycle.\n queueMicrotask(() => {\n throw err;\n });\n return;\n }\n\n // Reconcile: subscribe to newly-seen deps, drop deps no longer read.\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n for (const dep of seen) {\n if (!depSubs.has(dep)) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n }\n for (const [dep, sub] of depSubs) {\n if (!seen.has(dep)) {\n sub.unsubscribe();\n depSubs.delete(dep);\n }\n }\n\n // Push the new value through the standard update path. If the value\n // is structurally unchanged the atom's update() will short-circuit\n // for primitives; for objects we always send a new reference\n // because `compute` builds one each call.\n derivedAtom.update(next);\n };\n\n // Bootstrap: compute the initial value and the initial dep set.\n trackedThisRun = new Set();\n const initialValue = compute(trackingGet);\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n const derivedAtom = createAtom<T>(\n {\n key,\n default: initialValue,\n },\n { register: options.register !== false },\n );\n\n // Wire up dependency subscriptions now that the derived atom exists.\n for (const dep of seen) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n\n // Tear down dependency subs when the atom is destroyed so they don't\n // outlive the consumer and leak memory.\n derivedAtom.onDestroy(() => {\n for (const sub of depSubs.values()) sub.unsubscribe();\n depSubs.clear();\n });\n\n return derivedAtom;\n}\n","/**\n * @fileoverview Redux DevTools bridge for `@mongez/atom`.\n *\n * Opt-in, browser-only, zero-cost when not enabled (tree-shaken if you\n * never import `enableAtomDevtools`).\n *\n * Pipes every atom update into the Redux DevTools extension so you get:\n *\n * - A live list of every registered atom and its current value.\n * - A timeline of updates with diffs.\n * - Time-travel: jumping back in the timeline restores the matching\n * state via `silentUpdate` on every atom.\n *\n * @example\n * ```ts\n * // app entry, dev only\n * if (process.env.NODE_ENV !== \"production\") {\n * enableAtomDevtools({ name: \"MyApp\" });\n * }\n * ```\n */\nimport events from \"@mongez/events\";\nimport { atoms } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * Subset of the Redux DevTools instance API we use. Typed locally so we\n * don't need to take a dep on `@redux-devtools/extension`.\n */\ntype DevtoolsInstance = {\n init(state: unknown): void;\n send(action: { type: string; payload?: unknown }, state: unknown): void;\n subscribe(\n listener: (message: {\n type: string;\n payload?: { type?: string };\n state?: string;\n }) => void,\n ): () => void;\n disconnect?(): void;\n};\n\ntype DevtoolsExtension = {\n connect(options?: {\n name?: string;\n features?: Record<string, unknown>;\n }): DevtoolsInstance;\n};\n\nexport type EnableDevtoolsOptions = {\n /** Label shown in the extension UI. */\n name?: string;\n /**\n * Skip atoms whose key matches any of these patterns. Useful for\n * silencing high-frequency atoms (mouse position, scroll, etc.) that\n * would otherwise spam the timeline.\n */\n ignore?: Array<RegExp | string>;\n /**\n * How often (ms) to look for newly-registered atoms. Apps that\n * register every atom at startup never need this; the default of\n * 1000ms is fine for hot-reload and code-splitting cases.\n * @default 1000\n */\n scanInterval?: number;\n};\n\n/**\n * Connect every registered atom to the Redux DevTools extension.\n *\n * Returns a teardown function that disconnects and stops the registry\n * scan. Safe to call when the extension isn't installed — it returns\n * a no-op teardown without doing any work.\n */\nexport function enableAtomDevtools(\n options: EnableDevtoolsOptions = {},\n): () => void {\n const win =\n typeof window !== \"undefined\" ? (window as unknown as Window & {\n __REDUX_DEVTOOLS_EXTENSION__?: DevtoolsExtension;\n }) : undefined;\n\n const ext = win?.__REDUX_DEVTOOLS_EXTENSION__;\n if (!ext) return () => {};\n\n const devtools = ext.connect({\n name: options.name ?? \"@mongez/atom\",\n });\n\n const ignored = (key: string): boolean =>\n !!options.ignore?.some(pat =>\n typeof pat === \"string\" ? pat === key : pat.test(key),\n );\n\n const snapshot = (): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [k, a] of Object.entries(atoms)) {\n if (!ignored(k)) out[k] = a.value;\n }\n return out;\n };\n\n devtools.init(snapshot());\n\n // Per-atom subscriptions we own (so we can tear them down).\n const subscribed = new Map<string, () => void>();\n\n const subscribeAtom = (atom: Atom<any>) => {\n if (ignored(atom.key)) return;\n if (subscribed.has(atom.key)) return;\n const sub = atom.onChange(newValue => {\n devtools.send(\n { type: `${atom.key}/update`, payload: newValue },\n snapshot(),\n );\n });\n const onResetSub = atom.onReset(() => {\n devtools.send({ type: `${atom.key}/reset` }, snapshot());\n });\n const onDestroySub = atom.onDestroy(() => {\n devtools.send({ type: `${atom.key}/destroy` }, snapshot());\n // Drop our own subscription bookkeeping.\n subscribed.delete(atom.key);\n });\n subscribed.set(atom.key, () => {\n sub.unsubscribe();\n onResetSub.unsubscribe();\n onDestroySub.unsubscribe();\n });\n };\n\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n\n // Pick up atoms registered AFTER enableAtomDevtools fires. Apps that\n // import all their atoms at boot will never trigger this; lazy-loaded\n // routes that register atoms on demand will. Poll-based for simplicity.\n const interval = options.scanInterval ?? 1000;\n const scanTimer: ReturnType<typeof setInterval> = setInterval(() => {\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n }, interval);\n\n // Time-travel: when the user jumps to a snapshot in the extension,\n // restore every atom's value via silentUpdate so consumers see the\n // restored state on the next read.\n const unsubDevtools = devtools.subscribe(message => {\n if (message.type !== \"DISPATCH\") return;\n const payloadType = message.payload?.type;\n if (\n payloadType !== \"JUMP_TO_STATE\" &&\n payloadType !== \"JUMP_TO_ACTION\"\n ) {\n return;\n }\n if (!message.state) return;\n let restored: Record<string, unknown>;\n try {\n restored = JSON.parse(message.state);\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(restored)) {\n const atom = atoms[key];\n if (!atom) continue;\n atom.silentUpdate(value);\n // Synthesise an update event so React subscribers re-render.\n events.trigger(`atoms.${key}.update`, value, atom.currentValue, atom);\n }\n });\n\n return () => {\n clearInterval(scanTimer);\n for (const unsub of subscribed.values()) unsub();\n subscribed.clear();\n unsubDevtools();\n devtools.disconnect?.();\n };\n}\n","import { createAtom } from './atom';\nimport type { Atom, AtomActions, AtomOptions } from './types';\n\nexport type IndexOrCallback<Value> =\n | number\n | ((value: Value, index: number, list: Value[]) => boolean);\n\nexport interface AtomCollectionActions<Value> extends AtomActions<Value[]> {\n /**\n * Add items to the end of the array\n */\n push(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Add items to the beginning of the array\n */\n unshift(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Remove the last item from the array\n */\n pop(this: Atom<Value[]>): void;\n /**\n * Remove the first item from the array\n */\n shift(this: Atom<Value[]>): void;\n /**\n * Remove item from array either by index or callback\n */\n remove(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): void;\n /**\n * Remove item from array by value\n */\n removeItem(this: Atom<Value[]>, item: Value): void;\n /**\n * Remove all occurrences of an item from the array\n */\n removeAll(this: Atom<Value[]>, item: Value): void;\n /**\n * Get item from array either by index or callback\n */\n get(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): Value | undefined;\n /**\n * Find index of item in array by callback\n */\n index(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => boolean,\n ): number;\n /**\n * Map array items\n * This will update the array with the new mapped array and trigger update\n */\n map(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => Value,\n ): Value[];\n /**\n * Loop through array items\n */\n forEach(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => void,\n ): void;\n /**\n * Replace item in array by index\n */\n replace(this: Atom<Value[]>, index: number, item: Value): void;\n /**\n * Get array length\n */\n length: number; // As a property\n}\n\nexport type CollectionOptions<Value> = Omit<\n AtomOptions<Value[], AtomCollectionActions<Value>>,\n 'default'\n> & {\n default?: Value[];\n};\n\n/**\n * Create an atom collection\n */\nexport function atomCollection<Value = any>(\n options: CollectionOptions<Value>,\n): Atom<Value[], AtomCollectionActions<Value>> {\n return createAtom<Value[], AtomCollectionActions<Value>>({\n key: options.key,\n default: options.default ?? [],\n actions: {\n ...options.actions,\n push(...items: Value[]) {\n this.update([...this.currentValue, ...items]);\n },\n pop() {\n this.update(this.currentValue.slice(0, -1));\n },\n shift() {\n this.update(this.currentValue.slice(1));\n },\n unshift(...items: Value[]) {\n this.update([...items, ...this.currentValue]);\n },\n remove(indexOrCallback: IndexOrCallback<Value>) {\n const index =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n if (index === -1) return;\n\n this.update(this.value.filter((_, i) => i !== index));\n },\n removeItem(item: Value) {\n const index = this.value.indexOf(item);\n\n if (index === -1) return;\n\n // using splice\n this.value.splice(index, 1);\n\n this.update([...this.value]);\n },\n removeAll(item: Value) {\n this.update(this.value.filter((value) => value !== item));\n },\n get(indexOrCallback: IndexOrCallback<Value>) {\n const index: number =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n return this.value[index];\n },\n index(callback: (item: Value, index: number, array: Value[]) => boolean) {\n return this.value.findIndex(callback);\n },\n map(callback: (item: Value, index: number, array: Value[]) => Value) {\n const value = this.value.map(callback);\n\n this.update(value);\n\n return value;\n },\n forEach(callback: (item: Value, index: number, array: Value[]) => void) {\n this.value.forEach(callback);\n },\n get length() {\n return this.value?.length;\n },\n replace(index: number, item: Value) {\n this.update(\n this.value.map((value, i) => {\n if (i === index) {\n return item;\n }\n\n return value;\n }),\n );\n },\n },\n });\n}\n"],"mappings":"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"} |
| import { Atom, AtomActions, AtomOptions } from "./types.mjs"; | ||
| //#region ../@mongez/atom/src/atom-collection.d.ts | ||
| //#region ../atom/src/atom-collection.d.ts | ||
| type IndexOrCallback<Value> = number | ((value: Value, index: number, list: Value[]) => boolean); | ||
@@ -5,0 +5,0 @@ interface AtomCollectionActions<Value> extends AtomActions<Value[]> { |
| import { createAtom } from "./atom.mjs"; | ||
| //#region ../@mongez/atom/src/atom-collection.ts | ||
| //#region ../atom/src/atom-collection.ts | ||
| /** | ||
@@ -5,0 +5,0 @@ * Create an atom collection |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"atom-collection.mjs","names":[],"sources":["../../../../@mongez/atom/src/atom-collection.ts"],"sourcesContent":["import { createAtom } from './atom';\nimport type { Atom, AtomActions, AtomOptions } from './types';\n\nexport type IndexOrCallback<Value> =\n | number\n | ((value: Value, index: number, list: Value[]) => boolean);\n\nexport interface AtomCollectionActions<Value> extends AtomActions<Value[]> {\n /**\n * Add items to the end of the array\n */\n push(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Add items to the beginning of the array\n */\n unshift(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Remove the last item from the array\n */\n pop(this: Atom<Value[]>): void;\n /**\n * Remove the first item from the array\n */\n shift(this: Atom<Value[]>): void;\n /**\n * Remove item from array either by index or callback\n */\n remove(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): void;\n /**\n * Remove item from array by value\n */\n removeItem(this: Atom<Value[]>, item: Value): void;\n /**\n * Remove all occurrences of an item from the array\n */\n removeAll(this: Atom<Value[]>, item: Value): void;\n /**\n * Get item from array either by index or callback\n */\n get(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): Value | undefined;\n /**\n * Find index of item in array by callback\n */\n index(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => boolean,\n ): number;\n /**\n * Map array items\n * This will update the array with the new mapped array and trigger update\n */\n map(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => Value,\n ): Value[];\n /**\n * Loop through array items\n */\n forEach(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => void,\n ): void;\n /**\n * Replace item in array by index\n */\n replace(this: Atom<Value[]>, index: number, item: Value): void;\n /**\n * Get array length\n */\n length: number; // As a property\n}\n\nexport type CollectionOptions<Value> = Omit<\n AtomOptions<Value[], AtomCollectionActions<Value>>,\n 'default'\n> & {\n default?: Value[];\n};\n\n/**\n * Create an atom collection\n */\nexport function atomCollection<Value = any>(\n options: CollectionOptions<Value>,\n): Atom<Value[], AtomCollectionActions<Value>> {\n return createAtom<Value[], AtomCollectionActions<Value>>({\n key: options.key,\n default: options.default ?? [],\n actions: {\n ...options.actions,\n push(...items: Value[]) {\n this.update([...this.currentValue, ...items]);\n },\n pop() {\n this.update(this.currentValue.slice(0, -1));\n },\n shift() {\n this.update(this.currentValue.slice(1));\n },\n unshift(...items: Value[]) {\n this.update([...items, ...this.currentValue]);\n },\n remove(indexOrCallback: IndexOrCallback<Value>) {\n const index =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n if (index === -1) return;\n\n this.update(this.value.filter((_, i) => i !== index));\n },\n removeItem(item: Value) {\n const index = this.value.indexOf(item);\n\n if (index === -1) return;\n\n // using splice\n this.value.splice(index, 1);\n\n this.update([...this.value]);\n },\n removeAll(item: Value) {\n this.update(this.value.filter((value) => value !== item));\n },\n get(indexOrCallback: IndexOrCallback<Value>) {\n const index: number =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n return this.value[index];\n },\n index(callback: (item: Value, index: number, array: Value[]) => boolean) {\n return this.value.findIndex(callback);\n },\n map(callback: (item: Value, index: number, array: Value[]) => Value) {\n const value = this.value.map(callback);\n\n this.update(value);\n\n return value;\n },\n forEach(callback: (item: Value, index: number, array: Value[]) => void) {\n this.value.forEach(callback);\n },\n get length() {\n return this.value?.length;\n },\n replace(index: number, item: Value) {\n this.update(\n this.value.map((value, i) => {\n if (i === index) {\n return item;\n }\n\n return value;\n }),\n );\n },\n },\n });\n}\n"],"mappings":";;;;;;AAkFA,SAAgB,eACd,SAC6C;CAC7C,OAAO,WAAkD;EACvD,KAAK,QAAQ;EACb,SAAS,QAAQ,WAAW,CAAC;EAC7B,SAAS;GACP,GAAG,QAAQ;GACX,KAAK,GAAG,OAAgB;IACtB,KAAK,OAAO,CAAC,GAAG,KAAK,cAAc,GAAG,KAAK,CAAC;GAC9C;GACA,MAAM;IACJ,KAAK,OAAO,KAAK,aAAa,MAAM,GAAG,EAAE,CAAC;GAC5C;GACA,QAAQ;IACN,KAAK,OAAO,KAAK,aAAa,MAAM,CAAC,CAAC;GACxC;GACA,QAAQ,GAAG,OAAgB;IACzB,KAAK,OAAO,CAAC,GAAG,OAAO,GAAG,KAAK,YAAY,CAAC;GAC9C;GACA,OAAO,iBAAyC;IAC9C,MAAM,QACJ,OAAO,oBAAoB,aACvB,KAAK,MAAM,UAAU,eAAe,IACpC;IAEN,IAAI,UAAU,IAAI;IAElB,KAAK,OAAO,KAAK,MAAM,QAAQ,GAAG,MAAM,MAAM,KAAK,CAAC;GACtD;GACA,WAAW,MAAa;IACtB,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI;IAErC,IAAI,UAAU,IAAI;IAGlB,KAAK,MAAM,OAAO,OAAO,CAAC;IAE1B,KAAK,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC;GAC7B;GACA,UAAU,MAAa;IACrB,KAAK,OAAO,KAAK,MAAM,QAAQ,UAAU,UAAU,IAAI,CAAC;GAC1D;GACA,IAAI,iBAAyC;IAC3C,MAAM,QACJ,OAAO,oBAAoB,aACvB,KAAK,MAAM,UAAU,eAAe,IACpC;IAEN,OAAO,KAAK,MAAM;GACpB;GACA,MAAM,UAAmE;IACvE,OAAO,KAAK,MAAM,UAAU,QAAQ;GACtC;GACA,IAAI,UAAiE;IACnE,MAAM,QAAQ,KAAK,MAAM,IAAI,QAAQ;IAErC,KAAK,OAAO,KAAK;IAEjB,OAAO;GACT;GACA,QAAQ,UAAgE;IACtE,KAAK,MAAM,QAAQ,QAAQ;GAC7B;GACA,IAAI,SAAS;IACX,OAAO,KAAK,OAAO;GACrB;GACA,QAAQ,OAAe,MAAa;IAClC,KAAK,OACH,KAAK,MAAM,KAAK,OAAO,MAAM;KAC3B,IAAI,MAAM,OACR,OAAO;KAGT,OAAO;IACT,CAAC,CACH;GACF;EACF;CACF,CAAC;AACH"} | ||
| {"version":3,"file":"atom-collection.mjs","names":[],"sources":["../../../../../../atom/src/atom-collection.ts"],"sourcesContent":["import { createAtom } from './atom';\nimport type { Atom, AtomActions, AtomOptions } from './types';\n\nexport type IndexOrCallback<Value> =\n | number\n | ((value: Value, index: number, list: Value[]) => boolean);\n\nexport interface AtomCollectionActions<Value> extends AtomActions<Value[]> {\n /**\n * Add items to the end of the array\n */\n push(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Add items to the beginning of the array\n */\n unshift(this: Atom<Value[]>, ...items: Value[]): void;\n /**\n * Remove the last item from the array\n */\n pop(this: Atom<Value[]>): void;\n /**\n * Remove the first item from the array\n */\n shift(this: Atom<Value[]>): void;\n /**\n * Remove item from array either by index or callback\n */\n remove(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): void;\n /**\n * Remove item from array by value\n */\n removeItem(this: Atom<Value[]>, item: Value): void;\n /**\n * Remove all occurrences of an item from the array\n */\n removeAll(this: Atom<Value[]>, item: Value): void;\n /**\n * Get item from array either by index or callback\n */\n get(this: Atom<Value[]>, indexOrCallback: IndexOrCallback<Value>): Value | undefined;\n /**\n * Find index of item in array by callback\n */\n index(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => boolean,\n ): number;\n /**\n * Map array items\n * This will update the array with the new mapped array and trigger update\n */\n map(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => Value,\n ): Value[];\n /**\n * Loop through array items\n */\n forEach(\n this: Atom<Value[]>,\n callback: (item: Value, index: number, array: Value[]) => void,\n ): void;\n /**\n * Replace item in array by index\n */\n replace(this: Atom<Value[]>, index: number, item: Value): void;\n /**\n * Get array length\n */\n length: number; // As a property\n}\n\nexport type CollectionOptions<Value> = Omit<\n AtomOptions<Value[], AtomCollectionActions<Value>>,\n 'default'\n> & {\n default?: Value[];\n};\n\n/**\n * Create an atom collection\n */\nexport function atomCollection<Value = any>(\n options: CollectionOptions<Value>,\n): Atom<Value[], AtomCollectionActions<Value>> {\n return createAtom<Value[], AtomCollectionActions<Value>>({\n key: options.key,\n default: options.default ?? [],\n actions: {\n ...options.actions,\n push(...items: Value[]) {\n this.update([...this.currentValue, ...items]);\n },\n pop() {\n this.update(this.currentValue.slice(0, -1));\n },\n shift() {\n this.update(this.currentValue.slice(1));\n },\n unshift(...items: Value[]) {\n this.update([...items, ...this.currentValue]);\n },\n remove(indexOrCallback: IndexOrCallback<Value>) {\n const index =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n if (index === -1) return;\n\n this.update(this.value.filter((_, i) => i !== index));\n },\n removeItem(item: Value) {\n const index = this.value.indexOf(item);\n\n if (index === -1) return;\n\n // using splice\n this.value.splice(index, 1);\n\n this.update([...this.value]);\n },\n removeAll(item: Value) {\n this.update(this.value.filter((value) => value !== item));\n },\n get(indexOrCallback: IndexOrCallback<Value>) {\n const index: number =\n typeof indexOrCallback === 'function'\n ? this.value.findIndex(indexOrCallback)\n : indexOrCallback;\n\n return this.value[index];\n },\n index(callback: (item: Value, index: number, array: Value[]) => boolean) {\n return this.value.findIndex(callback);\n },\n map(callback: (item: Value, index: number, array: Value[]) => Value) {\n const value = this.value.map(callback);\n\n this.update(value);\n\n return value;\n },\n forEach(callback: (item: Value, index: number, array: Value[]) => void) {\n this.value.forEach(callback);\n },\n get length() {\n return this.value?.length;\n },\n replace(index: number, item: Value) {\n this.update(\n this.value.map((value, i) => {\n if (i === index) {\n return item;\n }\n\n return value;\n }),\n );\n },\n },\n });\n}\n"],"mappings":";;;;;;AAkFA,SAAgB,eACd,SAC6C;CAC7C,OAAO,WAAkD;EACvD,KAAK,QAAQ;EACb,SAAS,QAAQ,WAAW,CAAC;EAC7B,SAAS;GACP,GAAG,QAAQ;GACX,KAAK,GAAG,OAAgB;IACtB,KAAK,OAAO,CAAC,GAAG,KAAK,cAAc,GAAG,KAAK,CAAC;GAC9C;GACA,MAAM;IACJ,KAAK,OAAO,KAAK,aAAa,MAAM,GAAG,EAAE,CAAC;GAC5C;GACA,QAAQ;IACN,KAAK,OAAO,KAAK,aAAa,MAAM,CAAC,CAAC;GACxC;GACA,QAAQ,GAAG,OAAgB;IACzB,KAAK,OAAO,CAAC,GAAG,OAAO,GAAG,KAAK,YAAY,CAAC;GAC9C;GACA,OAAO,iBAAyC;IAC9C,MAAM,QACJ,OAAO,oBAAoB,aACvB,KAAK,MAAM,UAAU,eAAe,IACpC;IAEN,IAAI,UAAU,IAAI;IAElB,KAAK,OAAO,KAAK,MAAM,QAAQ,GAAG,MAAM,MAAM,KAAK,CAAC;GACtD;GACA,WAAW,MAAa;IACtB,MAAM,QAAQ,KAAK,MAAM,QAAQ,IAAI;IAErC,IAAI,UAAU,IAAI;IAGlB,KAAK,MAAM,OAAO,OAAO,CAAC;IAE1B,KAAK,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC;GAC7B;GACA,UAAU,MAAa;IACrB,KAAK,OAAO,KAAK,MAAM,QAAQ,UAAU,UAAU,IAAI,CAAC;GAC1D;GACA,IAAI,iBAAyC;IAC3C,MAAM,QACJ,OAAO,oBAAoB,aACvB,KAAK,MAAM,UAAU,eAAe,IACpC;IAEN,OAAO,KAAK,MAAM;GACpB;GACA,MAAM,UAAmE;IACvE,OAAO,KAAK,MAAM,UAAU,QAAQ;GACtC;GACA,IAAI,UAAiE;IACnE,MAAM,QAAQ,KAAK,MAAM,IAAI,QAAQ;IAErC,KAAK,OAAO,KAAK;IAEjB,OAAO;GACT;GACA,QAAQ,UAAgE;IACtE,KAAK,MAAM,QAAQ,QAAQ;GAC7B;GACA,IAAI,SAAS;IACX,OAAO,KAAK,OAAO;GACrB;GACA,QAAQ,OAAe,MAAa;IAClC,KAAK,OACH,KAAK,MAAM,KAAK,OAAO,MAAM;KAC3B,IAAI,MAAM,OACR,OAAO;KAGT,OAAO;IACT,CAAC,CACH;GACF;EACF;CACF,CAAC;AACH"} |
| import { Atom } from "./types.mjs"; | ||
| //#region ../@mongez/atom/src/atom-store.d.ts | ||
| //#region ../atom/src/atom-store.d.ts | ||
| /** | ||
@@ -5,0 +5,0 @@ * A store is an isolated registry of atom instances. |
@@ -1,2 +0,2 @@ | ||
| //#region ../@mongez/atom/src/atom-store.ts | ||
| //#region ../atom/src/atom-store.ts | ||
| /** | ||
@@ -3,0 +3,0 @@ * A store is an isolated registry of atom instances. |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"atom-store.mjs","names":[],"sources":["../../../../@mongez/atom/src/atom-store.ts"],"sourcesContent":["import type { Atom } from \"./types\";\n\n/**\n * A store is an isolated registry of atom instances.\n *\n * Each store creates and holds its own clones of atom templates so that\n * concurrent consumers (e.g. server-rendered requests) do not share state.\n *\n * Stores are looked up via React context by `<AtomStoreProvider>` in\n * `@mongez/react-atom`, but the class itself is framework-agnostic and can\n * be used directly outside React.\n */\nexport class AtomStore {\n /**\n * Scoped atom clones, keyed by the ORIGINAL atom's key (not the clone key).\n */\n private store = new Map<string, Atom<any>>();\n\n /**\n * Values applied to atoms the moment they enter the store. Useful for\n * SSR hydration when atoms register lazily.\n */\n private pendingValues = new Map<string, unknown>();\n\n /**\n * Get or lazily create a store-scoped clone of the given atom template.\n * The clone shares the template's options (actions, beforeUpdate, get,\n * onUpdate) but owns its own state and event topic.\n */\n use<V, A extends Record<string, any> = {}>(template: Atom<V, A>): Atom<V, A> {\n const existing = this.store.get(template.key);\n if (existing) return existing as Atom<V, A>;\n\n const scoped = template.clone({ register: false }) as Atom<V, A>;\n\n if (this.pendingValues.has(template.key)) {\n scoped.silentUpdate(this.pendingValues.get(template.key) as V);\n this.pendingValues.delete(template.key);\n }\n\n this.store.set(template.key, scoped);\n return scoped;\n }\n\n /**\n * Look up a scoped atom by its original key. Returns undefined when the\n * atom has not been used in this store yet.\n */\n get<V = any>(key: string): Atom<V> | undefined {\n return this.store.get(key) as Atom<V> | undefined;\n }\n\n /**\n * True when the given key has a scoped atom in this store.\n */\n has(key: string): boolean {\n return this.store.has(key);\n }\n\n /**\n * All scoped atoms currently in this store.\n */\n list(): Atom<any>[] {\n return Array.from(this.store.values());\n }\n\n /**\n * Apply initial values to atoms in the store. Atoms not yet registered\n * have their values queued until first `use(template)` call.\n */\n hydrate(snapshot: Record<string, unknown>): void {\n for (const key in snapshot) {\n const value = snapshot[key];\n const atom = this.store.get(key);\n if (atom) {\n atom.silentUpdate(value);\n } else {\n this.pendingValues.set(key, value);\n }\n }\n }\n\n /**\n * Serialize the current values of every scoped atom as a plain object.\n * Intended for SSR payloads that the client will pass back via\n * `<AtomStoreProvider initialValues={...}>`.\n */\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, atom] of this.store.entries()) {\n result[key] = atom.value;\n }\n return result;\n }\n\n /**\n * Destroy every scoped atom and clear the store. Call this at the end of\n * a request lifecycle to release event-bus subscriptions and let the\n * scoped atoms be garbage collected.\n */\n destroy(): void {\n for (const atom of this.store.values()) {\n atom.destroy();\n }\n this.store.clear();\n this.pendingValues.clear();\n }\n}\n\n/**\n * Convenience factory; equivalent to `new AtomStore()`.\n */\nexport function createAtomStore(): AtomStore {\n return new AtomStore();\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAa,YAAb,MAAuB;;;;CAIrB,AAAQ,wBAAQ,IAAI,IAAuB;;;;;CAM3C,AAAQ,gCAAgB,IAAI,IAAqB;;;;;;CAOjD,IAA2C,UAAkC;EAC3E,MAAM,WAAW,KAAK,MAAM,IAAI,SAAS,GAAG;EAC5C,IAAI,UAAU,OAAO;EAErB,MAAM,SAAS,SAAS,MAAM,EAAE,UAAU,MAAM,CAAC;EAEjD,IAAI,KAAK,cAAc,IAAI,SAAS,GAAG,GAAG;GACxC,OAAO,aAAa,KAAK,cAAc,IAAI,SAAS,GAAG,CAAM;GAC7D,KAAK,cAAc,OAAO,SAAS,GAAG;EACxC;EAEA,KAAK,MAAM,IAAI,SAAS,KAAK,MAAM;EACnC,OAAO;CACT;;;;;CAMA,IAAa,KAAkC;EAC7C,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;CAKA,IAAI,KAAsB;EACxB,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;CAKA,OAAoB;EAClB,OAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC;CACvC;;;;;CAMA,QAAQ,UAAyC;EAC/C,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GACvB,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;GAC/B,IAAI,MACF,KAAK,aAAa,KAAK;QAEvB,KAAK,cAAc,IAAI,KAAK,KAAK;EAErC;CACF;;;;;;CAOA,WAAoC;EAClC,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,SAAS,KAAK,MAAM,QAAQ,GAC3C,OAAO,OAAO,KAAK;EAErB,OAAO;CACT;;;;;;CAOA,UAAgB;EACd,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GACnC,KAAK,QAAQ;EAEf,KAAK,MAAM,MAAM;EACjB,KAAK,cAAc,MAAM;CAC3B;AACF;;;;AAKA,SAAgB,kBAA6B;CAC3C,OAAO,IAAI,UAAU;AACvB"} | ||
| {"version":3,"file":"atom-store.mjs","names":[],"sources":["../../../../../../atom/src/atom-store.ts"],"sourcesContent":["import type { Atom } from \"./types\";\n\n/**\n * A store is an isolated registry of atom instances.\n *\n * Each store creates and holds its own clones of atom templates so that\n * concurrent consumers (e.g. server-rendered requests) do not share state.\n *\n * Stores are looked up via React context by `<AtomStoreProvider>` in\n * `@mongez/react-atom`, but the class itself is framework-agnostic and can\n * be used directly outside React.\n */\nexport class AtomStore {\n /**\n * Scoped atom clones, keyed by the ORIGINAL atom's key (not the clone key).\n */\n private store = new Map<string, Atom<any>>();\n\n /**\n * Values applied to atoms the moment they enter the store. Useful for\n * SSR hydration when atoms register lazily.\n */\n private pendingValues = new Map<string, unknown>();\n\n /**\n * Get or lazily create a store-scoped clone of the given atom template.\n * The clone shares the template's options (actions, beforeUpdate, get,\n * onUpdate) but owns its own state and event topic.\n */\n use<V, A extends Record<string, any> = {}>(template: Atom<V, A>): Atom<V, A> {\n const existing = this.store.get(template.key);\n if (existing) return existing as Atom<V, A>;\n\n const scoped = template.clone({ register: false }) as Atom<V, A>;\n\n if (this.pendingValues.has(template.key)) {\n scoped.silentUpdate(this.pendingValues.get(template.key) as V);\n this.pendingValues.delete(template.key);\n }\n\n this.store.set(template.key, scoped);\n return scoped;\n }\n\n /**\n * Look up a scoped atom by its original key. Returns undefined when the\n * atom has not been used in this store yet.\n */\n get<V = any>(key: string): Atom<V> | undefined {\n return this.store.get(key) as Atom<V> | undefined;\n }\n\n /**\n * True when the given key has a scoped atom in this store.\n */\n has(key: string): boolean {\n return this.store.has(key);\n }\n\n /**\n * All scoped atoms currently in this store.\n */\n list(): Atom<any>[] {\n return Array.from(this.store.values());\n }\n\n /**\n * Apply initial values to atoms in the store. Atoms not yet registered\n * have their values queued until first `use(template)` call.\n */\n hydrate(snapshot: Record<string, unknown>): void {\n for (const key in snapshot) {\n const value = snapshot[key];\n const atom = this.store.get(key);\n if (atom) {\n atom.silentUpdate(value);\n } else {\n this.pendingValues.set(key, value);\n }\n }\n }\n\n /**\n * Serialize the current values of every scoped atom as a plain object.\n * Intended for SSR payloads that the client will pass back via\n * `<AtomStoreProvider initialValues={...}>`.\n */\n snapshot(): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const [key, atom] of this.store.entries()) {\n result[key] = atom.value;\n }\n return result;\n }\n\n /**\n * Destroy every scoped atom and clear the store. Call this at the end of\n * a request lifecycle to release event-bus subscriptions and let the\n * scoped atoms be garbage collected.\n */\n destroy(): void {\n for (const atom of this.store.values()) {\n atom.destroy();\n }\n this.store.clear();\n this.pendingValues.clear();\n }\n}\n\n/**\n * Convenience factory; equivalent to `new AtomStore()`.\n */\nexport function createAtomStore(): AtomStore {\n return new AtomStore();\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAa,YAAb,MAAuB;;;;CAIrB,AAAQ,wBAAQ,IAAI,IAAuB;;;;;CAM3C,AAAQ,gCAAgB,IAAI,IAAqB;;;;;;CAOjD,IAA2C,UAAkC;EAC3E,MAAM,WAAW,KAAK,MAAM,IAAI,SAAS,GAAG;EAC5C,IAAI,UAAU,OAAO;EAErB,MAAM,SAAS,SAAS,MAAM,EAAE,UAAU,MAAM,CAAC;EAEjD,IAAI,KAAK,cAAc,IAAI,SAAS,GAAG,GAAG;GACxC,OAAO,aAAa,KAAK,cAAc,IAAI,SAAS,GAAG,CAAM;GAC7D,KAAK,cAAc,OAAO,SAAS,GAAG;EACxC;EAEA,KAAK,MAAM,IAAI,SAAS,KAAK,MAAM;EACnC,OAAO;CACT;;;;;CAMA,IAAa,KAAkC;EAC7C,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;CAKA,IAAI,KAAsB;EACxB,OAAO,KAAK,MAAM,IAAI,GAAG;CAC3B;;;;CAKA,OAAoB;EAClB,OAAO,MAAM,KAAK,KAAK,MAAM,OAAO,CAAC;CACvC;;;;;CAMA,QAAQ,UAAyC;EAC/C,KAAK,MAAM,OAAO,UAAU;GAC1B,MAAM,QAAQ,SAAS;GACvB,MAAM,OAAO,KAAK,MAAM,IAAI,GAAG;GAC/B,IAAI,MACF,KAAK,aAAa,KAAK;QAEvB,KAAK,cAAc,IAAI,KAAK,KAAK;EAErC;CACF;;;;;;CAOA,WAAoC;EAClC,MAAM,SAAkC,CAAC;EACzC,KAAK,MAAM,CAAC,KAAK,SAAS,KAAK,MAAM,QAAQ,GAC3C,OAAO,OAAO,KAAK;EAErB,OAAO;CACT;;;;;;CAOA,UAAgB;EACd,KAAK,MAAM,QAAQ,KAAK,MAAM,OAAO,GACnC,KAAK,QAAQ;EAEf,KAAK,MAAM,MAAM;EACjB,KAAK,cAAc,MAAM;CAC3B;AACF;;;;AAKA,SAAgB,kBAA6B;CAC3C,OAAO,IAAI,UAAU;AACvB"} |
+1
-1
| import { Atom, AtomActions, AtomOptions, AtomValue } from "./types.mjs"; | ||
| //#region ../@mongez/atom/src/atom.d.ts | ||
| //#region ../atom/src/atom.d.ts | ||
| declare const atoms: Record<string, Atom<any>>; | ||
@@ -5,0 +5,0 @@ /** |
+1
-1
@@ -5,3 +5,3 @@ import { attachPersist, resolvePersistAdapter } from "./persist.mjs"; | ||
| //#region ../@mongez/atom/src/atom.ts | ||
| //#region ../atom/src/atom.ts | ||
| const atoms = {}; | ||
@@ -8,0 +8,0 @@ let cloneCounter = 0; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"atom.mjs","names":[],"sources":["../../../../@mongez/atom/src/atom.ts"],"sourcesContent":["/* eslint-disable no-multi-assign */\n/* eslint-disable guard-for-in */\n/* eslint-disable @typescript-eslint/no-shadow */\n/* eslint-disable prefer-template */\nimport events, { EventSubscription } from \"@mongez/events\";\nimport { clone, get } from \"@mongez/reinforcements\";\nimport { attachPersist, resolvePersistAdapter } from \"./persist\";\nimport {\n Atom,\n AtomActions,\n AtomOptions,\n AtomPartialChangeCallback,\n AtomValue,\n} from \"./types\";\n\nexport const atoms: Record<string, Atom<any>> = {};\n\nlet cloneCounter = 0;\n\n/**\n * Get atom by name\n */\nexport function getAtom<T>(name: string): Atom<T> | undefined {\n return atoms[name];\n}\n\n/**\n * Options that control how an atom is constructed.\n * Internal-only; used by store-scoped clones to opt out of the global registry.\n */\nexport type CreateAtomOptions = {\n /**\n * When false, the new atom will NOT be inserted into the module-level\n * `atoms` registry. Used by `AtomStore` to create per-store clones that\n * stay isolated from the global lookup table.\n *\n * Defaults to true.\n */\n register?: boolean;\n};\n\n/**\n * Create a new atom\n */\nexport function createAtom<\n Value = any,\n Actions extends AtomActions<Value> = AtomActions<Value>\n>(\n data: AtomOptions<AtomValue<Value>, Actions>,\n options: CreateAtomOptions = {}\n): Atom<Value, Actions> {\n let defaultValue = data.default;\n let atomValue = data.default;\n\n let atomValueIsObject = false;\n\n if (defaultValue && typeof defaultValue === \"object\") {\n atomValue = defaultValue = clone(defaultValue);\n atomValueIsObject = true;\n }\n\n const atomType = Array.isArray(defaultValue) ? \"array\" : typeof defaultValue;\n\n const atomEvent = `atoms.${data.key}`;\n\n const event = (type: string): string => `${atomEvent}.${type}`;\n\n const watchers: any = {};\n\n const atomKey = data.key;\n\n const atom: Atom<Value, Actions> = {\n default: defaultValue,\n currentValue: atomValue,\n key: atomKey,\n get type() {\n return atomType;\n },\n watch<T extends keyof Value>(\n key: T,\n callback: AtomPartialChangeCallback\n ): EventSubscription {\n if (!watchers[key]) {\n watchers[key] = [];\n }\n\n watchers[key].push(callback);\n\n return {\n unsubscribe: () => {\n watchers[key] = watchers[key].filter(\n (cb: AtomPartialChangeCallback) => cb !== callback\n );\n },\n } as EventSubscription;\n },\n get defaultValue(): Value {\n return this.default;\n },\n get value(): Value {\n return this.currentValue;\n },\n change<T extends keyof Value>(key: T, newValue: any) {\n this.update({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n silentChange<T extends keyof Value>(key: T, newValue: any) {\n this.silentUpdate({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n merge(newValue: Partial<Value>) {\n this.update({\n ...this.currentValue,\n ...newValue,\n });\n },\n update(newValue: (oldValue: Value, atom: Atom<Value, Actions>) => Value) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = newValue(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n events.trigger(event(\"update\"), this.currentValue, oldValue, this);\n if (atomValueIsObject) {\n for (const key in watchers) {\n const keyOldValue = get(oldValue, key);\n const keyNewValue = get(updatedValue, key);\n\n if (keyOldValue !== keyNewValue) {\n watchers[key].forEach(\n (callback: AtomPartialChangeCallback) =>\n callback(keyNewValue, keyOldValue, this)\n );\n }\n }\n }\n },\n silentUpdate(\n newValue: ((oldValue: Value, atom: Atom<Value>) => Value) | Value\n ) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = (newValue as any)(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n },\n onChange(\n callback: (newValue: Value, oldValue: Value, atom: Atom<Value>) => void\n ): EventSubscription {\n return events.subscribe(event(\"update\"), callback);\n },\n onReset(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(event(\"reset\"), callback);\n },\n get<T extends keyof Value>(key: T, defaultValue?: any): Value[T] {\n if (data.get) {\n return data.get(\n key as string,\n defaultValue,\n this.currentValue\n ) as Value[T];\n }\n\n return get(this.currentValue, key as string, defaultValue);\n },\n destroy() {\n events.trigger(event(\"delete\"), this);\n\n events.unsubscribeNamespace(atomEvent);\n delete atoms[this.key];\n },\n onDestroy(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(`atoms.${this.key}.delete`, callback);\n },\n reset() {\n this.update(clone(this.defaultValue));\n events.trigger(event(\"reset\"), this);\n },\n /**\n * Reset the value without triggering the update event\n * But this will trigger the reset event\n */\n silentReset() {\n this.currentValue = clone(this.defaultValue);\n events.trigger(event(\"reset\"), this);\n },\n clone(cloneOptions?: CreateAtomOptions) {\n return createAtom(\n {\n key: this.key + \".clone.\" + (++cloneCounter),\n default: clone(this.currentValue),\n beforeUpdate: data.beforeUpdate,\n get: data.get,\n onUpdate: data.onUpdate,\n actions: data.actions,\n },\n { register: cloneOptions?.register ?? true }\n );\n },\n } as any;\n\n // Install actions on the atom instance.\n //\n // Three kinds of entries can appear in `actions`:\n // 1. Plain functions — bound to the atom so `this` refers to it.\n // 2. Property getters (e.g. `atomCollection`'s `length`) — forwarded\n // as getters bound to the atom; calling `.bind(...)` on them would\n // blow up because the getter is invoked the moment we touch it.\n // 3. Anything else — assigned by value as a fallback.\n if (data.actions) {\n const actions = data.actions as Record<string, unknown>;\n for (const actionKey of Object.keys(actions)) {\n const descriptor = Object.getOwnPropertyDescriptor(actions, actionKey);\n if (descriptor?.get) {\n Object.defineProperty(atom, actionKey, {\n get: descriptor.get.bind(atom),\n set: descriptor.set ? descriptor.set.bind(atom) : undefined,\n enumerable: true,\n configurable: true,\n });\n continue;\n }\n const value = actions[actionKey];\n if (typeof value === \"function\") {\n (atom as any)[actionKey] = (value as Function).bind(atom);\n } else {\n (atom as any)[actionKey] = value;\n }\n }\n }\n\n if (data.onUpdate) {\n events.subscribe(event(\"update\"), data.onUpdate.bind(atom));\n }\n\n if (options.register !== false) {\n atoms[atomKey] = atom;\n }\n\n // Persistence wiring. Resolve the adapter (boolean → built-in\n // localStorage, object → as-is, falsy → skip). The adapter handles\n // the initial read and the write-through; we just hand it the atom.\n const adapter = resolvePersistAdapter(data.persist);\n if (adapter) {\n attachPersist(atom, adapter, data);\n }\n\n return atom;\n}\n\n/**\n * Get all atoms list\n */\nexport function atomsList(): Atom<any>[] {\n return Object.values(atoms);\n}\n\n/**\n * Return atoms in object format\n */\nexport function atomsObject(): Record<string, Atom<any>> {\n return atoms;\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"atom.mjs","names":[],"sources":["../../../../../../atom/src/atom.ts"],"sourcesContent":["/* eslint-disable no-multi-assign */\n/* eslint-disable guard-for-in */\n/* eslint-disable @typescript-eslint/no-shadow */\n/* eslint-disable prefer-template */\nimport events, { EventSubscription } from \"@mongez/events\";\nimport { clone, get } from \"@mongez/reinforcements\";\nimport { attachPersist, resolvePersistAdapter } from \"./persist\";\nimport {\n Atom,\n AtomActions,\n AtomOptions,\n AtomPartialChangeCallback,\n AtomValue,\n} from \"./types\";\n\nexport const atoms: Record<string, Atom<any>> = {};\n\nlet cloneCounter = 0;\n\n/**\n * Get atom by name\n */\nexport function getAtom<T>(name: string): Atom<T> | undefined {\n return atoms[name];\n}\n\n/**\n * Options that control how an atom is constructed.\n * Internal-only; used by store-scoped clones to opt out of the global registry.\n */\nexport type CreateAtomOptions = {\n /**\n * When false, the new atom will NOT be inserted into the module-level\n * `atoms` registry. Used by `AtomStore` to create per-store clones that\n * stay isolated from the global lookup table.\n *\n * Defaults to true.\n */\n register?: boolean;\n};\n\n/**\n * Create a new atom\n */\nexport function createAtom<\n Value = any,\n Actions extends AtomActions<Value> = AtomActions<Value>\n>(\n data: AtomOptions<AtomValue<Value>, Actions>,\n options: CreateAtomOptions = {}\n): Atom<Value, Actions> {\n let defaultValue = data.default;\n let atomValue = data.default;\n\n let atomValueIsObject = false;\n\n if (defaultValue && typeof defaultValue === \"object\") {\n atomValue = defaultValue = clone(defaultValue);\n atomValueIsObject = true;\n }\n\n const atomType = Array.isArray(defaultValue) ? \"array\" : typeof defaultValue;\n\n const atomEvent = `atoms.${data.key}`;\n\n const event = (type: string): string => `${atomEvent}.${type}`;\n\n const watchers: any = {};\n\n const atomKey = data.key;\n\n const atom: Atom<Value, Actions> = {\n default: defaultValue,\n currentValue: atomValue,\n key: atomKey,\n get type() {\n return atomType;\n },\n watch<T extends keyof Value>(\n key: T,\n callback: AtomPartialChangeCallback\n ): EventSubscription {\n if (!watchers[key]) {\n watchers[key] = [];\n }\n\n watchers[key].push(callback);\n\n return {\n unsubscribe: () => {\n watchers[key] = watchers[key].filter(\n (cb: AtomPartialChangeCallback) => cb !== callback\n );\n },\n } as EventSubscription;\n },\n get defaultValue(): Value {\n return this.default;\n },\n get value(): Value {\n return this.currentValue;\n },\n change<T extends keyof Value>(key: T, newValue: any) {\n this.update({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n silentChange<T extends keyof Value>(key: T, newValue: any) {\n this.silentUpdate({\n ...this.currentValue,\n [key]: newValue,\n });\n },\n merge(newValue: Partial<Value>) {\n this.update({\n ...this.currentValue,\n ...newValue,\n });\n },\n update(newValue: (oldValue: Value, atom: Atom<Value, Actions>) => Value) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = newValue(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n events.trigger(event(\"update\"), this.currentValue, oldValue, this);\n if (atomValueIsObject) {\n for (const key in watchers) {\n const keyOldValue = get(oldValue, key);\n const keyNewValue = get(updatedValue, key);\n\n if (keyOldValue !== keyNewValue) {\n watchers[key].forEach(\n (callback: AtomPartialChangeCallback) =>\n callback(keyNewValue, keyOldValue, this)\n );\n }\n }\n }\n },\n silentUpdate(\n newValue: ((oldValue: Value, atom: Atom<Value>) => Value) | Value\n ) {\n if (newValue === this.currentValue) return;\n\n const oldValue = this.currentValue;\n let updatedValue: Value;\n\n if (typeof newValue === \"function\") {\n updatedValue = (newValue as any)(oldValue, this);\n } else {\n updatedValue = newValue;\n }\n\n if (data.beforeUpdate) {\n const beforeUpdateOutput = data.beforeUpdate(\n updatedValue,\n oldValue,\n this\n );\n\n if (beforeUpdateOutput !== undefined) {\n updatedValue = beforeUpdateOutput;\n }\n }\n\n this.currentValue = updatedValue;\n },\n onChange(\n callback: (newValue: Value, oldValue: Value, atom: Atom<Value>) => void\n ): EventSubscription {\n return events.subscribe(event(\"update\"), callback);\n },\n onReset(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(event(\"reset\"), callback);\n },\n get<T extends keyof Value>(key: T, defaultValue?: any): Value[T] {\n if (data.get) {\n return data.get(\n key as string,\n defaultValue,\n this.currentValue\n ) as Value[T];\n }\n\n return get(this.currentValue, key as string, defaultValue);\n },\n destroy() {\n events.trigger(event(\"delete\"), this);\n\n events.unsubscribeNamespace(atomEvent);\n delete atoms[this.key];\n },\n onDestroy(callback: (atom: Atom<Value>) => void): EventSubscription {\n return events.subscribe(`atoms.${this.key}.delete`, callback);\n },\n reset() {\n this.update(clone(this.defaultValue));\n events.trigger(event(\"reset\"), this);\n },\n /**\n * Reset the value without triggering the update event\n * But this will trigger the reset event\n */\n silentReset() {\n this.currentValue = clone(this.defaultValue);\n events.trigger(event(\"reset\"), this);\n },\n clone(cloneOptions?: CreateAtomOptions) {\n return createAtom(\n {\n key: this.key + \".clone.\" + (++cloneCounter),\n default: clone(this.currentValue),\n beforeUpdate: data.beforeUpdate,\n get: data.get,\n onUpdate: data.onUpdate,\n actions: data.actions,\n },\n { register: cloneOptions?.register ?? true }\n );\n },\n } as any;\n\n // Install actions on the atom instance.\n //\n // Three kinds of entries can appear in `actions`:\n // 1. Plain functions — bound to the atom so `this` refers to it.\n // 2. Property getters (e.g. `atomCollection`'s `length`) — forwarded\n // as getters bound to the atom; calling `.bind(...)` on them would\n // blow up because the getter is invoked the moment we touch it.\n // 3. Anything else — assigned by value as a fallback.\n if (data.actions) {\n const actions = data.actions as Record<string, unknown>;\n for (const actionKey of Object.keys(actions)) {\n const descriptor = Object.getOwnPropertyDescriptor(actions, actionKey);\n if (descriptor?.get) {\n Object.defineProperty(atom, actionKey, {\n get: descriptor.get.bind(atom),\n set: descriptor.set ? descriptor.set.bind(atom) : undefined,\n enumerable: true,\n configurable: true,\n });\n continue;\n }\n const value = actions[actionKey];\n if (typeof value === \"function\") {\n (atom as any)[actionKey] = (value as Function).bind(atom);\n } else {\n (atom as any)[actionKey] = value;\n }\n }\n }\n\n if (data.onUpdate) {\n events.subscribe(event(\"update\"), data.onUpdate.bind(atom));\n }\n\n if (options.register !== false) {\n atoms[atomKey] = atom;\n }\n\n // Persistence wiring. Resolve the adapter (boolean → built-in\n // localStorage, object → as-is, falsy → skip). The adapter handles\n // the initial read and the write-through; we just hand it the atom.\n const adapter = resolvePersistAdapter(data.persist);\n if (adapter) {\n attachPersist(atom, adapter, data);\n }\n\n return atom;\n}\n\n/**\n * Get all atoms list\n */\nexport function atomsList(): Atom<any>[] {\n return Object.values(atoms);\n}\n\n/**\n * Return atoms in object format\n */\nexport function atomsObject(): Record<string, Atom<any>> {\n return atoms;\n}\n"],"mappings":"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"} |
+1
-1
| import { Atom } from "./types.mjs"; | ||
| //#region ../@mongez/atom/src/derive.d.ts | ||
| //#region ../atom/src/derive.d.ts | ||
| /** | ||
@@ -5,0 +5,0 @@ * The reader passed to a derive compute function. Calling `get(atom)` |
+1
-1
| import { createAtom } from "./atom.mjs"; | ||
| //#region ../@mongez/atom/src/derive.ts | ||
| //#region ../atom/src/derive.ts | ||
| /** | ||
@@ -5,0 +5,0 @@ * Create a derived atom. |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"derive.mjs","names":[],"sources":["../../../../@mongez/atom/src/derive.ts"],"sourcesContent":["/**\n * @fileoverview Derived atoms.\n *\n * A derived atom holds a value computed from one or more other atoms.\n * Dependencies are auto-tracked: whichever atoms the compute function\n * reads via the `get` argument become dependencies. When any of those\n * change, the derived value recomputes and notifies its subscribers.\n *\n * Conceptually similar to Jotai's `atom(get => ...)` and MobX's\n * `computed`. Returns a normal `Atom<T>`, so every consumer pattern in\n * `@mongez/react-atom` (useValue, useState, watch, onChange, …) works.\n *\n * @example\n * ```ts\n * const first = createAtom({ key: \"first\", default: \"Ada\" });\n * const last = createAtom({ key: \"last\", default: \"Lovelace\" });\n *\n * const fullName = derive(\"fullName\", get => `${get(first)} ${get(last)}`);\n *\n * fullName.value; // \"Ada Lovelace\"\n * first.update(\"Grace\");\n * fullName.value; // \"Grace Lovelace\"\n * ```\n */\nimport { type EventSubscription } from \"@mongez/events\";\nimport { createAtom } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * The reader passed to a derive compute function. Calling `get(atom)`\n * registers that atom as a dependency and returns its current value.\n */\nexport type DeriveGetter = <V>(atom: Atom<V, any>) => V;\n\nexport type DeriveOptions = {\n /**\n * Skip the global `atoms` registry. Used by `AtomStore` clones — most\n * consumers should leave this alone.\n * @default true\n */\n register?: boolean;\n};\n\n/**\n * Create a derived atom.\n *\n * The compute function runs once eagerly on creation to seed the initial\n * value and to discover dependencies. After that, it re-runs every time\n * any tracked dependency changes.\n *\n * Conditional reads work: an `if` branch inside the compute function\n * that reads a different atom on a later run picks up the new dep and\n * drops the old one. This handles the \"dynamic dependency graph\" case\n * (e.g. `if (get(currentRoute) === \"users\") return get(usersAtom)`).\n *\n * Calling `update`, `silentUpdate`, `change`, `merge` directly on the\n * returned atom works but is discouraged — the next dependency change\n * will overwrite anything you wrote. Use a regular atom if you need\n * writable state.\n */\nexport function derive<T>(\n key: string,\n compute: (get: DeriveGetter) => T,\n options: DeriveOptions = {},\n): Atom<T> {\n /**\n * Active subscriptions to dependencies, keyed by the source atom.\n * Replaced wholesale on each recompute so dynamic dependency graphs\n * don't accumulate stale subscriptions.\n */\n let depSubs = new Map<Atom<any>, EventSubscription>();\n\n /**\n * A scratch set used during a recompute to mark which atoms were\n * touched on this run. After compute finishes we diff against\n * `depSubs`, drop stale ones, and add new ones.\n */\n let trackedThisRun: Set<Atom<any>> | undefined;\n\n const trackingGet: DeriveGetter = atom => {\n if (trackedThisRun) trackedThisRun.add(atom);\n return atom.value;\n };\n\n /**\n * Recompute the derived value and reconcile the dependency set.\n * Updates the derived atom via the normal `update` flow so all\n * downstream subscribers see the change.\n *\n * Errors thrown inside `compute` are caught and re-thrown\n * asynchronously: we don't want a single broken derivation to take\n * down the atom-bus subscriber. The atom's previous value is kept.\n */\n const recompute = () => {\n trackedThisRun = new Set();\n let next: T;\n try {\n next = compute(trackingGet);\n } catch (err) {\n trackedThisRun = undefined;\n // Surface the error without breaking the source-atom's update cycle.\n queueMicrotask(() => {\n throw err;\n });\n return;\n }\n\n // Reconcile: subscribe to newly-seen deps, drop deps no longer read.\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n for (const dep of seen) {\n if (!depSubs.has(dep)) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n }\n for (const [dep, sub] of depSubs) {\n if (!seen.has(dep)) {\n sub.unsubscribe();\n depSubs.delete(dep);\n }\n }\n\n // Push the new value through the standard update path. If the value\n // is structurally unchanged the atom's update() will short-circuit\n // for primitives; for objects we always send a new reference\n // because `compute` builds one each call.\n derivedAtom.update(next);\n };\n\n // Bootstrap: compute the initial value and the initial dep set.\n trackedThisRun = new Set();\n const initialValue = compute(trackingGet);\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n const derivedAtom = createAtom<T>(\n {\n key,\n default: initialValue,\n },\n { register: options.register !== false },\n );\n\n // Wire up dependency subscriptions now that the derived atom exists.\n for (const dep of seen) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n\n // Tear down dependency subs when the atom is destroyed so they don't\n // outlive the consumer and leak memory.\n derivedAtom.onDestroy(() => {\n for (const sub of depSubs.values()) sub.unsubscribe();\n depSubs.clear();\n });\n\n return derivedAtom;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4DA,SAAgB,OACd,KACA,SACA,UAAyB,CAAC,GACjB;;;;;;CAMT,IAAI,0BAAU,IAAI,IAAkC;;;;;;CAOpD,IAAI;CAEJ,MAAM,eAA4B,SAAQ;EACxC,IAAI,gBAAgB,eAAe,IAAI,IAAI;EAC3C,OAAO,KAAK;CACd;;;;;;;;;;CAWA,MAAM,kBAAkB;EACtB,iCAAiB,IAAI,IAAI;EACzB,IAAI;EACJ,IAAI;GACF,OAAO,QAAQ,WAAW;EAC5B,SAAS,KAAK;GACZ,iBAAiB;GAEjB,qBAAqB;IACnB,MAAM;GACR,CAAC;GACD;EACF;EAGA,MAAM,OAAO;EACb,iBAAiB;EAEjB,KAAK,MAAM,OAAO,MAChB,IAAI,CAAC,QAAQ,IAAI,GAAG,GAClB,QAAQ,IAAI,KAAK,IAAI,SAAS,SAAS,CAAC;EAG5C,KAAK,MAAM,CAAC,KAAK,QAAQ,SACvB,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG;GAClB,IAAI,YAAY;GAChB,QAAQ,OAAO,GAAG;EACpB;EAOF,YAAY,OAAO,IAAI;CACzB;CAGA,iCAAiB,IAAI,IAAI;CACzB,MAAM,eAAe,QAAQ,WAAW;CACxC,MAAM,OAAO;CACb,iBAAiB;CAEjB,MAAM,cAAc,WAClB;EACE;EACA,SAAS;CACX,GACA,EAAE,UAAU,QAAQ,aAAa,MAAM,CACzC;CAGA,KAAK,MAAM,OAAO,MAChB,QAAQ,IAAI,KAAK,IAAI,SAAS,SAAS,CAAC;CAK1C,YAAY,gBAAgB;EAC1B,KAAK,MAAM,OAAO,QAAQ,OAAO,GAAG,IAAI,YAAY;EACpD,QAAQ,MAAM;CAChB,CAAC;CAED,OAAO;AACT"} | ||
| {"version":3,"file":"derive.mjs","names":[],"sources":["../../../../../../atom/src/derive.ts"],"sourcesContent":["/**\n * @fileoverview Derived atoms.\n *\n * A derived atom holds a value computed from one or more other atoms.\n * Dependencies are auto-tracked: whichever atoms the compute function\n * reads via the `get` argument become dependencies. When any of those\n * change, the derived value recomputes and notifies its subscribers.\n *\n * Conceptually similar to Jotai's `atom(get => ...)` and MobX's\n * `computed`. Returns a normal `Atom<T>`, so every consumer pattern in\n * `@mongez/react-atom` (useValue, useState, watch, onChange, …) works.\n *\n * @example\n * ```ts\n * const first = createAtom({ key: \"first\", default: \"Ada\" });\n * const last = createAtom({ key: \"last\", default: \"Lovelace\" });\n *\n * const fullName = derive(\"fullName\", get => `${get(first)} ${get(last)}`);\n *\n * fullName.value; // \"Ada Lovelace\"\n * first.update(\"Grace\");\n * fullName.value; // \"Grace Lovelace\"\n * ```\n */\nimport { type EventSubscription } from \"@mongez/events\";\nimport { createAtom } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * The reader passed to a derive compute function. Calling `get(atom)`\n * registers that atom as a dependency and returns its current value.\n */\nexport type DeriveGetter = <V>(atom: Atom<V, any>) => V;\n\nexport type DeriveOptions = {\n /**\n * Skip the global `atoms` registry. Used by `AtomStore` clones — most\n * consumers should leave this alone.\n * @default true\n */\n register?: boolean;\n};\n\n/**\n * Create a derived atom.\n *\n * The compute function runs once eagerly on creation to seed the initial\n * value and to discover dependencies. After that, it re-runs every time\n * any tracked dependency changes.\n *\n * Conditional reads work: an `if` branch inside the compute function\n * that reads a different atom on a later run picks up the new dep and\n * drops the old one. This handles the \"dynamic dependency graph\" case\n * (e.g. `if (get(currentRoute) === \"users\") return get(usersAtom)`).\n *\n * Calling `update`, `silentUpdate`, `change`, `merge` directly on the\n * returned atom works but is discouraged — the next dependency change\n * will overwrite anything you wrote. Use a regular atom if you need\n * writable state.\n */\nexport function derive<T>(\n key: string,\n compute: (get: DeriveGetter) => T,\n options: DeriveOptions = {},\n): Atom<T> {\n /**\n * Active subscriptions to dependencies, keyed by the source atom.\n * Replaced wholesale on each recompute so dynamic dependency graphs\n * don't accumulate stale subscriptions.\n */\n let depSubs = new Map<Atom<any>, EventSubscription>();\n\n /**\n * A scratch set used during a recompute to mark which atoms were\n * touched on this run. After compute finishes we diff against\n * `depSubs`, drop stale ones, and add new ones.\n */\n let trackedThisRun: Set<Atom<any>> | undefined;\n\n const trackingGet: DeriveGetter = atom => {\n if (trackedThisRun) trackedThisRun.add(atom);\n return atom.value;\n };\n\n /**\n * Recompute the derived value and reconcile the dependency set.\n * Updates the derived atom via the normal `update` flow so all\n * downstream subscribers see the change.\n *\n * Errors thrown inside `compute` are caught and re-thrown\n * asynchronously: we don't want a single broken derivation to take\n * down the atom-bus subscriber. The atom's previous value is kept.\n */\n const recompute = () => {\n trackedThisRun = new Set();\n let next: T;\n try {\n next = compute(trackingGet);\n } catch (err) {\n trackedThisRun = undefined;\n // Surface the error without breaking the source-atom's update cycle.\n queueMicrotask(() => {\n throw err;\n });\n return;\n }\n\n // Reconcile: subscribe to newly-seen deps, drop deps no longer read.\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n for (const dep of seen) {\n if (!depSubs.has(dep)) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n }\n for (const [dep, sub] of depSubs) {\n if (!seen.has(dep)) {\n sub.unsubscribe();\n depSubs.delete(dep);\n }\n }\n\n // Push the new value through the standard update path. If the value\n // is structurally unchanged the atom's update() will short-circuit\n // for primitives; for objects we always send a new reference\n // because `compute` builds one each call.\n derivedAtom.update(next);\n };\n\n // Bootstrap: compute the initial value and the initial dep set.\n trackedThisRun = new Set();\n const initialValue = compute(trackingGet);\n const seen = trackedThisRun;\n trackedThisRun = undefined;\n\n const derivedAtom = createAtom<T>(\n {\n key,\n default: initialValue,\n },\n { register: options.register !== false },\n );\n\n // Wire up dependency subscriptions now that the derived atom exists.\n for (const dep of seen) {\n depSubs.set(dep, dep.onChange(recompute));\n }\n\n // Tear down dependency subs when the atom is destroyed so they don't\n // outlive the consumer and leak memory.\n derivedAtom.onDestroy(() => {\n for (const sub of depSubs.values()) sub.unsubscribe();\n depSubs.clear();\n });\n\n return derivedAtom;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AA4DA,SAAgB,OACd,KACA,SACA,UAAyB,CAAC,GACjB;;;;;;CAMT,IAAI,0BAAU,IAAI,IAAkC;;;;;;CAOpD,IAAI;CAEJ,MAAM,eAA4B,SAAQ;EACxC,IAAI,gBAAgB,eAAe,IAAI,IAAI;EAC3C,OAAO,KAAK;CACd;;;;;;;;;;CAWA,MAAM,kBAAkB;EACtB,iCAAiB,IAAI,IAAI;EACzB,IAAI;EACJ,IAAI;GACF,OAAO,QAAQ,WAAW;EAC5B,SAAS,KAAK;GACZ,iBAAiB;GAEjB,qBAAqB;IACnB,MAAM;GACR,CAAC;GACD;EACF;EAGA,MAAM,OAAO;EACb,iBAAiB;EAEjB,KAAK,MAAM,OAAO,MAChB,IAAI,CAAC,QAAQ,IAAI,GAAG,GAClB,QAAQ,IAAI,KAAK,IAAI,SAAS,SAAS,CAAC;EAG5C,KAAK,MAAM,CAAC,KAAK,QAAQ,SACvB,IAAI,CAAC,KAAK,IAAI,GAAG,GAAG;GAClB,IAAI,YAAY;GAChB,QAAQ,OAAO,GAAG;EACpB;EAOF,YAAY,OAAO,IAAI;CACzB;CAGA,iCAAiB,IAAI,IAAI;CACzB,MAAM,eAAe,QAAQ,WAAW;CACxC,MAAM,OAAO;CACb,iBAAiB;CAEjB,MAAM,cAAc,WAClB;EACE;EACA,SAAS;CACX,GACA,EAAE,UAAU,QAAQ,aAAa,MAAM,CACzC;CAGA,KAAK,MAAM,OAAO,MAChB,QAAQ,IAAI,KAAK,IAAI,SAAS,SAAS,CAAC;CAK1C,YAAY,gBAAgB;EAC1B,KAAK,MAAM,OAAO,QAAQ,OAAO,GAAG,IAAI,YAAY;EACpD,QAAQ,MAAM;CAChB,CAAC;CAED,OAAO;AACT"} |
@@ -1,2 +0,2 @@ | ||
| //#region ../@mongez/atom/src/devtools.d.ts | ||
| //#region ../atom/src/devtools.d.ts | ||
| type EnableDevtoolsOptions = { | ||
@@ -3,0 +3,0 @@ /** Label shown in the extension UI. */name?: string; |
+1
-1
| import { atoms } from "./atom.mjs"; | ||
| import events from "@mongez/events"; | ||
| //#region ../@mongez/atom/src/devtools.ts | ||
| //#region ../atom/src/devtools.ts | ||
| /** | ||
@@ -6,0 +6,0 @@ * @fileoverview Redux DevTools bridge for `@mongez/atom`. |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"devtools.mjs","names":[],"sources":["../../../../@mongez/atom/src/devtools.ts"],"sourcesContent":["/**\n * @fileoverview Redux DevTools bridge for `@mongez/atom`.\n *\n * Opt-in, browser-only, zero-cost when not enabled (tree-shaken if you\n * never import `enableAtomDevtools`).\n *\n * Pipes every atom update into the Redux DevTools extension so you get:\n *\n * - A live list of every registered atom and its current value.\n * - A timeline of updates with diffs.\n * - Time-travel: jumping back in the timeline restores the matching\n * state via `silentUpdate` on every atom.\n *\n * @example\n * ```ts\n * // app entry, dev only\n * if (process.env.NODE_ENV !== \"production\") {\n * enableAtomDevtools({ name: \"MyApp\" });\n * }\n * ```\n */\nimport events from \"@mongez/events\";\nimport { atoms } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * Subset of the Redux DevTools instance API we use. Typed locally so we\n * don't need to take a dep on `@redux-devtools/extension`.\n */\ntype DevtoolsInstance = {\n init(state: unknown): void;\n send(action: { type: string; payload?: unknown }, state: unknown): void;\n subscribe(\n listener: (message: {\n type: string;\n payload?: { type?: string };\n state?: string;\n }) => void,\n ): () => void;\n disconnect?(): void;\n};\n\ntype DevtoolsExtension = {\n connect(options?: {\n name?: string;\n features?: Record<string, unknown>;\n }): DevtoolsInstance;\n};\n\nexport type EnableDevtoolsOptions = {\n /** Label shown in the extension UI. */\n name?: string;\n /**\n * Skip atoms whose key matches any of these patterns. Useful for\n * silencing high-frequency atoms (mouse position, scroll, etc.) that\n * would otherwise spam the timeline.\n */\n ignore?: Array<RegExp | string>;\n /**\n * How often (ms) to look for newly-registered atoms. Apps that\n * register every atom at startup never need this; the default of\n * 1000ms is fine for hot-reload and code-splitting cases.\n * @default 1000\n */\n scanInterval?: number;\n};\n\n/**\n * Connect every registered atom to the Redux DevTools extension.\n *\n * Returns a teardown function that disconnects and stops the registry\n * scan. Safe to call when the extension isn't installed — it returns\n * a no-op teardown without doing any work.\n */\nexport function enableAtomDevtools(\n options: EnableDevtoolsOptions = {},\n): () => void {\n const win =\n typeof window !== \"undefined\" ? (window as unknown as Window & {\n __REDUX_DEVTOOLS_EXTENSION__?: DevtoolsExtension;\n }) : undefined;\n\n const ext = win?.__REDUX_DEVTOOLS_EXTENSION__;\n if (!ext) return () => {};\n\n const devtools = ext.connect({\n name: options.name ?? \"@mongez/atom\",\n });\n\n const ignored = (key: string): boolean =>\n !!options.ignore?.some(pat =>\n typeof pat === \"string\" ? pat === key : pat.test(key),\n );\n\n const snapshot = (): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [k, a] of Object.entries(atoms)) {\n if (!ignored(k)) out[k] = a.value;\n }\n return out;\n };\n\n devtools.init(snapshot());\n\n // Per-atom subscriptions we own (so we can tear them down).\n const subscribed = new Map<string, () => void>();\n\n const subscribeAtom = (atom: Atom<any>) => {\n if (ignored(atom.key)) return;\n if (subscribed.has(atom.key)) return;\n const sub = atom.onChange(newValue => {\n devtools.send(\n { type: `${atom.key}/update`, payload: newValue },\n snapshot(),\n );\n });\n const onResetSub = atom.onReset(() => {\n devtools.send({ type: `${atom.key}/reset` }, snapshot());\n });\n const onDestroySub = atom.onDestroy(() => {\n devtools.send({ type: `${atom.key}/destroy` }, snapshot());\n // Drop our own subscription bookkeeping.\n subscribed.delete(atom.key);\n });\n subscribed.set(atom.key, () => {\n sub.unsubscribe();\n onResetSub.unsubscribe();\n onDestroySub.unsubscribe();\n });\n };\n\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n\n // Pick up atoms registered AFTER enableAtomDevtools fires. Apps that\n // import all their atoms at boot will never trigger this; lazy-loaded\n // routes that register atoms on demand will. Poll-based for simplicity.\n const interval = options.scanInterval ?? 1000;\n const scanTimer: ReturnType<typeof setInterval> = setInterval(() => {\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n }, interval);\n\n // Time-travel: when the user jumps to a snapshot in the extension,\n // restore every atom's value via silentUpdate so consumers see the\n // restored state on the next read.\n const unsubDevtools = devtools.subscribe(message => {\n if (message.type !== \"DISPATCH\") return;\n const payloadType = message.payload?.type;\n if (\n payloadType !== \"JUMP_TO_STATE\" &&\n payloadType !== \"JUMP_TO_ACTION\"\n ) {\n return;\n }\n if (!message.state) return;\n let restored: Record<string, unknown>;\n try {\n restored = JSON.parse(message.state);\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(restored)) {\n const atom = atoms[key];\n if (!atom) continue;\n atom.silentUpdate(value);\n // Synthesise an update event so React subscribers re-render.\n events.trigger(`atoms.${key}.update`, value, atom.currentValue, atom);\n }\n });\n\n return () => {\n clearInterval(scanTimer);\n for (const unsub of subscribed.values()) unsub();\n subscribed.clear();\n unsubDevtools();\n devtools.disconnect?.();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,mBACd,UAAiC,CAAC,GACtB;CAMZ,MAAM,OAJJ,OAAO,WAAW,cAAe,SAE5B,SAEU;CACjB,IAAI,CAAC,KAAK,aAAa,CAAC;CAExB,MAAM,WAAW,IAAI,QAAQ,EAC3B,MAAM,QAAQ,QAAQ,eACxB,CAAC;CAED,MAAM,WAAW,QACf,CAAC,CAAC,QAAQ,QAAQ,MAAK,QACrB,OAAO,QAAQ,WAAW,QAAQ,MAAM,IAAI,KAAK,GAAG,CACtD;CAEF,MAAM,iBAA0C;EAC9C,MAAM,MAA+B,CAAC;EACtC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,GACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,KAAK,EAAE;EAE9B,OAAO;CACT;CAEA,SAAS,KAAK,SAAS,CAAC;CAGxB,MAAM,6BAAa,IAAI,IAAwB;CAE/C,MAAM,iBAAiB,SAAoB;EACzC,IAAI,QAAQ,KAAK,GAAG,GAAG;EACvB,IAAI,WAAW,IAAI,KAAK,GAAG,GAAG;EAC9B,MAAM,MAAM,KAAK,UAAS,aAAY;GACpC,SAAS,KACP;IAAE,MAAM,GAAG,KAAK,IAAI;IAAU,SAAS;GAAS,GAChD,SAAS,CACX;EACF,CAAC;EACD,MAAM,aAAa,KAAK,cAAc;GACpC,SAAS,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,GAAG,SAAS,CAAC;EACzD,CAAC;EACD,MAAM,eAAe,KAAK,gBAAgB;GACxC,SAAS,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,GAAG,SAAS,CAAC;GAEzD,WAAW,OAAO,KAAK,GAAG;EAC5B,CAAC;EACD,WAAW,IAAI,KAAK,WAAW;GAC7B,IAAI,YAAY;GAChB,WAAW,YAAY;GACvB,aAAa,YAAY;EAC3B,CAAC;CACH;CAEA,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK,GAAG,cAAc,IAAI;CAK3D,MAAM,WAAW,QAAQ,gBAAgB;CACzC,MAAM,YAA4C,kBAAkB;EAClE,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK,GAAG,cAAc,IAAI;CAC7D,GAAG,QAAQ;CAKX,MAAM,gBAAgB,SAAS,WAAU,YAAW;EAClD,IAAI,QAAQ,SAAS,YAAY;EACjC,MAAM,cAAc,QAAQ,SAAS;EACrC,IACE,gBAAgB,mBAChB,gBAAgB,kBAEhB;EAEF,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI;EACJ,IAAI;GACF,WAAW,KAAK,MAAM,QAAQ,KAAK;EACrC,QAAQ;GACN;EACF;EACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAAG;GACnD,MAAM,OAAO,MAAM;GACnB,IAAI,CAAC,MAAM;GACX,KAAK,aAAa,KAAK;GAEvB,OAAO,QAAQ,SAAS,IAAI,UAAU,OAAO,KAAK,cAAc,IAAI;EACtE;CACF,CAAC;CAED,aAAa;EACX,cAAc,SAAS;EACvB,KAAK,MAAM,SAAS,WAAW,OAAO,GAAG,MAAM;EAC/C,WAAW,MAAM;EACjB,cAAc;EACd,SAAS,aAAa;CACxB;AACF"} | ||
| {"version":3,"file":"devtools.mjs","names":[],"sources":["../../../../../../atom/src/devtools.ts"],"sourcesContent":["/**\n * @fileoverview Redux DevTools bridge for `@mongez/atom`.\n *\n * Opt-in, browser-only, zero-cost when not enabled (tree-shaken if you\n * never import `enableAtomDevtools`).\n *\n * Pipes every atom update into the Redux DevTools extension so you get:\n *\n * - A live list of every registered atom and its current value.\n * - A timeline of updates with diffs.\n * - Time-travel: jumping back in the timeline restores the matching\n * state via `silentUpdate` on every atom.\n *\n * @example\n * ```ts\n * // app entry, dev only\n * if (process.env.NODE_ENV !== \"production\") {\n * enableAtomDevtools({ name: \"MyApp\" });\n * }\n * ```\n */\nimport events from \"@mongez/events\";\nimport { atoms } from \"./atom\";\nimport type { Atom } from \"./types\";\n\n/**\n * Subset of the Redux DevTools instance API we use. Typed locally so we\n * don't need to take a dep on `@redux-devtools/extension`.\n */\ntype DevtoolsInstance = {\n init(state: unknown): void;\n send(action: { type: string; payload?: unknown }, state: unknown): void;\n subscribe(\n listener: (message: {\n type: string;\n payload?: { type?: string };\n state?: string;\n }) => void,\n ): () => void;\n disconnect?(): void;\n};\n\ntype DevtoolsExtension = {\n connect(options?: {\n name?: string;\n features?: Record<string, unknown>;\n }): DevtoolsInstance;\n};\n\nexport type EnableDevtoolsOptions = {\n /** Label shown in the extension UI. */\n name?: string;\n /**\n * Skip atoms whose key matches any of these patterns. Useful for\n * silencing high-frequency atoms (mouse position, scroll, etc.) that\n * would otherwise spam the timeline.\n */\n ignore?: Array<RegExp | string>;\n /**\n * How often (ms) to look for newly-registered atoms. Apps that\n * register every atom at startup never need this; the default of\n * 1000ms is fine for hot-reload and code-splitting cases.\n * @default 1000\n */\n scanInterval?: number;\n};\n\n/**\n * Connect every registered atom to the Redux DevTools extension.\n *\n * Returns a teardown function that disconnects and stops the registry\n * scan. Safe to call when the extension isn't installed — it returns\n * a no-op teardown without doing any work.\n */\nexport function enableAtomDevtools(\n options: EnableDevtoolsOptions = {},\n): () => void {\n const win =\n typeof window !== \"undefined\" ? (window as unknown as Window & {\n __REDUX_DEVTOOLS_EXTENSION__?: DevtoolsExtension;\n }) : undefined;\n\n const ext = win?.__REDUX_DEVTOOLS_EXTENSION__;\n if (!ext) return () => {};\n\n const devtools = ext.connect({\n name: options.name ?? \"@mongez/atom\",\n });\n\n const ignored = (key: string): boolean =>\n !!options.ignore?.some(pat =>\n typeof pat === \"string\" ? pat === key : pat.test(key),\n );\n\n const snapshot = (): Record<string, unknown> => {\n const out: Record<string, unknown> = {};\n for (const [k, a] of Object.entries(atoms)) {\n if (!ignored(k)) out[k] = a.value;\n }\n return out;\n };\n\n devtools.init(snapshot());\n\n // Per-atom subscriptions we own (so we can tear them down).\n const subscribed = new Map<string, () => void>();\n\n const subscribeAtom = (atom: Atom<any>) => {\n if (ignored(atom.key)) return;\n if (subscribed.has(atom.key)) return;\n const sub = atom.onChange(newValue => {\n devtools.send(\n { type: `${atom.key}/update`, payload: newValue },\n snapshot(),\n );\n });\n const onResetSub = atom.onReset(() => {\n devtools.send({ type: `${atom.key}/reset` }, snapshot());\n });\n const onDestroySub = atom.onDestroy(() => {\n devtools.send({ type: `${atom.key}/destroy` }, snapshot());\n // Drop our own subscription bookkeeping.\n subscribed.delete(atom.key);\n });\n subscribed.set(atom.key, () => {\n sub.unsubscribe();\n onResetSub.unsubscribe();\n onDestroySub.unsubscribe();\n });\n };\n\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n\n // Pick up atoms registered AFTER enableAtomDevtools fires. Apps that\n // import all their atoms at boot will never trigger this; lazy-loaded\n // routes that register atoms on demand will. Poll-based for simplicity.\n const interval = options.scanInterval ?? 1000;\n const scanTimer: ReturnType<typeof setInterval> = setInterval(() => {\n for (const atom of Object.values(atoms)) subscribeAtom(atom);\n }, interval);\n\n // Time-travel: when the user jumps to a snapshot in the extension,\n // restore every atom's value via silentUpdate so consumers see the\n // restored state on the next read.\n const unsubDevtools = devtools.subscribe(message => {\n if (message.type !== \"DISPATCH\") return;\n const payloadType = message.payload?.type;\n if (\n payloadType !== \"JUMP_TO_STATE\" &&\n payloadType !== \"JUMP_TO_ACTION\"\n ) {\n return;\n }\n if (!message.state) return;\n let restored: Record<string, unknown>;\n try {\n restored = JSON.parse(message.state);\n } catch {\n return;\n }\n for (const [key, value] of Object.entries(restored)) {\n const atom = atoms[key];\n if (!atom) continue;\n atom.silentUpdate(value);\n // Synthesise an update event so React subscribers re-render.\n events.trigger(`atoms.${key}.update`, value, atom.currentValue, atom);\n }\n });\n\n return () => {\n clearInterval(scanTimer);\n for (const unsub of subscribed.values()) unsub();\n subscribed.clear();\n unsubDevtools();\n devtools.disconnect?.();\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0EA,SAAgB,mBACd,UAAiC,CAAC,GACtB;CAMZ,MAAM,OAJJ,OAAO,WAAW,cAAe,SAE5B,OAEQ,EAAE;CACjB,IAAI,CAAC,KAAK,aAAa,CAAC;CAExB,MAAM,WAAW,IAAI,QAAQ,EAC3B,MAAM,QAAQ,QAAQ,eACxB,CAAC;CAED,MAAM,WAAW,QACf,CAAC,CAAC,QAAQ,QAAQ,MAAK,QACrB,OAAO,QAAQ,WAAW,QAAQ,MAAM,IAAI,KAAK,GAAG,CACtD;CAEF,MAAM,iBAA0C;EAC9C,MAAM,MAA+B,CAAC;EACtC,KAAK,MAAM,CAAC,GAAG,MAAM,OAAO,QAAQ,KAAK,GACvC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,KAAK,EAAE;EAE9B,OAAO;CACT;CAEA,SAAS,KAAK,SAAS,CAAC;CAGxB,MAAM,6BAAa,IAAI,IAAwB;CAE/C,MAAM,iBAAiB,SAAoB;EACzC,IAAI,QAAQ,KAAK,GAAG,GAAG;EACvB,IAAI,WAAW,IAAI,KAAK,GAAG,GAAG;EAC9B,MAAM,MAAM,KAAK,UAAS,aAAY;GACpC,SAAS,KACP;IAAE,MAAM,GAAG,KAAK,IAAI;IAAU,SAAS;GAAS,GAChD,SAAS,CACX;EACF,CAAC;EACD,MAAM,aAAa,KAAK,cAAc;GACpC,SAAS,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,QAAQ,GAAG,SAAS,CAAC;EACzD,CAAC;EACD,MAAM,eAAe,KAAK,gBAAgB;GACxC,SAAS,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,UAAU,GAAG,SAAS,CAAC;GAEzD,WAAW,OAAO,KAAK,GAAG;EAC5B,CAAC;EACD,WAAW,IAAI,KAAK,WAAW;GAC7B,IAAI,YAAY;GAChB,WAAW,YAAY;GACvB,aAAa,YAAY;EAC3B,CAAC;CACH;CAEA,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK,GAAG,cAAc,IAAI;CAK3D,MAAM,WAAW,QAAQ,gBAAgB;CACzC,MAAM,YAA4C,kBAAkB;EAClE,KAAK,MAAM,QAAQ,OAAO,OAAO,KAAK,GAAG,cAAc,IAAI;CAC7D,GAAG,QAAQ;CAKX,MAAM,gBAAgB,SAAS,WAAU,YAAW;EAClD,IAAI,QAAQ,SAAS,YAAY;EACjC,MAAM,cAAc,QAAQ,SAAS;EACrC,IACE,gBAAgB,mBAChB,gBAAgB,kBAEhB;EAEF,IAAI,CAAC,QAAQ,OAAO;EACpB,IAAI;EACJ,IAAI;GACF,WAAW,KAAK,MAAM,QAAQ,KAAK;EACrC,QAAQ;GACN;EACF;EACA,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,GAAG;GACnD,MAAM,OAAO,MAAM;GACnB,IAAI,CAAC,MAAM;GACX,KAAK,aAAa,KAAK;GAEvB,OAAO,QAAQ,SAAS,IAAI,UAAU,OAAO,KAAK,cAAc,IAAI;EACtE;CACF,CAAC;CAED,aAAa;EACX,cAAc,SAAS;EACvB,KAAK,MAAM,SAAS,WAAW,OAAO,GAAG,MAAM;EAC/C,WAAW,MAAM;EACjB,cAAc;EACd,SAAS,aAAa;CACxB;AACF"} |
| import { Atom, AtomOptions } from "./types.mjs"; | ||
| //#region ../@mongez/atom/src/persist.d.ts | ||
| //#region ../atom/src/persist.d.ts | ||
| /** | ||
@@ -5,0 +5,0 @@ * Shape of an external store. Methods may be sync or async; the |
+1
-1
@@ -1,2 +0,2 @@ | ||
| //#region ../@mongez/atom/src/persist.ts | ||
| //#region ../atom/src/persist.ts | ||
| /** | ||
@@ -3,0 +3,0 @@ * Built-in adapter backed by `window.localStorage`. JSON-encodes the |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"persist.mjs","names":[],"sources":["../../../../@mongez/atom/src/persist.ts"],"sourcesContent":["/**\n * @fileoverview Persistence adapters for atoms.\n *\n * Plug in any store-shaped object (cache, localStorage wrapper, cookie\n * helper, IndexedDB layer) and atoms will:\n *\n * 1. Load their initial value from the adapter at creation (sync or async).\n * 2. Write every subsequent update through to the adapter.\n * 3. Remove the entry on `reset()`.\n *\n * The default adapter is a thin localStorage wrapper for the client; it\n * silently no-ops on the server (no `window`). For SSR-safe per-request\n * persistence, supply your own cookie-aware adapter.\n */\nimport type { Atom, AtomOptions } from \"./types\";\n\n/**\n * Shape of an external store. Methods may be sync or async; the\n * persistence layer handles both transparently.\n */\nexport type PersistAdapter<V = unknown> = {\n /** Read the persisted value for `key`. `undefined` means \"not present\". */\n get(key: string): V | undefined | Promise<V | undefined>;\n /** Write `value` to the store under `key`. */\n set(key: string, value: V): void | Promise<void>;\n /** Drop the entry for `key`. Called on `reset()`. */\n remove(key: string): void | Promise<void>;\n};\n\n/**\n * The shape that goes on `AtomOptions.persist`.\n *\n * - `true` → use the built-in localStorage adapter (client-only).\n * - `false` / omitted → no persistence.\n * - Any object matching `PersistAdapter` → use that adapter.\n */\nexport type PersistOption<V = unknown> =\n | boolean\n | PersistAdapter<V>;\n\n/**\n * Built-in adapter backed by `window.localStorage`. JSON-encodes the\n * value on write, decodes on read. No-ops on the server.\n */\nexport const localStorageAdapter: PersistAdapter = {\n get(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const raw = window.localStorage.getItem(key);\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n // Corrupt entry — pretend it doesn't exist.\n return undefined;\n }\n },\n set(key, value) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // QuotaExceededError or private-mode storage block — silently drop.\n }\n },\n remove(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n window.localStorage.removeItem(key);\n },\n};\n\n/**\n * Resolve a `PersistOption` to an actual adapter, or `undefined` if\n * persistence is disabled.\n */\nexport function resolvePersistAdapter<V>(\n option: PersistOption<V> | undefined,\n): PersistAdapter<V> | undefined {\n if (!option) return undefined;\n if (option === true) return localStorageAdapter as PersistAdapter<V>;\n return option;\n}\n\n/**\n * Wire an atom up to a persistence adapter.\n *\n * On creation, asynchronously reads the stored value and applies it via\n * `silentUpdate` (so subscribers see it on next render but no `update`\n * event fires). On every update afterwards, writes through to the\n * adapter. On `reset`, removes the entry.\n *\n * This is internal — `createAtom` calls it when `options.persist` is\n * truthy. Consumers don't call it directly.\n */\nexport function attachPersist<V, A extends Record<string, any>>(\n atom: Atom<V, A>,\n adapter: PersistAdapter<V>,\n options: AtomOptions<V, any>,\n): void {\n // Bootstrap: read the stored value. We don't block the constructor on\n // an async adapter — the consumer sees the default until the read\n // resolves, then a silentUpdate flips the value in place.\n // Both sync throws and async rejections are caught so a broken\n // adapter never crashes atom creation.\n try {\n const stored = adapter.get(atom.key);\n if (stored instanceof Promise) {\n stored\n .then(value => {\n if (value !== undefined) atom.silentUpdate(value);\n })\n .catch(() => {\n /* keep default */\n });\n } else if (stored !== undefined) {\n atom.silentUpdate(stored);\n }\n } catch {\n /* sync throw on read — keep default */\n }\n\n // Write-through on every update. Using onChange instead of replacing\n // beforeUpdate so we don't fight the user's own beforeUpdate hook.\n // Sync throws are swallowed so a transient storage error (quota,\n // private-mode block, etc.) doesn't break the consumer's update flow;\n // async rejections are caught the same way.\n const updateSub = atom.onChange(newValue => {\n try {\n const result = adapter.set(atom.key, newValue);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* adapter blew up — keep going */\n }\n });\n\n // Drop the entry on reset so the next session starts fresh.\n const resetSub = atom.onReset(() => {\n try {\n const result = adapter.remove(atom.key);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* same — non-fatal */\n }\n });\n\n // Clean up subscriptions when the atom dies.\n atom.onDestroy(() => {\n updateSub.unsubscribe();\n resetSub.unsubscribe();\n });\n\n // Suppress unused-options lint when not consumed by future logic.\n void options;\n}\n"],"mappings":";;;;;AA4CA,MAAa,sBAAsC;CACjD,IAAI,KAAK;EACP,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc,OAAO;EAClE,MAAM,MAAM,OAAO,aAAa,QAAQ,GAAG;EAC3C,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI;GACF,OAAO,KAAK,MAAM,GAAG;EACvB,QAAQ;GAEN;EACF;CACF;CACA,IAAI,KAAK,OAAO;EACd,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,IAAI;GACF,OAAO,aAAa,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;EACxD,QAAQ,CAER;CACF;CACA,OAAO,KAAK;EACV,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,OAAO,aAAa,WAAW,GAAG;CACpC;AACF;;;;;AAMA,SAAgB,sBACd,QAC+B;CAC/B,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,cACd,MACA,SACA,SACM;CAMN,IAAI;EACF,MAAM,SAAS,QAAQ,IAAI,KAAK,GAAG;EACnC,IAAI,kBAAkB,SACpB,OACG,MAAK,UAAS;GACb,IAAI,UAAU,QAAW,KAAK,aAAa,KAAK;EAClD,CAAC,EACA,YAAY,CAEb,CAAC;OACE,IAAI,WAAW,QACpB,KAAK,aAAa,MAAM;CAE5B,QAAQ,CAER;CAOA,MAAM,YAAY,KAAK,UAAS,aAAY;EAC1C,IAAI;GACF,MAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,QAAQ;GAC7C,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,MAAM,WAAW,KAAK,cAAc;EAClC,IAAI;GACF,MAAM,SAAS,QAAQ,OAAO,KAAK,GAAG;GACtC,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,KAAK,gBAAgB;EACnB,UAAU,YAAY;EACtB,SAAS,YAAY;CACvB,CAAC;AAIH"} | ||
| {"version":3,"file":"persist.mjs","names":[],"sources":["../../../../../../atom/src/persist.ts"],"sourcesContent":["/**\n * @fileoverview Persistence adapters for atoms.\n *\n * Plug in any store-shaped object (cache, localStorage wrapper, cookie\n * helper, IndexedDB layer) and atoms will:\n *\n * 1. Load their initial value from the adapter at creation (sync or async).\n * 2. Write every subsequent update through to the adapter.\n * 3. Remove the entry on `reset()`.\n *\n * The default adapter is a thin localStorage wrapper for the client; it\n * silently no-ops on the server (no `window`). For SSR-safe per-request\n * persistence, supply your own cookie-aware adapter.\n */\nimport type { Atom, AtomOptions } from \"./types\";\n\n/**\n * Shape of an external store. Methods may be sync or async; the\n * persistence layer handles both transparently.\n */\nexport type PersistAdapter<V = unknown> = {\n /** Read the persisted value for `key`. `undefined` means \"not present\". */\n get(key: string): V | undefined | Promise<V | undefined>;\n /** Write `value` to the store under `key`. */\n set(key: string, value: V): void | Promise<void>;\n /** Drop the entry for `key`. Called on `reset()`. */\n remove(key: string): void | Promise<void>;\n};\n\n/**\n * The shape that goes on `AtomOptions.persist`.\n *\n * - `true` → use the built-in localStorage adapter (client-only).\n * - `false` / omitted → no persistence.\n * - Any object matching `PersistAdapter` → use that adapter.\n */\nexport type PersistOption<V = unknown> =\n | boolean\n | PersistAdapter<V>;\n\n/**\n * Built-in adapter backed by `window.localStorage`. JSON-encodes the\n * value on write, decodes on read. No-ops on the server.\n */\nexport const localStorageAdapter: PersistAdapter = {\n get(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return undefined;\n const raw = window.localStorage.getItem(key);\n if (raw === null) return undefined;\n try {\n return JSON.parse(raw);\n } catch {\n // Corrupt entry — pretend it doesn't exist.\n return undefined;\n }\n },\n set(key, value) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n try {\n window.localStorage.setItem(key, JSON.stringify(value));\n } catch {\n // QuotaExceededError or private-mode storage block — silently drop.\n }\n },\n remove(key) {\n if (typeof window === \"undefined\" || !window.localStorage) return;\n window.localStorage.removeItem(key);\n },\n};\n\n/**\n * Resolve a `PersistOption` to an actual adapter, or `undefined` if\n * persistence is disabled.\n */\nexport function resolvePersistAdapter<V>(\n option: PersistOption<V> | undefined,\n): PersistAdapter<V> | undefined {\n if (!option) return undefined;\n if (option === true) return localStorageAdapter as PersistAdapter<V>;\n return option;\n}\n\n/**\n * Wire an atom up to a persistence adapter.\n *\n * On creation, asynchronously reads the stored value and applies it via\n * `silentUpdate` (so subscribers see it on next render but no `update`\n * event fires). On every update afterwards, writes through to the\n * adapter. On `reset`, removes the entry.\n *\n * This is internal — `createAtom` calls it when `options.persist` is\n * truthy. Consumers don't call it directly.\n */\nexport function attachPersist<V, A extends Record<string, any>>(\n atom: Atom<V, A>,\n adapter: PersistAdapter<V>,\n options: AtomOptions<V, any>,\n): void {\n // Bootstrap: read the stored value. We don't block the constructor on\n // an async adapter — the consumer sees the default until the read\n // resolves, then a silentUpdate flips the value in place.\n // Both sync throws and async rejections are caught so a broken\n // adapter never crashes atom creation.\n try {\n const stored = adapter.get(atom.key);\n if (stored instanceof Promise) {\n stored\n .then(value => {\n if (value !== undefined) atom.silentUpdate(value);\n })\n .catch(() => {\n /* keep default */\n });\n } else if (stored !== undefined) {\n atom.silentUpdate(stored);\n }\n } catch {\n /* sync throw on read — keep default */\n }\n\n // Write-through on every update. Using onChange instead of replacing\n // beforeUpdate so we don't fight the user's own beforeUpdate hook.\n // Sync throws are swallowed so a transient storage error (quota,\n // private-mode block, etc.) doesn't break the consumer's update flow;\n // async rejections are caught the same way.\n const updateSub = atom.onChange(newValue => {\n try {\n const result = adapter.set(atom.key, newValue);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* adapter blew up — keep going */\n }\n });\n\n // Drop the entry on reset so the next session starts fresh.\n const resetSub = atom.onReset(() => {\n try {\n const result = adapter.remove(atom.key);\n if (result instanceof Promise) result.catch(() => {});\n } catch {\n /* same — non-fatal */\n }\n });\n\n // Clean up subscriptions when the atom dies.\n atom.onDestroy(() => {\n updateSub.unsubscribe();\n resetSub.unsubscribe();\n });\n\n // Suppress unused-options lint when not consumed by future logic.\n void options;\n}\n"],"mappings":";;;;;AA4CA,MAAa,sBAAsC;CACjD,IAAI,KAAK;EACP,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc,OAAO;EAClE,MAAM,MAAM,OAAO,aAAa,QAAQ,GAAG;EAC3C,IAAI,QAAQ,MAAM,OAAO;EACzB,IAAI;GACF,OAAO,KAAK,MAAM,GAAG;EACvB,QAAQ;GAEN;EACF;CACF;CACA,IAAI,KAAK,OAAO;EACd,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,IAAI;GACF,OAAO,aAAa,QAAQ,KAAK,KAAK,UAAU,KAAK,CAAC;EACxD,QAAQ,CAER;CACF;CACA,OAAO,KAAK;EACV,IAAI,OAAO,WAAW,eAAe,CAAC,OAAO,cAAc;EAC3D,OAAO,aAAa,WAAW,GAAG;CACpC;AACF;;;;;AAMA,SAAgB,sBACd,QAC+B;CAC/B,IAAI,CAAC,QAAQ,OAAO;CACpB,IAAI,WAAW,MAAM,OAAO;CAC5B,OAAO;AACT;;;;;;;;;;;;AAaA,SAAgB,cACd,MACA,SACA,SACM;CAMN,IAAI;EACF,MAAM,SAAS,QAAQ,IAAI,KAAK,GAAG;EACnC,IAAI,kBAAkB,SACpB,OACG,MAAK,UAAS;GACb,IAAI,UAAU,QAAW,KAAK,aAAa,KAAK;EAClD,CAAC,CAAC,CACD,YAAY,CAEb,CAAC;OACE,IAAI,WAAW,QACpB,KAAK,aAAa,MAAM;CAE5B,QAAQ,CAER;CAOA,MAAM,YAAY,KAAK,UAAS,aAAY;EAC1C,IAAI;GACF,MAAM,SAAS,QAAQ,IAAI,KAAK,KAAK,QAAQ;GAC7C,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,MAAM,WAAW,KAAK,cAAc;EAClC,IAAI;GACF,MAAM,SAAS,QAAQ,OAAO,KAAK,GAAG;GACtC,IAAI,kBAAkB,SAAS,OAAO,YAAY,CAAC,CAAC;EACtD,QAAQ,CAER;CACF,CAAC;CAGD,KAAK,gBAAgB;EACnB,UAAU,YAAY;EACtB,SAAS,YAAY;CACvB,CAAC;AAIH"} |
+1
-1
| import { PersistOption } from "./persist.mjs"; | ||
| import { EventSubscription } from "@mongez/events"; | ||
| //#region ../@mongez/atom/src/types.d.ts | ||
| //#region ../atom/src/types.d.ts | ||
| type AtomPartialChangeCallback = (newValue: any, oldValue: any, atom: Atom<any>) => void; | ||
@@ -6,0 +6,0 @@ type AtomValue<Value> = Value; |
+11
-11
| { | ||
| "name": "@mongez/atom", | ||
| "description": "An agnostic state management tool that work with any framework on browser or server", | ||
| "author": "hassanzohdy", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/hassanzohdy/atom" | ||
| }, | ||
| "license": "MIT", | ||
| "dependencies": { | ||
| "@mongez/events": "^2.2.0", | ||
| "@mongez/reinforcements": "^3.1.0" | ||
| }, | ||
| "keywords": [ | ||
@@ -21,13 +31,3 @@ "react", | ||
| ], | ||
| "author": "hassanzohdy", | ||
| "license": "MIT", | ||
| "repository": { | ||
| "type": "git", | ||
| "url": "https://github.com/hassanzohdy/atom" | ||
| }, | ||
| "dependencies": { | ||
| "@mongez/events": "^2.2.0", | ||
| "@mongez/reinforcements": "^3.1.0" | ||
| }, | ||
| "version": "6.0.9", | ||
| "version": "6.0.10", | ||
| "main": "./cjs/index.cjs", | ||
@@ -34,0 +34,0 @@ "module": "./esm/index.mjs", |
@@ -5,4 +5,2 @@ --- | ||
| How to define function actions, property getters, and plain values in the `actions` bag passed to `createAtom` or `atomCollection`, with `this` bound to the atom instance. | ||
| TRIGGER when: code defines an `actions` object on `createAtom` or `atomCollection`, uses `ThisType<Atom<V, A>>`, or declares `get total()` / `get isEmpty()` style getters; user asks "how do I add methods to an atom", "how do I use `this` inside an action", or "how do I add a computed getter"; file uses `this.update(...)` / `this.merge(...)` inside a `createAtom` actions block. | ||
| SKIP: defining the atom itself (use `mongez-atom-defining-atoms` or `mongez-atom-atoms`); array-specialized verbs like `push`/`pop`/`remove` (use `mongez-atom-collections`); React hook patterns like `useValue` / `useState` (those live in `@mongez/react-atom`, not this package). | ||
| --- | ||
@@ -9,0 +7,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to use `AtomStore` and `createAtomStore` for per-request SSR isolation — creating scoped atom clones, hydrating snapshots, and tearing down stores after each request. | ||
| TRIGGER when: code imports `AtomStore`, `createAtomStore`, or calls `store.use`, `store.get`, `store.has`, `store.list`, `store.hydrate`, `store.snapshot`, `store.destroy` from `@mongez/atom`; user asks "how do I isolate atom state per SSR request", "why are atoms leaking between requests", or "how do I serialize and rehydrate atoms"; `import { createAtomStore, AtomStore } from "@mongez/atom"`. | ||
| SKIP: defining the atoms themselves (use `mongez-atom-atoms` or `mongez-atom-defining-atoms`); React-side `AtomStoreProvider` / `useAtomStore` wiring (lives in `@mongez/react-atom`); generic client-only state without SSR (no store needed). | ||
| --- | ||
@@ -12,11 +10,2 @@ | ||
| ## When to use | ||
| Load this skill when the user: | ||
| - Runs SSR (Next.js, Remix, Express + React, Fastify) and shares atom state between requests | ||
| - Uses `AtomStore` or `createAtomStore` from `@mongez/atom` | ||
| - Asks why two concurrent requests overwrite each other's atoms | ||
| - Needs to serialize server-side atom state and send it to the client for hydration | ||
| - Is wiring up `<AtomStoreProvider>` from `@mongez/react-atom` | ||
| ## The problem AtomStore solves | ||
@@ -23,0 +12,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| Full reference for `createAtom` — signature, base methods, object-only methods, lifecycle events, registry helpers, and usage examples. | ||
| TRIGGER when: code imports or calls `createAtom`, `getAtom`, `atomsList`, `atomsObject`, or uses `atom.update`, `atom.silentUpdate`, `atom.merge`, `atom.change`, `atom.silentChange`, `atom.watch`, `atom.reset`, `atom.silentReset`, `atom.onChange`, `atom.onReset`, `atom.onDestroy`, `atom.clone`, `atom.destroy`, `beforeUpdate`; user asks "how do I define an atom", "what methods does an atom have", or "how do I subscribe to atom changes"; `import { createAtom } from "@mongez/atom"`. | ||
| SKIP: array-typed atom mutation verbs (use `mongez-atom-collections`); computed atoms (use `mongez-atom-derived` / `mongez-atom-derived-atoms`); attaching custom methods via `actions` bag (use `mongez-atom-actions`); SSR isolation (use `mongez-atom-atom-store` / `mongez-atom-stores`); persistence (use `mongez-atom-persist` / `mongez-atom-persistence`); React hooks (live in `@mongez/react-atom`). | ||
| --- | ||
@@ -28,2 +26,3 @@ | ||
| get?: (key: string, defaultValue?: V, atomValue?: V) => V; | ||
| persist?: PersistOption<V>; | ||
| }; | ||
@@ -30,0 +29,0 @@ ``` |
@@ -5,4 +5,2 @@ --- | ||
| How to use `atomCollection` to manage array-typed atoms with built-in mutation verbs like `push`, `pop`, `remove`, `map`, and `replace`. | ||
| TRIGGER when: code imports or calls `atomCollection`, or invokes `push`, `unshift`, `pop`, `shift`, `replace`, `remove`, `removeItem`, `removeAll`, `map`, `forEach`, `index`, `get(indexOrPredicate)`, `length` on an atom, or uses `AtomCollectionActions` / `CollectionOptions` types; user asks "how do I manage an array as atom state", "how do I push/pop/remove items in an atom", or "what's the difference between createAtom and atomCollection"; `import { atomCollection } from "@mongez/atom"`. | ||
| SKIP: scalar/object atoms (use `mongez-atom-atoms` or `mongez-atom-defining-atoms`); computed array views over collections (use `mongez-atom-derived` / `mongez-atom-derived-atoms`); React-list rendering hooks (live in `@mongez/react-atom`). | ||
| --- | ||
@@ -9,0 +7,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to create atoms with `createAtom` and `atomCollection` — options, actions, typing, object helpers, and the built-in mutation API. | ||
| TRIGGER when: code imports or calls `createAtom` or `atomCollection`, uses `Atom<V>` / `AtomOptions` generics, or invokes `merge`, `change`, `silentChange`, `silentUpdate`, `beforeUpdate`, `watch`, `onChange`, `onReset`, `clone`, `destroy`; user asks "how do I create an atom", "how do I type an atom", "how do I add actions/methods to my atom", or "how do I subscribe to atom changes"; `import { createAtom, atomCollection } from "@mongez/atom"`. | ||
| SKIP: deep reference of every base method (use `mongez-atom-atoms` for full surface); array verb specifics (use `mongez-atom-collections`); computed atoms (use `mongez-atom-derived` / `mongez-atom-derived-atoms`); SSR stores (use `mongez-atom-atom-store` / `mongez-atom-stores`); persistence (use `mongez-atom-persist` / `mongez-atom-persistence`); React hooks (live in `@mongez/react-atom`). | ||
| --- | ||
@@ -12,11 +10,2 @@ | ||
| ## When to use | ||
| Load this skill when the user is: | ||
| - Creating a new atom with `createAtom` or `atomCollection` | ||
| - Adding typed actions to an atom | ||
| - Using `merge`, `change`, `watch`, `silentUpdate`, or `beforeUpdate` | ||
| - Subscribing to changes with `onChange` or `onReset` | ||
| - Working with `reset`, `destroy`, or `clone` | ||
| ## Install | ||
@@ -23,0 +12,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to create computed atoms with `derive()` — auto-tracked dependencies, conditional reads, chained derivations, and cleanup. | ||
| TRIGGER when: code imports or calls `derive`, uses `DeriveGetter` / `DeriveOptions` types, or builds a value from other atoms via a `get` argument; user asks "how do I create a computed atom that updates with its sources", "how do I auto-track dependencies", or "how do I clean up a derived atom"; `import { derive } from "@mongez/atom"`. | ||
| SKIP: writable base atoms (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); array verbs over a collection (use `mongez-atom-collections`); React-side hook integration (lives in `@mongez/react-atom`); the sibling `mongez-atom-derived` skill — only one of the two should fire for the same request. | ||
| --- | ||
@@ -12,10 +10,2 @@ | ||
| ## When to use | ||
| Load this skill when the user: | ||
| - Needs a value that is computed from one or more other atoms | ||
| - Uses `derive()` from `@mongez/atom` | ||
| - Asks about auto-tracked dependencies (Jotai/MobX-computed style) | ||
| - Needs a derived value that works with React hooks in `@mongez/react-atom` | ||
| ## What derive() does | ||
@@ -22,0 +12,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to create computed atoms with `derive()` — auto-tracked dependencies, dynamic dep graphs, chaining, and React consumption. | ||
| TRIGGER when: code imports or calls `derive`, uses `DeriveGetter` / `DeriveOptions` types, or builds a value from other atoms via a `get` argument; user asks "how do I create a computed/derived atom", "how do I auto-track atom dependencies", or "how do I chain derived atoms"; `import { derive } from "@mongez/atom"`. | ||
| SKIP: writable base atoms (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); array verbs over a collection (use `mongez-atom-collections`); React-side `useValue` / `useState` wiring (lives in `@mongez/react-atom`); the sibling `mongez-atom-derived-atoms` skill — only one of the two should fire for the same request. | ||
| --- | ||
@@ -9,0 +7,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to enable Redux DevTools integration for `@mongez/atom` — setup, options, time-travel, and filtering high-frequency atoms. | ||
| TRIGGER when: code imports or calls `enableAtomDevtools`, uses `EnableDevtoolsOptions`, or passes `name` / `ignore` / `scanInterval` options for DevTools; user asks "how do I debug atoms with Redux DevTools", "how do I time-travel atom state", or "how do I skip noisy atoms in the DevTools timeline"; `import { enableAtomDevtools } from "@mongez/atom"`. | ||
| SKIP: defining atoms (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); production-only / non-debug code paths; logging atom values to console without the Redux extension; React-renderer profiling (DevTools handles state, React Profiler handles renders). | ||
| --- | ||
@@ -9,0 +7,0 @@ |
+88
-70
| --- | ||
| name: mongez-atom-overview | ||
| description: | | ||
| Mental model, exports, and decision guide for `@mongez/atom` — the framework-agnostic state primitive at the core of the Mongez state family. | ||
| TRIGGER when: code first imports anything from `@mongez/atom` (`createAtom`, `atomCollection`, `derive`, `AtomStore`, `createAtomStore`, `enableAtomDevtools`, `getAtom`, `atomsList`, `atomsObject`); user asks "what is @mongez/atom", "which package should I use for state — @mongez/atom or @mongez/react-atom", "what does @mongez/atom export", or "give me a high-level architecture of Mongez state"; `import { ... } from "@mongez/atom"` with no specific topic yet identified. | ||
| SKIP: any deep how-to question that already maps to a focused skill (`mongez-atom-atoms`, `mongez-atom-collections`, `mongez-atom-derived`, `mongez-atom-persist`, `mongez-atom-atom-store`, `mongez-atom-devtools`, `mongez-atom-actions`, `mongez-atom-recipes`); React-specific hook questions (`@mongez/react-atom`); server-state caching (`@mongez/atomic-query`). | ||
| Mental model, exports, and decision guide for @mongez/atom — the framework-agnostic state primitive at the core of the Mongez state family. Atoms bundle a typed value with action methods, support derived values, persistence, SSR isolation, and Redux DevTools time-travel. | ||
| --- | ||
@@ -11,66 +9,97 @@ | ||
| ## When to use | ||
| Framework-agnostic state, the way it should be. An atom isn't just a value — it's a value with **action methods bound to it**. Call domain verbs (`cartAtom.push(item)`, `authAtom.login(creds)`, `sidebarAtom.toggle()`) instead of writing free-standing setters everywhere. Works in any JS/TS environment — React, Vue, Node, vanilla. | ||
| Load this skill when the user: | ||
| - Is new to `@mongez/atom` and needs orientation | ||
| - Asks "which package should I use for state?" | ||
| - Asks about lifecycle events, the global registry, or DevTools wiring | ||
| - Needs to understand the relationship between `@mongez/atom`, `@mongez/react-atom`, and `@mongez/atomic-query` | ||
| ## Highlighted features | ||
| ## Mental model | ||
| <div class="mongez-highlights"> | ||
| An **atom** is not just a value — it is a value bundled with methods (actions) that mutate it. Instead of writing free-standing setter helpers, you define verbs directly on the atom: | ||
| <div class="mongez-highlight" data-accent="ice"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="3"/><circle cx="12" cy="12" r="10"/></svg> | ||
| <h3>Values with verbs</h3> | ||
| <p>Action methods live on the atom — <code>sidebar.toggle()</code>, not <code>setSidebar(!sidebar.value)</code>. Bound <code>this</code>, named intent.</p> | ||
| </div> | ||
| ```ts | ||
| sidebar.toggle(); // not: setSidebar(!sidebar.value) | ||
| cart.push(item); // not: setCart([...cart.value, item]) | ||
| auth.login(creds); // not: dispatch({ type: "AUTH_LOGIN", payload: creds }) | ||
| ``` | ||
| <div class="mongez-highlight" data-accent="ice"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"/></svg> | ||
| <h3>Derived values, auto-tracked</h3> | ||
| <p><code>derive("fullName", get => `${get(first)} ${get(last)}`)</code> — dependency graph rebuilt on each read, conditional reads work, chained derives propagate.</p> | ||
| </div> | ||
| All atoms live in a module-level registry (`atoms` object exported from the package). Each atom is keyed by the `key` string passed to `createAtom`. Keys should be namespaced with dots: `"ui.sidebar"`, `"cart"`, `"user.profile"`. | ||
| <div class="mongez-highlight" data-accent="fire"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg> | ||
| <h3>Persistence built in</h3> | ||
| <p><code>persist: true</code> for localStorage, or any custom <code>PersistAdapter</code> (cookies, IndexedDB, <code>@mongez/cache</code>, …). Restore on construction, write on every update.</p> | ||
| </div> | ||
| ## Package hierarchy | ||
| <div class="mongez-highlight" data-accent="fire"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="2" y="3" width="20" height="14" rx="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg> | ||
| <h3>SSR-isolated stores</h3> | ||
| <p><code>AtomStore</code> + <code>createAtomStore</code> for per-request isolation. Module-level singletons stay safe; per-user state stays scoped.</p> | ||
| </div> | ||
| | Package | Role | | ||
| |---|---| | ||
| | `@mongez/atom` | Core. Framework-agnostic atom factory, SSR isolation, persistence, DevTools. Use in any JS/TS environment. | | ||
| | `@mongez/react-atom` | React adapter. Wraps `@mongez/atom` with hooks (`useAtom`, `useValue`, `useState`), `<AtomStoreProvider>`, and SSR hydration helpers. Use in React apps. | | ||
| | `@mongez/atomic-query` | Server-state cache on top of atoms. `useQuery`, `useMutation`, `useInfiniteQuery`. Use for remote data fetching. | | ||
| <div class="mongez-highlight" data-accent="bolt"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg> | ||
| <h3>Redux DevTools time-travel</h3> | ||
| <p><code>enableAtomDevtools()</code> bridges to the Redux DevTools extension — full action history + jump-to-state. Tree-shaken when never imported.</p> | ||
| </div> | ||
| **Rule of thumb**: `@mongez/atom` for shared, UI-independent logic. `@mongez/react-atom` for anything that drives component re-renders. `@mongez/atomic-query` for async server data. | ||
| <div class="mongez-highlight" data-accent="bolt"> | ||
| <svg class="mongez-highlight-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg> | ||
| <h3>Specialized factories</h3> | ||
| <p><code>atomCollection</code> for arrays (adds <code>push</code>/<code>pop</code>/<code>remove</code>/<code>map</code>), <code>derive</code> for computed values — the common shapes don't need rebuilding.</p> | ||
| </div> | ||
| ## Exports at a glance | ||
| </div> | ||
| ## Install | ||
| ```sh | ||
| npm install @mongez/atom | ||
| # or: yarn add @mongez/atom | ||
| # or: pnpm add @mongez/atom | ||
| ``` | ||
| Peer deps installed automatically: `@mongez/events`, `@mongez/reinforcements`. | ||
| ## Quick peek | ||
| ```ts | ||
| import { | ||
| // Core factory | ||
| createAtom, | ||
| import { createAtom } from "@mongez/atom"; | ||
| // Array-specialized factory (adds push/pop/remove/map/etc.) | ||
| atomCollection, | ||
| const sidebarAtom = createAtom({ | ||
| key: "ui.sidebar", | ||
| default: false, | ||
| actions: { | ||
| open() { this.update(true); }, | ||
| close() { this.update(false); }, | ||
| toggle() { this.update(!this.value); }, | ||
| }, | ||
| }); | ||
| // Computed/derived atom with auto-tracked dependencies | ||
| derive, | ||
| sidebarAtom.toggle(); // no setSidebar(!sidebar.value) ceremony | ||
| sidebarAtom.value; // true | ||
| ``` | ||
| // SSR per-request isolation | ||
| AtomStore, | ||
| createAtomStore, | ||
| Atoms aren't just values — they're values with verbs bound to them. Call domain methods directly on the atom instead of writing setters everywhere. **Naming convention:** suffix atom variables with `Atom` (e.g. `counterAtom`, `sidebarAtom`) to avoid clashes with component props. | ||
| // Redux DevTools bridge (browser-only, opt-in) | ||
| enableAtomDevtools, | ||
| ## Mental model | ||
| // Global registry helpers | ||
| getAtom, | ||
| atomsList, | ||
| atomsObject, | ||
| All atoms live in a module-level registry (`atoms` object exported from the package). Each atom is keyed by the `key` string passed to `createAtom`. Keys should be namespaced with dots: `"ui.sidebar"`, `"cart"`, `"user.profile"`. | ||
| // Types | ||
| type Atom, | ||
| type AtomOptions, | ||
| type AtomActions, | ||
| type PersistAdapter, | ||
| type PersistOption, | ||
| } from "@mongez/atom"; | ||
| ```ts | ||
| sidebarAtom.toggle(); // not: setSidebar(!sidebar.value) | ||
| cartAtom.push(item); // not: setCart([...cart.value, item]) | ||
| authAtom.login(creds); // not: dispatch({ type: "AUTH_LOGIN", payload: creds }) | ||
| ``` | ||
| ## Package hierarchy | ||
| | Package | Role | | ||
| |---|---| | ||
| | `@mongez/atom` | Core. Framework-agnostic atom factory, SSR isolation, persistence, DevTools. | | ||
| | [`@mongez/react-atom`](/react-atom/overview/) | React adapter. Per-atom hooks (`useValue`, `useState`, `use`), `<AtomStoreProvider>`, preset atoms. | | ||
| | [`@mongez/atomic-query`](/atomic-query/overview/) | Server-state cache on top of atoms. `useQuery`, `useMutation`, `useInfiniteQuery`. | | ||
| **Rule of thumb**: `@mongez/atom` for shared, UI-independent logic. `@mongez/react-atom` for anything that drives component re-renders. `@mongez/atomic-query` for async server data. | ||
| ## Lifecycle events | ||
@@ -88,26 +117,15 @@ | ||
| ## DevTools | ||
| ## Key pitfalls | ||
| ```ts | ||
| import { enableAtomDevtools } from "@mongez/atom"; | ||
| - **Key collisions are global.** If two `createAtom` calls share the same key, the second overwrites the first. Prefix keys by domain (`"ui.sidebar"`, not `"sidebar"`). | ||
| - **No reference equality shortcut for objects.** `update()` short-circuits only when the new value `=== currentValue`. For object atoms, always pass a new reference or use `merge()` / `change()`. | ||
| - **No React here.** `useAtom` lives in `@mongez/react-atom`, not this package. | ||
| // Call once at app entry, dev only. | ||
| if (process.env.NODE_ENV !== "production") { | ||
| enableAtomDevtools({ | ||
| name: "MyApp", | ||
| ignore: [/^mouse\./, /^scroll\./], // skip high-frequency atoms | ||
| scanInterval: 1000, // ms, default; picks up lazily registered atoms | ||
| }); | ||
| } | ||
| ``` | ||
| ## Where to go next | ||
| - Connects to `window.__REDUX_DEVTOOLS_EXTENSION__`. No-op when extension is absent. | ||
| - Tree-shaken when never imported. | ||
| - Time-travel via `JUMP_TO_STATE` restores all atoms via `silentUpdate`. | ||
| - Returns a teardown function. | ||
| ## Key pitfalls | ||
| - **Key collisions**: keys are global. If two `createAtom` calls share the same key, the second overwrites the first in the registry. Prefix keys by domain (`"ui.sidebar"`, not `"sidebar"`). | ||
| - **No reference equality shortcut for objects**: `update()` short-circuits only when the new value `=== currentValue`. For object atoms, always pass a new reference or use `merge()`/`change()`. | ||
| - `@mongez/atom` has no React — never import `useAtom` from it. That lives in `@mongez/react-atom`. | ||
| - **[Atoms](../atoms/)**, **[Defining atoms](../defining-atoms/)**, **[Actions](../actions/)** — the core API | ||
| - **[Derived atoms](../derived/)**, **[Atom collections](../collections/)** — specialised shapes | ||
| - **[Persistence](../persist/)** — `persist: true` and custom `PersistAdapter` | ||
| - **[Atom stores (SSR)](../atom-store/)** — per-request isolation | ||
| - **[Devtools](../devtools/)** — Redux DevTools time-travel | ||
| - **[Recipes](../recipes/)** — cross-feature compositions |
@@ -5,4 +5,2 @@ --- | ||
| How to persist atom values using the built-in `localStorageAdapter` or a custom `PersistAdapter` (cookies, IndexedDB, `@mongez/cache`, or any async store). | ||
| TRIGGER when: code sets `persist: true` or `persist: { get, set, remove }` on a `createAtom` call, imports `PersistAdapter`, `PersistOption`, `localStorageAdapter`, or `resolvePersistAdapter`; user asks "how do I save atom state across reloads", "how do I write a custom localStorage / cookie / IndexedDB adapter", or "why is my atom value lost on refresh"; `import { type PersistAdapter, localStorageAdapter } from "@mongez/atom"`. | ||
| SKIP: per-request SSR isolation (use `mongez-atom-atom-store` / `mongez-atom-stores`); defining the atom itself (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); server-state caching with HTTP keys (use `@mongez/atomic-query`); the sibling `mongez-atom-persistence` skill — only one of the two should fire for the same request. | ||
| --- | ||
@@ -9,0 +7,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| How to persist atom values across page loads using the built-in `localStorageAdapter` or a custom `PersistAdapter` (cookies, IndexedDB, any sync/async store). | ||
| TRIGGER when: code sets `persist: true` or `persist: customAdapter` on a `createAtom` call, imports `PersistAdapter`, `PersistOption`, or `localStorageAdapter`; user asks "how do I persist atom state to localStorage", "why doesn't persist work on the server", or "how do I write an SSR-safe cookie adapter"; `import { type PersistAdapter, localStorageAdapter } from "@mongez/atom"`. | ||
| SKIP: per-request SSR isolation (use `mongez-atom-atom-store` / `mongez-atom-stores`); defining the atom itself (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); cache-with-invalidation patterns (use `@mongez/atomic-query`); the sibling `mongez-atom-persist` skill — only one of the two should fire for the same request. | ||
| --- | ||
@@ -12,10 +10,2 @@ | ||
| ## When to use | ||
| Load this skill when the user: | ||
| - Wants atom values to survive page reloads | ||
| - Uses `persist: true` or `persist: myAdapter` in `AtomOptions` | ||
| - Needs to swap localStorage for a different store (IndexedDB, cookies, memory) | ||
| - Asks why `persist: true` doesn't work on the server (SSR/Node) | ||
| ## The persist option | ||
@@ -22,0 +12,0 @@ |
@@ -5,4 +5,2 @@ --- | ||
| Idiomatic composition recipes for `@mongez/atom` covering boolean toggles, cart totals, derived watch patterns, SSR hydration, DevTools teardown, and scratch atoms. | ||
| TRIGGER when: code combines several of `createAtom`, `atomCollection`, `derive`, `createAtomStore`, `enableAtomDevtools`, `onChange`, `watch` in one place; user asks "give me a real-world example", "show me an end-to-end SSR snapshot + hydrate pattern", "build a cart with computed totals", "how do I tear down `enableAtomDevtools` on HMR", or "how do I derive state into another atom via `onChange`"; `import { createAtom, atomCollection, derive, createAtomStore, enableAtomDevtools } from "@mongez/atom"` together. | ||
| SKIP: single-feature deep dives — route to the focused skill instead (`mongez-atom-atoms`, `mongez-atom-collections`, `mongez-atom-derived`, `mongez-atom-persist`, `mongez-atom-atom-store`, `mongez-atom-devtools`, `mongez-atom-actions`); React-specific composition (lives in `@mongez/react-atom`); query/cache patterns (use `@mongez/atomic-query`). | ||
| --- | ||
@@ -54,3 +52,3 @@ | ||
| ## Derived state via `watch` | ||
| ## Side effect via `onChange` | ||
@@ -57,0 +55,0 @@ When you need a side effect on every change (writing to another atom, logging, hitting an API) rather than a pure derivation, subscribe via `onChange`. For pure computed values, prefer `derive` (see `mongez-atom-derived`). |
@@ -5,4 +5,2 @@ --- | ||
| How to use `AtomStore` and `createAtomStore` to isolate per-request atom state in SSR environments and avoid cross-request state leaks. | ||
| TRIGGER when: code imports `AtomStore`, `createAtomStore`, or calls `store.use`, `store.get`, `store.has`, `store.list`, `store.hydrate`, `store.snapshot`, `store.destroy` from `@mongez/atom`; user asks "how do I scope atoms per request in Next.js / Express / Fastify", "how do I avoid SSR state leaks", or "how do I serialize server atom state and rehydrate on the client"; `import { createAtomStore, AtomStore } from "@mongez/atom"`. | ||
| SKIP: defining the template atoms themselves (use `mongez-atom-atoms` / `mongez-atom-defining-atoms`); React-side `AtomStoreProvider` / `useAtomStore` (lives in `@mongez/react-atom`); pure client-only apps with no SSR; the sibling `mongez-atom-atom-store` skill — only one of the two should fire for the same request. | ||
| --- | ||
@@ -9,0 +7,0 @@ |
| {"version":3,"file":"atom-collection.d.mts","names":[],"sources":["../../../../@mongez/atom/src/atom-collection.ts"],"mappings":";;;KAGY,eAAA,qBAEN,KAAA,EAAO,KAAA,EAAO,KAAA,UAAe,IAAA,EAAM,KAAK;AAAA,UAE7B,qBAAA,gBAAqC,WAAA,CAAY,KAAA;EAJtD;;;EAQV,IAAA,CAAK,IAAA,EAAM,IAAA,CAAK,KAAA,QAAa,KAAA,EAAO,KAAA;EARV;;;EAY1B,OAAA,CAAQ,IAAA,EAAM,IAAA,CAAK,KAAA,QAAa,KAAA,EAAO,KAAA;EAVA;;;EAcvC,GAAA,CAAI,IAAA,EAAM,IAAA,CAAK,KAAA;EAZA;;;EAgBf,KAAA,CAAM,IAAA,EAAM,IAAA,CAAK,KAAA;EAZD;;;EAgBhB,MAAA,CAAO,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,eAAA,EAAiB,eAAA,CAAgB,KAAA;EAZ/C;;;EAgBd,UAAA,CAAW,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,IAAA,EAAM,KAAA;EARrB;;;EAYjB,SAAA,CAAU,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,IAAA,EAAM,KAAA;EARwB;;;EAY7D,GAAA,CAAI,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,eAAA,EAAiB,eAAA,CAAgB,KAAA,IAAS,KAAA;EAR7B;;;EAYtC,KAAA,CACE,IAAA,EAAM,IAAA,CAAK,KAAA,KACX,QAAA,GAAW,IAAA,EAAM,KAAA,EAAO,KAAA,UAAe,KAAA,EAAO,KAAA;EANjC;;;;EAYf,GAAA,CACE,IAAA,EAAM,IAAA,CAAK,KAAA,KACX,QAAA,GAAW,IAAA,EAAM,KAAA,EAAO,KAAA,UAAe,KAAA,EAAO,KAAA,OAAY,KAAA,GACzD,KAAA;EAVU;;;EAcb,OAAA,CACE,IAAA,EAAM,IAAA,CAAK,KAAA,KACX,QAAA,GAAW,IAAA,EAAM,KAAA,EAAO,KAAA,UAAe,KAAA,EAAO,KAAA;EARnC;;;EAab,OAAA,CAAQ,IAAA,EAAM,IAAA,CAAK,KAAA,KAAU,KAAA,UAAe,IAAA,EAAM,KAAA;EAZU;;;EAgB5D,MAAA;AAAA;AAAA,KAGU,iBAAA,UAA2B,IAAA,CACrC,WAAA,CAAY,KAAA,IAAS,qBAAA,CAAsB,KAAA;EAG3C,OAAA,GAAU,KAAA;AAAA;;;;iBAMI,cAAA,aAAA,CACd,OAAA,EAAS,iBAAA,CAAkB,KAAA,IAC1B,IAAA,CAAK,KAAA,IAAS,qBAAA,CAAsB,KAAA"} |
| {"version":3,"file":"atom-store.d.mts","names":[],"sources":["../../../../@mongez/atom/src/atom-store.ts"],"mappings":";;;;;AAYA;;;;;;;;cAAa,SAAA;EAiBuD;;;EAAA,QAb1D,KAAA;EAsDU;;;;EAAA,QAhDV,aAAA;EAAA;;;;;EAOR,GAAA,cAAiB,MAAA,mBAAA,CAA0B,QAAA,EAAU,IAAA,CAAK,CAAA,EAAG,CAAA,IAAK,IAAA,CAAK,CAAA,EAAG,CAAA;EAAhB;;;;EAmB1D,GAAA,SAAA,CAAa,GAAA,WAAc,IAAA,CAAK,CAAA;EAnB0C;;;EA0B1E,GAAA,CAAI,GAAA;EAPuB;;;EAc3B,IAAA,CAAA,GAAQ,IAAA;EAAR;;;;EAQA,OAAA,CAAQ,QAAA,EAAU,MAAA;EAiBlB;;;;AAaO;EAbP,QAAA,CAAA,GAAY,MAAA;EAyBiB;;;AAAa;;EAZ1C,OAAA,CAAA;AAAA;;;;iBAYc,eAAA,CAAA,GAAmB,SAAS"} |
| {"version":3,"file":"atom.d.mts","names":[],"sources":["../../../../@mongez/atom/src/atom.ts"],"mappings":";;;cAea,KAAA,EAAO,MAAM,SAAS,IAAA;;AAAnC;;iBAOgB,OAAA,GAAA,CAAW,IAAA,WAAe,IAAI,CAAC,CAAA;;AAPR;AAOvC;;KAQY,iBAAA;EARkC;;;;;;AAAE;EAgB9C,QAAQ;AAAA;;;AAAA;iBAMM,UAAA,8BAEE,WAAA,CAAY,KAAA,IAAS,WAAA,CAAY,KAAA,EAAA,CAEjD,IAAA,EAAM,WAAA,CAAY,SAAA,CAAU,KAAA,GAAQ,OAAA,GACpC,OAAA,GAAS,iBAAA,GACR,IAAA,CAAK,KAAA,EAAO,OAAA;;;;iBAqPC,SAAA,CAAA,GAAa,IAAI;;;;iBAOjB,WAAA,CAAA,GAAe,MAAM,SAAS,IAAA"} |
| {"version":3,"file":"derive.d.mts","names":[],"sources":["../../../../@mongez/atom/src/derive.ts"],"mappings":";;;;;AAgCA;;KAAY,YAAA,OAAmB,IAAA,EAAM,IAAA,CAAK,CAAA,WAAY,CAAA;AAAA,KAE1C,aAAA;EAFyB;;;;;EAQnC,QAAQ;AAAA;;;;AAR6C;AAEvD;;;;AAMU;AAoBV;;;;;;;;iBAAgB,MAAA,GAAA,CACd,GAAA,UACA,OAAA,GAAU,GAAA,EAAK,YAAA,KAAiB,CAAA,EAChC,OAAA,GAAS,aAAA,GACR,IAAA,CAAK,CAAA"} |
| {"version":3,"file":"devtools.d.mts","names":[],"sources":["../../../../@mongez/atom/src/devtools.ts"],"mappings":";KAiDY,qBAAA;EAAA,uCAEV,IAAA;;;;;;EAMA,MAAA,GAAS,KAAK,CAAC,MAAA;EAOf;;AAAY;AAUd;;;EAVE,YAAA;AAAA;;;;;;;;iBAUc,kBAAA,CACd,OAAmC,GAA1B,qBAA0B"} |
| {"version":3,"file":"persist.d.mts","names":[],"sources":["../../../../@mongez/atom/src/persist.ts"],"mappings":";;;;;;;KAoBY,cAAA;EAIV,2EAFA,GAAA,CAAI,GAAA,WAAc,CAAA,eAAgB,OAAA,CAAQ,CAAA,eAElB;EAAxB,GAAA,CAAI,GAAA,UAAa,KAAA,EAAO,CAAA,UAAW,OAAA,QAAA;EAEnC,MAAA,CAAO,GAAA,kBAAqB,OAAA;AAAA;;;AAAO;AAUrC;;;;KAAY,aAAA,0BAER,cAAc,CAAC,CAAA;;;;AAAC;cAMP,mBAAA,EAAqB,cAwBjC;;;;AAAA;iBAMe,qBAAA,GAAA,CACd,MAAA,EAAQ,aAAA,CAAc,CAAA,gBACrB,cAAA,CAAe,CAAA;;;;;;;;;;;;iBAiBF,aAAA,cAA2B,MAAA,cAAA,CACzC,IAAA,EAAM,IAAA,CAAK,CAAA,EAAG,CAAA,GACd,OAAA,EAAS,cAAA,CAAe,CAAA,GACxB,OAAA,EAAS,WAAA,CAAY,CAAA"} |
| {"version":3,"file":"types.d.mts","names":[],"sources":["../../../../@mongez/atom/src/types.ts"],"mappings":"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"} |
260981
-3.01%44
-12%