@kdeveloper/kvark 0.2.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -39,7 +39,7 @@ import { Provider, useAtomValue, useSetAtom } from "@kdeveloper/kvark/react";
39
39
  const userIdAtom = atom({
40
40
  debugLabel: "userId",
41
41
  get: async () => 1,
42
- set: async (_ctx, id: number) => id,
42
+ set: async (_ctx, _id: number) => {},
43
43
  });
44
44
 
45
45
  const userAtom = atom({
@@ -86,7 +86,7 @@ import { atom } from "@kdeveloper/kvark";
86
86
  const countAtom = atom({
87
87
  debugLabel: "count",
88
88
  get: async () => 0,
89
- set: async (_ctx, value: number) => value,
89
+ set: async (_ctx, _value: number) => {},
90
90
  });
91
91
 
92
92
  // Derived atom — reads from another atom
@@ -100,6 +100,69 @@ const doubleAtom = atom({
100
100
  });
101
101
  ```
102
102
 
103
+ ### Writable atoms
104
+
105
+ An atom is writable when its config includes a `set` function. The return type becomes `WritableAtom<Value, Args>`, where `Args` is the tuple of arguments the setter accepts.
106
+
107
+ ```ts
108
+ const profileAtom = atom({
109
+ debugLabel: "profile",
110
+ get: async () => {
111
+ const res = await fetch("/api/profile");
112
+ return res.json() as Promise<Profile>;
113
+ },
114
+ set: async (ctx, patch: Partial<Profile>) => {
115
+ await fetch("/api/profile", {
116
+ method: "PATCH",
117
+ body: JSON.stringify(patch),
118
+ signal: ctx.signal,
119
+ });
120
+ },
121
+ });
122
+ ```
123
+
124
+ **Lifecycle after `set`:** the store calls `config.set(ctx, ...args)`, waits for the returned promise, then calls `invalidate` on the atom. This marks it `stale` and triggers a background refetch via `get` — the same stale-while-revalidate flow described [above](#stale-while-revalidate).
125
+
126
+ The `ctx` passed to `set` provides:
127
+
128
+ - **`ctx.get(key)`** — read any declared dependency (same as in `get`).
129
+ - **`ctx.signal`** — an `AbortSignal` tied to the atom's lifecycle, useful for cancelling in-flight requests.
130
+ - **`ctx.setOptimisticValue(value)`** — synchronously update the atom's cached value before the async work completes (see below).
131
+
132
+ #### Optimistic updates
133
+
134
+ Call `ctx.setOptimisticValue(value)` inside `set` to immediately reflect the new value in the UI while the mutation runs in the background. If the mutation throws (or the signal aborts), the store automatically rolls back to the state captured before the first `setOptimisticValue` call. Derived atoms that depend on this atom are marked `stale` so they re-render too.
135
+
136
+ ```ts
137
+ const todoAtom = atom({
138
+ debugLabel: "todo",
139
+ get: async () => {
140
+ const res = await fetch("/api/todo");
141
+ return res.json() as Promise<Todo>;
142
+ },
143
+ set: async (ctx, title: string) => {
144
+ ctx.setOptimisticValue({ title, done: false });
145
+ await fetch("/api/todo", {
146
+ method: "PUT",
147
+ body: JSON.stringify({ title }),
148
+ signal: ctx.signal,
149
+ });
150
+ },
151
+ });
152
+ ```
153
+
154
+ If the `PUT` fails, the atom reverts to whatever value `get` had loaded before the optimistic update — no manual rollback needed.
155
+
156
+ #### Writable atoms vs `onMount`
157
+
158
+ Both can update an atom's cached value, but they serve different purposes:
159
+
160
+ | | `set` | `onMount` |
161
+ |-|-------|-----------|
162
+ | **Triggered by** | Explicit call (`store.set`, `useSetAtom`) | First subscriber mounts |
163
+ | **After update** | `invalidate` → refetch via `get` | No refetch — value stays as-is |
164
+ | **Use case** | Mutations, API calls, optimistic updates | Timers, subscriptions, imperative push |
165
+
103
166
  ### `onMount`
104
167
 
105
168
  Optional lifecycle hook that runs when the atom **first gains a subscriber** in a store (for example when a React component using `useAtomValue` mounts). It receives a synchronous `set(value)` that marks the atom `fresh` and notifies listeners — useful for timers, subscriptions, or imperative updates that should not go through `get`.
@@ -320,7 +383,7 @@ export function App() {
320
383
  ## Utility Types
321
384
 
322
385
  ```ts
323
- import type { AtomValue, AtomArgs, IsWritable } from "@kdeveloper/kvark";
386
+ import type { AtomValue, AtomArgs, IsWritable, WritableAtomContext } from "@kdeveloper/kvark";
324
387
 
325
388
  type UserData = AtomValue<typeof userAtom>; // → User
326
389
  type PostArgs = AtomArgs<typeof postAtom>; // → [postId: number]
@@ -19,6 +19,9 @@ function atom(config) {
19
19
  if (config.set != null) {
20
20
  internal.set = config.set;
21
21
  }
22
+ if (config.onMount != null) {
23
+ internal.onMount = config.onMount;
24
+ }
22
25
  if (config.set != null) {
23
26
  return {
24
27
  [CONFIG]: internal,
@@ -161,7 +164,9 @@ var Store = class {
161
164
  state: PENDING_STATE,
162
165
  version: 0,
163
166
  promise: null,
164
- listeners: /* @__PURE__ */ new Set()
167
+ listeners: /* @__PURE__ */ new Set(),
168
+ mountCount: 0,
169
+ unmount: null
165
170
  };
166
171
  this.#atoms.set(atom2, entry);
167
172
  }
@@ -257,6 +262,16 @@ var Store = class {
257
262
  }
258
263
  }
259
264
  }
265
+ #markReverseDependentsStale(atom2) {
266
+ const rdeps = this.#rdeps.get(atom2);
267
+ if (rdeps != null) {
268
+ const visited = /* @__PURE__ */ new Set([atom2]);
269
+ for (const dep of rdeps) {
270
+ this.#markStale(dep, visited);
271
+ this.#pending.add(dep);
272
+ }
273
+ }
274
+ }
260
275
  #flushPending() {
261
276
  const batch = [...this.#pending];
262
277
  this.#pending.clear();
@@ -313,8 +328,28 @@ var Store = class {
313
328
  if (entry.state.status === "pending" && entry.promise == null) {
314
329
  void this.resolve(atom2);
315
330
  }
331
+ entry.mountCount++;
332
+ if (entry.mountCount === 1) {
333
+ const config = atom2[CONFIG];
334
+ if (config.onMount != null) {
335
+ const setValue = (value) => {
336
+ entry.state = { status: "fresh", value, error: void 0 };
337
+ entry.version++;
338
+ this.#scheduleNotify(atom2);
339
+ };
340
+ const cleanup = config.onMount(setValue);
341
+ if (typeof cleanup === "function") {
342
+ entry.unmount = cleanup;
343
+ }
344
+ }
345
+ }
316
346
  return () => {
317
347
  entry.listeners.delete(listener);
348
+ entry.mountCount--;
349
+ if (entry.mountCount === 0 && entry.unmount != null) {
350
+ entry.unmount();
351
+ entry.unmount = null;
352
+ }
318
353
  };
319
354
  }
320
355
  #autoRegisterFamily(atom2) {
@@ -349,8 +384,32 @@ var Store = class {
349
384
  );
350
385
  }
351
386
  const deps = config.dependencies ?? {};
352
- const ctx = this.#makeCtx(atom2, deps);
353
- await config.set(ctx, ...args);
387
+ const baseCtx = this.#makeCtx(atom2, deps);
388
+ const entry = this.#getOrCreate(atom2);
389
+ const optimisticSnapshot = { current: null };
390
+ const ctx = {
391
+ ...baseCtx,
392
+ setOptimisticValue: (value) => {
393
+ if (optimisticSnapshot.current == null) {
394
+ optimisticSnapshot.current = { state: entry.state, version: entry.version };
395
+ }
396
+ entry.state = { status: "fresh", value, error: void 0 };
397
+ entry.version++;
398
+ this.#scheduleNotify(atom2);
399
+ this.#markReverseDependentsStale(atom2);
400
+ }
401
+ };
402
+ try {
403
+ await config.set(ctx, ...args);
404
+ } catch (e) {
405
+ const snap = optimisticSnapshot.current;
406
+ if (snap != null) {
407
+ entry.state = snap.state;
408
+ entry.version = snap.version;
409
+ this.#scheduleNotify(atom2);
410
+ }
411
+ throw e;
412
+ }
354
413
  this.invalidate(atom2);
355
414
  }
356
415
  getClient() {
@@ -402,4 +461,4 @@ export {
402
461
  atomFamily,
403
462
  createStore
404
463
  };
405
- //# sourceMappingURL=chunk-6WIZ6MFN.js.map
464
+ //# sourceMappingURL=chunk-QWP5ITSY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/types.ts","../src/internal/atom.ts","../src/internal/family.ts","../src/internal/store.ts"],"sourcesContent":["export const CONFIG = Symbol(\"kvark.config\");\nexport const WRITABLE = Symbol(\"kvark.writable\");\nexport const FAMILY_LINK = Symbol(\"kvark.familyLink\");\n\nexport type AtomState<Value> =\n | { status: \"pending\"; value: undefined; error: undefined }\n | { status: \"stale\"; value: Value; error: undefined }\n | { status: \"fresh\"; value: Value; error: undefined }\n | { status: \"error\"; value: Value | undefined; error: unknown };\n\nexport type StalePolicy = \"keep\" | \"suspend\" | \"reset\";\n\nexport type AtomContext<Deps extends Record<string, Atom<unknown>>> = {\n get: <K extends keyof Deps>(key: K) => Promise<AtomValue<Deps[K]>>;\n signal: AbortSignal;\n};\n\nexport type WritableAtomContext<\n Deps extends Record<string, Atom<unknown>>,\n Value,\n> = AtomContext<Deps> & {\n setOptimisticValue: (value: Value) => void;\n};\n\nexport type AtomConfig<\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = {\n dependencies?: Deps;\n stalePolicy?: StalePolicy;\n debugLabel?: string;\n get: (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (ctx: WritableAtomContext<Deps, Value>, ...args: Args) => Promise<void>;\n onMount?: (set: (value: Value) => void) => (() => void) | void;\n};\n\nexport interface InternalAtomConfig<Value> {\n dependencies?: Record<string, Atom<unknown>>;\n stalePolicy?: StalePolicy;\n debugLabel?: string;\n get: (ctx: AtomContext<Record<string, Atom<unknown>>>) => Promise<Value>;\n set?: (\n ctx: WritableAtomContext<Record<string, Atom<unknown>>, Value>,\n ...args: readonly unknown[]\n ) => Promise<void>;\n onMount?: (set: (value: unknown) => void) => (() => void) | void;\n}\n\nexport interface Atom<out Value> {\n readonly [CONFIG]: InternalAtomConfig<Value>;\n readonly debugLabel: string | undefined;\n}\n\nexport interface WritableAtom<\n out Value,\n in out Args extends readonly unknown[],\n> extends Atom<Value> {\n readonly [WRITABLE]: Args;\n}\n\nexport type AtomValue<A> = A extends Atom<infer V> ? V : never;\nexport type IsWritable<A> = A extends WritableAtom<unknown, readonly unknown[]> ? true : false;\nexport type AtomArgs<A> = A extends WritableAtom<unknown, infer Args> ? Args : never;\n","import type {\n Atom,\n AtomConfig,\n InternalAtomConfig,\n WritableAtom,\n WritableAtomContext,\n} from \"./types.js\";\nimport { CONFIG, WRITABLE } from \"./types.js\";\n\ntype WritableConfig<\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = AtomConfig<Value, Deps, Args> & {\n set: (ctx: WritableAtomContext<Deps, Value>, ...args: Args) => Promise<void>;\n};\n\ntype ReadonlyConfig<Value, Deps extends Record<string, Atom<unknown>>> = Omit<\n AtomConfig<Value, Deps, readonly []>,\n \"set\"\n>;\n\nexport function atom<\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(config: WritableConfig<Value, Deps, Args>): WritableAtom<Value, Args>;\n\nexport function atom<Value, Deps extends Record<string, Atom<unknown>> = Record<never, never>>(\n config: ReadonlyConfig<Value, Deps>,\n): Atom<Value>;\n\nexport function atom<\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(config: AtomConfig<Value, Deps, Args>): Atom<Value> {\n const internal: InternalAtomConfig<Value> = Object.create(null) as InternalAtomConfig<Value>;\n internal.get = config.get as InternalAtomConfig<Value>[\"get\"];\n\n if (config.debugLabel != null) {\n internal.debugLabel = config.debugLabel;\n }\n if (config.dependencies != null) {\n internal.dependencies = config.dependencies as Record<string, Atom<unknown>>;\n }\n if (config.stalePolicy != null) {\n internal.stalePolicy = config.stalePolicy;\n }\n if (config.set != null) {\n internal.set = config.set as NonNullable<InternalAtomConfig<Value>[\"set\"]>;\n }\n if (config.onMount != null) {\n internal.onMount = config.onMount as NonNullable<InternalAtomConfig<Value>[\"onMount\"]>;\n }\n\n if (config.set != null) {\n return {\n [CONFIG]: internal,\n [WRITABLE]: [] as unknown as Args,\n debugLabel: config.debugLabel,\n } as unknown as Atom<Value>;\n }\n\n return {\n [CONFIG]: internal,\n debugLabel: config.debugLabel,\n } as Atom<Value>;\n}\n","import type { Atom, AtomContext, StalePolicy } from \"./types.js\";\nimport { FAMILY_LINK } from \"./types.js\";\nimport { atom } from \"./atom.js\";\n\nexport type AtomFamilyOptions<\n Param,\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = {\n dependencies?: (param: Param) => Deps;\n stalePolicy?: StalePolicy;\n cachePolicy?: \"keep-all\" | \"lru\";\n lruSize?: number;\n debugLabel?: string;\n get: (param: Param) => (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (param: Param) => (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n};\n\nexport interface AtomFamily<Param, Value> {\n (param: Param): Atom<Value>;\n invalidate(param: Param): void;\n invalidateAll(): void;\n remove(param: Param): void;\n getCache(): ReadonlyMap<Param, Atom<Value>>;\n}\n\nexport interface FamilyLink {\n invalidateAtom: (atom: Atom<unknown>) => void;\n registerStore: (cb: (atom: Atom<unknown>) => void) => () => void;\n}\n\nexport function getFamilyLink(target: Atom<unknown>): FamilyLink | undefined {\n return (target as unknown as Record<symbol, FamilyLink | undefined>)[FAMILY_LINK];\n}\n\nexport function atomFamily<\n Param,\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(options: AtomFamilyOptions<Param, Value, Deps, Args>): AtomFamily<Param, Value> {\n const cachePolicy = options.cachePolicy ?? \"keep-all\";\n const lruSize = options.lruSize ?? 100;\n const cache = new Map<Param, Atom<Value>>();\n const lruOrder: Param[] = [];\n const storeCallbacks = new Set<(atom: Atom<unknown>) => void>();\n\n const link: FamilyLink = {\n invalidateAtom(target: Atom<unknown>): void {\n for (const cb of storeCallbacks) {\n cb(target);\n }\n },\n registerStore(cb: (atom: Atom<unknown>) => void): () => void {\n storeCallbacks.add(cb);\n return () => {\n storeCallbacks.delete(cb);\n };\n },\n };\n\n function attachLink(target: Atom<Value>): void {\n (target as unknown as Record<symbol, FamilyLink>)[FAMILY_LINK] = link;\n }\n\n function getOrCreate(param: Param): Atom<Value> {\n const cached = cache.get(param);\n if (cached != null) {\n if (cachePolicy === \"lru\") {\n touchLru(param);\n }\n return cached;\n }\n\n const deps = options.dependencies?.(param);\n const getFn = options.get(param);\n const setFn = options.set?.(param);\n\n const label =\n options.debugLabel != null ? `${options.debugLabel}(${String(param)})` : undefined;\n\n const created = createAtom(label, deps, getFn, setFn);\n attachLink(created);\n cache.set(param, created);\n\n if (cachePolicy === \"lru\") {\n lruOrder.push(param);\n evictLru();\n }\n\n return created;\n }\n\n function createAtom(\n label: string | undefined,\n deps: Deps | undefined,\n getFn: (ctx: AtomContext<Deps>) => Promise<Value>,\n setFn: ((ctx: AtomContext<Deps>, ...args: Args) => Promise<void>) | undefined,\n ): Atom<Value> {\n const base = {\n get: getFn as (ctx: AtomContext<Deps>) => Promise<Value>,\n } as {\n debugLabel?: string;\n dependencies?: Deps;\n stalePolicy?: StalePolicy;\n get: (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n };\n\n if (label != null) {\n base.debugLabel = label;\n }\n if (deps != null) {\n base.dependencies = deps;\n }\n if (options.stalePolicy != null) {\n base.stalePolicy = options.stalePolicy;\n }\n\n if (setFn != null) {\n base.set = setFn;\n return atom(\n base as typeof base & {\n set: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n },\n ) as Atom<Value>;\n }\n\n return atom(base as Omit<typeof base, \"set\">);\n }\n\n function touchLru(param: Param): void {\n const idx = lruOrder.indexOf(param);\n if (idx !== -1) {\n lruOrder.splice(idx, 1);\n }\n lruOrder.push(param);\n }\n\n function evictLru(): void {\n while (lruOrder.length > lruSize) {\n const evicted = lruOrder.shift();\n if (evicted !== undefined) {\n cache.delete(evicted);\n }\n }\n }\n\n const family = getOrCreate as AtomFamily<Param, Value>;\n\n family.invalidate = (param: Param): void => {\n const cached = cache.get(param);\n if (cached != null) {\n link.invalidateAtom(cached);\n }\n };\n\n family.invalidateAll = (): void => {\n for (const cached of cache.values()) {\n link.invalidateAtom(cached);\n }\n };\n\n family.remove = (param: Param): void => {\n cache.delete(param);\n if (cachePolicy === \"lru\") {\n const idx = lruOrder.indexOf(param);\n if (idx !== -1) {\n lruOrder.splice(idx, 1);\n }\n }\n };\n\n family.getCache = (): ReadonlyMap<Param, Atom<Value>> => cache;\n\n return family;\n}\n","import type {\n Atom,\n AtomContext,\n AtomState,\n InternalAtomConfig,\n StalePolicy,\n WritableAtom,\n WritableAtomContext,\n} from \"./types.js\";\nimport { CONFIG } from \"./types.js\";\nimport { getFamilyLink } from \"./family.js\";\n\ntype AtomEntry<V> = {\n state: AtomState<V>;\n version: number;\n promise: Promise<V> | null;\n listeners: Set<() => void>;\n mountCount: number;\n unmount: (() => void) | null;\n};\n\nexport interface StoreClient {\n get<V>(atom: Atom<V>): Promise<V>;\n set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void>;\n invalidate(atom: Atom<unknown>): void;\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void;\n subscribe<V>(atom: Atom<V>, listener: (state: AtomState<V>) => void): () => void;\n}\n\nconst PENDING_STATE: AtomState<never> = {\n status: \"pending\",\n value: undefined,\n error: undefined,\n} as AtomState<never>;\n\nexport class Store {\n readonly #atoms = new WeakMap<Atom<unknown>, AtomEntry<unknown>>();\n readonly #rdeps = new Map<Atom<unknown>, Set<Atom<unknown>>>();\n readonly #pending = new Set<Atom<unknown>>();\n readonly #controllers = new WeakMap<Atom<unknown>, AbortController>();\n readonly #familyUnsubs = new Set<() => void>();\n readonly #registeredFamilies = new WeakSet<object>();\n #client: StoreClient | null = null;\n\n #getOrCreate<V>(atom: Atom<V>): AtomEntry<V> {\n let entry = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (entry == null) {\n entry = {\n state: PENDING_STATE as AtomState<V>,\n version: 0,\n promise: null,\n listeners: new Set(),\n mountCount: 0,\n unmount: null,\n };\n this.#atoms.set(atom, entry as AtomEntry<unknown>);\n }\n return entry;\n }\n\n resolve<V>(atom: Atom<V>): Promise<V> {\n const entry = this.#getOrCreate(atom);\n if (entry.promise != null) {\n return entry.promise;\n }\n\n const promise = this.#runGet(atom).finally(() => {\n const e = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (e != null) {\n e.promise = null;\n }\n });\n entry.promise = promise;\n return promise;\n }\n\n async #runGet<V>(atom: Atom<V>): Promise<V> {\n const config: InternalAtomConfig<V> = atom[CONFIG];\n const dependencies = config.dependencies;\n const stalePolicy: StalePolicy = config.stalePolicy ?? \"keep\";\n const entry = this.#getOrCreate(atom);\n\n if (dependencies != null) {\n await Promise.all(Object.values(dependencies).map((dep) => this.resolve(dep)));\n this.#registerRdeps(atom, dependencies);\n }\n\n const ctx = this.#makeCtx(atom, dependencies ?? {});\n\n try {\n const value = await config.get(ctx);\n entry.state = { status: \"fresh\", value, error: undefined };\n entry.version++;\n this.#scheduleNotify(atom);\n return value;\n } catch (e: unknown) {\n if (isAbortError(e)) {\n throw e;\n }\n const prevValue = extractPreviousValue<V>(entry.state);\n entry.state = {\n status: \"error\",\n value: stalePolicy === \"keep\" ? prevValue : undefined,\n error: e,\n } as AtomState<V>;\n entry.version++;\n this.#scheduleNotify(atom);\n throw e;\n }\n }\n\n invalidate(atom: Atom<unknown>): void {\n this.#markStale(atom);\n this.#pending.add(atom);\n if (this.#pending.size === 1) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void {\n for (const a of atoms) {\n this.#markStale(a);\n this.#pending.add(a);\n }\n if (this.#pending.size > 0) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n #markStale(atom: Atom<unknown>, visited = new Set<Atom<unknown>>()): void {\n if (visited.has(atom)) {\n return;\n }\n visited.add(atom);\n\n const entry = this.#atoms.get(atom);\n if (entry != null) {\n const stalePolicy: StalePolicy = atom[CONFIG].stalePolicy ?? \"keep\";\n\n if (entry.state.status === \"fresh\") {\n if (stalePolicy === \"reset\") {\n entry.state = PENDING_STATE;\n } else {\n entry.state = { ...entry.state, status: \"stale\" };\n }\n }\n\n entry.promise = null;\n this.#controllers.get(atom)?.abort();\n }\n\n const rdeps = this.#rdeps.get(atom);\n if (rdeps != null) {\n for (const dep of rdeps) {\n this.#markStale(dep, visited);\n }\n }\n }\n\n #markReverseDependentsStale(atom: Atom<unknown>): void {\n const rdeps = this.#rdeps.get(atom);\n if (rdeps != null) {\n const visited = new Set<Atom<unknown>>([atom]);\n for (const dep of rdeps) {\n this.#markStale(dep, visited);\n this.#pending.add(dep);\n }\n }\n }\n\n #flushPending(): void {\n const batch = [...this.#pending];\n this.#pending.clear();\n const toNotify = new Set<() => void>();\n for (const a of batch) {\n const entry = this.#atoms.get(a);\n if (entry != null) {\n for (const l of entry.listeners) {\n toNotify.add(l);\n }\n }\n }\n for (const l of toNotify) {\n l();\n }\n }\n\n #scheduleNotify(atom: Atom<unknown>): void {\n this.#pending.add(atom);\n if (this.#pending.size === 1) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n #registerRdeps(atom: Atom<unknown>, deps: Record<string, Atom<unknown>>): void {\n for (const dep of Object.values(deps)) {\n let set = this.#rdeps.get(dep);\n if (set == null) {\n set = new Set();\n this.#rdeps.set(dep, set);\n }\n set.add(atom);\n }\n }\n\n #makeCtx(\n atom: Atom<unknown>,\n deps: Record<string, Atom<unknown>>,\n ): AtomContext<Record<string, Atom<unknown>>> {\n this.#controllers.get(atom)?.abort();\n const controller = new AbortController();\n this.#controllers.set(atom, controller);\n\n return {\n signal: controller.signal,\n get: async (key: string) => {\n const dep = deps[key];\n if (dep == null) {\n throw new Error(`Unknown dependency key: \"${String(key)}\"`);\n }\n return this.resolve(dep);\n },\n };\n }\n\n subscribe<V>(atom: Atom<V>, listener: () => void): () => void {\n const entry = this.#getOrCreate(atom);\n entry.listeners.add(listener);\n\n this.#autoRegisterFamily(atom);\n\n if (entry.state.status === \"pending\" && entry.promise == null) {\n void this.resolve(atom);\n }\n\n entry.mountCount++;\n if (entry.mountCount === 1) {\n const config: InternalAtomConfig<V> = atom[CONFIG];\n if (config.onMount != null) {\n const setValue = (value: V): void => {\n entry.state = { status: \"fresh\", value, error: undefined };\n entry.version++;\n this.#scheduleNotify(atom);\n };\n const cleanup = config.onMount(setValue as (value: unknown) => void);\n if (typeof cleanup === \"function\") {\n entry.unmount = cleanup;\n }\n }\n }\n\n return () => {\n entry.listeners.delete(listener);\n entry.mountCount--;\n if (entry.mountCount === 0 && entry.unmount != null) {\n entry.unmount();\n entry.unmount = null;\n }\n };\n }\n\n #autoRegisterFamily(atom: Atom<unknown>): void {\n const link = getFamilyLink(atom);\n if (link == null) {\n return;\n }\n if (this.#registeredFamilies.has(link)) {\n return;\n }\n this.#registeredFamilies.add(link);\n const unsub = link.registerStore((target: Atom<unknown>) => {\n this.invalidate(target);\n });\n this.#familyUnsubs.add(unsub);\n }\n\n getSnapshot<V>(atom: Atom<V>): AtomState<V> {\n return this.#getOrCreate(atom).state;\n }\n\n getServerSnapshot<V>(atom: Atom<V>): AtomState<V> {\n const entry = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (entry != null) {\n return entry.state;\n }\n return PENDING_STATE as AtomState<V>;\n }\n\n async set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void> {\n const config: InternalAtomConfig<V> = atom[CONFIG];\n if (config.set == null) {\n throw new Error(\n `Atom${atom.debugLabel != null ? ` \"${atom.debugLabel}\"` : \"\"} is not writable`,\n );\n }\n\n const deps = config.dependencies ?? {};\n const baseCtx = this.#makeCtx(atom, deps);\n const entry = this.#getOrCreate(atom);\n\n // Ref so TS sees mutations; `let` + closure assignment is inferred as always null in catch.\n const optimisticSnapshot: {\n current: { state: AtomState<V>; version: number } | null;\n } = { current: null };\n\n const ctx: WritableAtomContext<Record<string, Atom<unknown>>, V> = {\n ...baseCtx,\n setOptimisticValue: (value: V): void => {\n if (optimisticSnapshot.current == null) {\n optimisticSnapshot.current = { state: entry.state, version: entry.version };\n }\n entry.state = { status: \"fresh\", value, error: undefined };\n entry.version++;\n this.#scheduleNotify(atom);\n this.#markReverseDependentsStale(atom);\n },\n };\n\n try {\n await config.set(ctx, ...args);\n } catch (e: unknown) {\n const snap = optimisticSnapshot.current;\n if (snap != null) {\n entry.state = snap.state;\n entry.version = snap.version;\n this.#scheduleNotify(atom);\n }\n throw e;\n }\n\n this.invalidate(atom);\n }\n\n getClient(): StoreClient {\n if (this.#client != null) {\n return this.#client;\n }\n\n const store = this;\n this.#client = {\n get<V>(atom: Atom<V>): Promise<V> {\n return store.resolve(atom);\n },\n set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void> {\n return store.set(atom, ...args);\n },\n invalidate(atom: Atom<unknown>): void {\n store.invalidate(atom);\n },\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void {\n store.invalidateMany(atoms);\n },\n subscribe<V>(atom: Atom<V>, listener: (state: AtomState<V>) => void): () => void {\n return store.subscribe(atom, () => {\n listener(store.getSnapshot(atom));\n });\n },\n };\n return this.#client;\n }\n}\n\nexport function createStore(): Store {\n return new Store();\n}\n\nfunction isAbortError(e: unknown): boolean {\n return e instanceof DOMException && e.name === \"AbortError\";\n}\n\nfunction extractPreviousValue<V>(state: AtomState<V>): V | undefined {\n if (state.status === \"stale\" || state.status === \"fresh\") {\n return state.value;\n }\n if (state.status === \"error\") {\n return state.value;\n }\n return undefined;\n}\n"],"mappings":";AAAO,IAAM,SAAS,uBAAO,cAAc;AACpC,IAAM,WAAW,uBAAO,gBAAgB;AACxC,IAAM,cAAc,uBAAO,kBAAkB;;;AC8B7C,SAAS,KAId,QAAoD;AACpD,QAAM,WAAsC,uBAAO,OAAO,IAAI;AAC9D,WAAS,MAAM,OAAO;AAEtB,MAAI,OAAO,cAAc,MAAM;AAC7B,aAAS,aAAa,OAAO;AAAA,EAC/B;AACA,MAAI,OAAO,gBAAgB,MAAM;AAC/B,aAAS,eAAe,OAAO;AAAA,EACjC;AACA,MAAI,OAAO,eAAe,MAAM;AAC9B,aAAS,cAAc,OAAO;AAAA,EAChC;AACA,MAAI,OAAO,OAAO,MAAM;AACtB,aAAS,MAAM,OAAO;AAAA,EACxB;AACA,MAAI,OAAO,WAAW,MAAM;AAC1B,aAAS,UAAU,OAAO;AAAA,EAC5B;AAEA,MAAI,OAAO,OAAO,MAAM;AACtB,WAAO;AAAA,MACL,CAAC,MAAM,GAAG;AAAA,MACV,CAAC,QAAQ,GAAG,CAAC;AAAA,MACb,YAAY,OAAO;AAAA,IACrB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,CAAC,MAAM,GAAG;AAAA,IACV,YAAY,OAAO;AAAA,EACrB;AACF;;;ACpCO,SAAS,cAAc,QAA+C;AAC3E,SAAQ,OAA6D,WAAW;AAClF;AAEO,SAAS,WAKd,SAAgF;AAChF,QAAM,cAAc,QAAQ,eAAe;AAC3C,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,QAAQ,oBAAI,IAAwB;AAC1C,QAAM,WAAoB,CAAC;AAC3B,QAAM,iBAAiB,oBAAI,IAAmC;AAE9D,QAAM,OAAmB;AAAA,IACvB,eAAe,QAA6B;AAC1C,iBAAW,MAAM,gBAAgB;AAC/B,WAAG,MAAM;AAAA,MACX;AAAA,IACF;AAAA,IACA,cAAc,IAA+C;AAC3D,qBAAe,IAAI,EAAE;AACrB,aAAO,MAAM;AACX,uBAAe,OAAO,EAAE;AAAA,MAC1B;AAAA,IACF;AAAA,EACF;AAEA,WAAS,WAAW,QAA2B;AAC7C,IAAC,OAAiD,WAAW,IAAI;AAAA,EACnE;AAEA,WAAS,YAAY,OAA2B;AAC9C,UAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,QAAI,UAAU,MAAM;AAClB,UAAI,gBAAgB,OAAO;AACzB,iBAAS,KAAK;AAAA,MAChB;AACA,aAAO;AAAA,IACT;AAEA,UAAM,OAAO,QAAQ,eAAe,KAAK;AACzC,UAAM,QAAQ,QAAQ,IAAI,KAAK;AAC/B,UAAM,QAAQ,QAAQ,MAAM,KAAK;AAEjC,UAAM,QACJ,QAAQ,cAAc,OAAO,GAAG,QAAQ,UAAU,IAAI,OAAO,KAAK,CAAC,MAAM;AAE3E,UAAM,UAAU,WAAW,OAAO,MAAM,OAAO,KAAK;AACpD,eAAW,OAAO;AAClB,UAAM,IAAI,OAAO,OAAO;AAExB,QAAI,gBAAgB,OAAO;AACzB,eAAS,KAAK,KAAK;AACnB,eAAS;AAAA,IACX;AAEA,WAAO;AAAA,EACT;AAEA,WAAS,WACP,OACA,MACA,OACA,OACa;AACb,UAAM,OAAO;AAAA,MACX,KAAK;AAAA,IACP;AAQA,QAAI,SAAS,MAAM;AACjB,WAAK,aAAa;AAAA,IACpB;AACA,QAAI,QAAQ,MAAM;AAChB,WAAK,eAAe;AAAA,IACtB;AACA,QAAI,QAAQ,eAAe,MAAM;AAC/B,WAAK,cAAc,QAAQ;AAAA,IAC7B;AAEA,QAAI,SAAS,MAAM;AACjB,WAAK,MAAM;AACX,aAAO;AAAA,QACL;AAAA,MAGF;AAAA,IACF;AAEA,WAAO,KAAK,IAAgC;AAAA,EAC9C;AAEA,WAAS,SAAS,OAAoB;AACpC,UAAM,MAAM,SAAS,QAAQ,KAAK;AAClC,QAAI,QAAQ,IAAI;AACd,eAAS,OAAO,KAAK,CAAC;AAAA,IACxB;AACA,aAAS,KAAK,KAAK;AAAA,EACrB;AAEA,WAAS,WAAiB;AACxB,WAAO,SAAS,SAAS,SAAS;AAChC,YAAM,UAAU,SAAS,MAAM;AAC/B,UAAI,YAAY,QAAW;AACzB,cAAM,OAAO,OAAO;AAAA,MACtB;AAAA,IACF;AAAA,EACF;AAEA,QAAM,SAAS;AAEf,SAAO,aAAa,CAAC,UAAuB;AAC1C,UAAM,SAAS,MAAM,IAAI,KAAK;AAC9B,QAAI,UAAU,MAAM;AAClB,WAAK,eAAe,MAAM;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,gBAAgB,MAAY;AACjC,eAAW,UAAU,MAAM,OAAO,GAAG;AACnC,WAAK,eAAe,MAAM;AAAA,IAC5B;AAAA,EACF;AAEA,SAAO,SAAS,CAAC,UAAuB;AACtC,UAAM,OAAO,KAAK;AAClB,QAAI,gBAAgB,OAAO;AACzB,YAAM,MAAM,SAAS,QAAQ,KAAK;AAClC,UAAI,QAAQ,IAAI;AACd,iBAAS,OAAO,KAAK,CAAC;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,WAAW,MAAuC;AAEzD,SAAO;AACT;;;ACpJA,IAAM,gBAAkC;AAAA,EACtC,QAAQ;AAAA,EACR,OAAO;AAAA,EACP,OAAO;AACT;AAEO,IAAM,QAAN,MAAY;AAAA,EACR,SAAS,oBAAI,QAA2C;AAAA,EACxD,SAAS,oBAAI,IAAuC;AAAA,EACpD,WAAW,oBAAI,IAAmB;AAAA,EAClC,eAAe,oBAAI,QAAwC;AAAA,EAC3D,gBAAgB,oBAAI,IAAgB;AAAA,EACpC,sBAAsB,oBAAI,QAAgB;AAAA,EACnD,UAA8B;AAAA,EAE9B,aAAgBA,OAA6B;AAC3C,QAAI,QAAQ,KAAK,OAAO,IAAIA,KAAI;AAChC,QAAI,SAAS,MAAM;AACjB,cAAQ;AAAA,QACN,OAAO;AAAA,QACP,SAAS;AAAA,QACT,SAAS;AAAA,QACT,WAAW,oBAAI,IAAI;AAAA,QACnB,YAAY;AAAA,QACZ,SAAS;AAAA,MACX;AACA,WAAK,OAAO,IAAIA,OAAM,KAA2B;AAAA,IACnD;AACA,WAAO;AAAA,EACT;AAAA,EAEA,QAAWA,OAA2B;AACpC,UAAM,QAAQ,KAAK,aAAaA,KAAI;AACpC,QAAI,MAAM,WAAW,MAAM;AACzB,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,UAAU,KAAK,QAAQA,KAAI,EAAE,QAAQ,MAAM;AAC/C,YAAM,IAAI,KAAK,OAAO,IAAIA,KAAI;AAC9B,UAAI,KAAK,MAAM;AACb,UAAE,UAAU;AAAA,MACd;AAAA,IACF,CAAC;AACD,UAAM,UAAU;AAChB,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,QAAWA,OAA2B;AAC1C,UAAM,SAAgCA,MAAK,MAAM;AACjD,UAAM,eAAe,OAAO;AAC5B,UAAM,cAA2B,OAAO,eAAe;AACvD,UAAM,QAAQ,KAAK,aAAaA,KAAI;AAEpC,QAAI,gBAAgB,MAAM;AACxB,YAAM,QAAQ,IAAI,OAAO,OAAO,YAAY,EAAE,IAAI,CAAC,QAAQ,KAAK,QAAQ,GAAG,CAAC,CAAC;AAC7E,WAAK,eAAeA,OAAM,YAAY;AAAA,IACxC;AAEA,UAAM,MAAM,KAAK,SAASA,OAAM,gBAAgB,CAAC,CAAC;AAElD,QAAI;AACF,YAAM,QAAQ,MAAM,OAAO,IAAI,GAAG;AAClC,YAAM,QAAQ,EAAE,QAAQ,SAAS,OAAO,OAAO,OAAU;AACzD,YAAM;AACN,WAAK,gBAAgBA,KAAI;AACzB,aAAO;AAAA,IACT,SAAS,GAAY;AACnB,UAAI,aAAa,CAAC,GAAG;AACnB,cAAM;AAAA,MACR;AACA,YAAM,YAAY,qBAAwB,MAAM,KAAK;AACrD,YAAM,QAAQ;AAAA,QACZ,QAAQ;AAAA,QACR,OAAO,gBAAgB,SAAS,YAAY;AAAA,QAC5C,OAAO;AAAA,MACT;AACA,YAAM;AACN,WAAK,gBAAgBA,KAAI;AACzB,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEA,WAAWA,OAA2B;AACpC,SAAK,WAAWA,KAAI;AACpB,SAAK,SAAS,IAAIA,KAAI;AACtB,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,qBAAe,MAAM;AACnB,aAAK,cAAc;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,eAAe,OAA2C;AACxD,eAAW,KAAK,OAAO;AACrB,WAAK,WAAW,CAAC;AACjB,WAAK,SAAS,IAAI,CAAC;AAAA,IACrB;AACA,QAAI,KAAK,SAAS,OAAO,GAAG;AAC1B,qBAAe,MAAM;AACnB,aAAK,cAAc;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,WAAWA,OAAqB,UAAU,oBAAI,IAAmB,GAAS;AACxE,QAAI,QAAQ,IAAIA,KAAI,GAAG;AACrB;AAAA,IACF;AACA,YAAQ,IAAIA,KAAI;AAEhB,UAAM,QAAQ,KAAK,OAAO,IAAIA,KAAI;AAClC,QAAI,SAAS,MAAM;AACjB,YAAM,cAA2BA,MAAK,MAAM,EAAE,eAAe;AAE7D,UAAI,MAAM,MAAM,WAAW,SAAS;AAClC,YAAI,gBAAgB,SAAS;AAC3B,gBAAM,QAAQ;AAAA,QAChB,OAAO;AACL,gBAAM,QAAQ,EAAE,GAAG,MAAM,OAAO,QAAQ,QAAQ;AAAA,QAClD;AAAA,MACF;AAEA,YAAM,UAAU;AAChB,WAAK,aAAa,IAAIA,KAAI,GAAG,MAAM;AAAA,IACrC;AAEA,UAAM,QAAQ,KAAK,OAAO,IAAIA,KAAI;AAClC,QAAI,SAAS,MAAM;AACjB,iBAAW,OAAO,OAAO;AACvB,aAAK,WAAW,KAAK,OAAO;AAAA,MAC9B;AAAA,IACF;AAAA,EACF;AAAA,EAEA,4BAA4BA,OAA2B;AACrD,UAAM,QAAQ,KAAK,OAAO,IAAIA,KAAI;AAClC,QAAI,SAAS,MAAM;AACjB,YAAM,UAAU,oBAAI,IAAmB,CAACA,KAAI,CAAC;AAC7C,iBAAW,OAAO,OAAO;AACvB,aAAK,WAAW,KAAK,OAAO;AAC5B,aAAK,SAAS,IAAI,GAAG;AAAA,MACvB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,gBAAsB;AACpB,UAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ;AAC/B,SAAK,SAAS,MAAM;AACpB,UAAM,WAAW,oBAAI,IAAgB;AACrC,eAAW,KAAK,OAAO;AACrB,YAAM,QAAQ,KAAK,OAAO,IAAI,CAAC;AAC/B,UAAI,SAAS,MAAM;AACjB,mBAAW,KAAK,MAAM,WAAW;AAC/B,mBAAS,IAAI,CAAC;AAAA,QAChB;AAAA,MACF;AAAA,IACF;AACA,eAAW,KAAK,UAAU;AACxB,QAAE;AAAA,IACJ;AAAA,EACF;AAAA,EAEA,gBAAgBA,OAA2B;AACzC,SAAK,SAAS,IAAIA,KAAI;AACtB,QAAI,KAAK,SAAS,SAAS,GAAG;AAC5B,qBAAe,MAAM;AACnB,aAAK,cAAc;AAAA,MACrB,CAAC;AAAA,IACH;AAAA,EACF;AAAA,EAEA,eAAeA,OAAqB,MAA2C;AAC7E,eAAW,OAAO,OAAO,OAAO,IAAI,GAAG;AACrC,UAAI,MAAM,KAAK,OAAO,IAAI,GAAG;AAC7B,UAAI,OAAO,MAAM;AACf,cAAM,oBAAI,IAAI;AACd,aAAK,OAAO,IAAI,KAAK,GAAG;AAAA,MAC1B;AACA,UAAI,IAAIA,KAAI;AAAA,IACd;AAAA,EACF;AAAA,EAEA,SACEA,OACA,MAC4C;AAC5C,SAAK,aAAa,IAAIA,KAAI,GAAG,MAAM;AACnC,UAAM,aAAa,IAAI,gBAAgB;AACvC,SAAK,aAAa,IAAIA,OAAM,UAAU;AAEtC,WAAO;AAAA,MACL,QAAQ,WAAW;AAAA,MACnB,KAAK,OAAO,QAAgB;AAC1B,cAAM,MAAM,KAAK,GAAG;AACpB,YAAI,OAAO,MAAM;AACf,gBAAM,IAAI,MAAM,4BAA4B,OAAO,GAAG,CAAC,GAAG;AAAA,QAC5D;AACA,eAAO,KAAK,QAAQ,GAAG;AAAA,MACzB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,UAAaA,OAAe,UAAkC;AAC5D,UAAM,QAAQ,KAAK,aAAaA,KAAI;AACpC,UAAM,UAAU,IAAI,QAAQ;AAE5B,SAAK,oBAAoBA,KAAI;AAE7B,QAAI,MAAM,MAAM,WAAW,aAAa,MAAM,WAAW,MAAM;AAC7D,WAAK,KAAK,QAAQA,KAAI;AAAA,IACxB;AAEA,UAAM;AACN,QAAI,MAAM,eAAe,GAAG;AAC1B,YAAM,SAAgCA,MAAK,MAAM;AACjD,UAAI,OAAO,WAAW,MAAM;AAC1B,cAAM,WAAW,CAAC,UAAmB;AACnC,gBAAM,QAAQ,EAAE,QAAQ,SAAS,OAAO,OAAO,OAAU;AACzD,gBAAM;AACN,eAAK,gBAAgBA,KAAI;AAAA,QAC3B;AACA,cAAM,UAAU,OAAO,QAAQ,QAAoC;AACnE,YAAI,OAAO,YAAY,YAAY;AACjC,gBAAM,UAAU;AAAA,QAClB;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM;AACX,YAAM,UAAU,OAAO,QAAQ;AAC/B,YAAM;AACN,UAAI,MAAM,eAAe,KAAK,MAAM,WAAW,MAAM;AACnD,cAAM,QAAQ;AACd,cAAM,UAAU;AAAA,MAClB;AAAA,IACF;AAAA,EACF;AAAA,EAEA,oBAAoBA,OAA2B;AAC7C,UAAM,OAAO,cAAcA,KAAI;AAC/B,QAAI,QAAQ,MAAM;AAChB;AAAA,IACF;AACA,QAAI,KAAK,oBAAoB,IAAI,IAAI,GAAG;AACtC;AAAA,IACF;AACA,SAAK,oBAAoB,IAAI,IAAI;AACjC,UAAM,QAAQ,KAAK,cAAc,CAAC,WAA0B;AAC1D,WAAK,WAAW,MAAM;AAAA,IACxB,CAAC;AACD,SAAK,cAAc,IAAI,KAAK;AAAA,EAC9B;AAAA,EAEA,YAAeA,OAA6B;AAC1C,WAAO,KAAK,aAAaA,KAAI,EAAE;AAAA,EACjC;AAAA,EAEA,kBAAqBA,OAA6B;AAChD,UAAM,QAAQ,KAAK,OAAO,IAAIA,KAAI;AAClC,QAAI,SAAS,MAAM;AACjB,aAAO,MAAM;AAAA,IACf;AACA,WAAO;AAAA,EACT;AAAA,EAEA,MAAM,IAAqCA,UAA6B,MAAwB;AAC9F,UAAM,SAAgCA,MAAK,MAAM;AACjD,QAAI,OAAO,OAAO,MAAM;AACtB,YAAM,IAAI;AAAA,QACR,OAAOA,MAAK,cAAc,OAAO,KAAKA,MAAK,UAAU,MAAM,EAAE;AAAA,MAC/D;AAAA,IACF;AAEA,UAAM,OAAO,OAAO,gBAAgB,CAAC;AACrC,UAAM,UAAU,KAAK,SAASA,OAAM,IAAI;AACxC,UAAM,QAAQ,KAAK,aAAaA,KAAI;AAGpC,UAAM,qBAEF,EAAE,SAAS,KAAK;AAEpB,UAAM,MAA6D;AAAA,MACjE,GAAG;AAAA,MACH,oBAAoB,CAAC,UAAmB;AACtC,YAAI,mBAAmB,WAAW,MAAM;AACtC,6BAAmB,UAAU,EAAE,OAAO,MAAM,OAAO,SAAS,MAAM,QAAQ;AAAA,QAC5E;AACA,cAAM,QAAQ,EAAE,QAAQ,SAAS,OAAO,OAAO,OAAU;AACzD,cAAM;AACN,aAAK,gBAAgBA,KAAI;AACzB,aAAK,4BAA4BA,KAAI;AAAA,MACvC;AAAA,IACF;AAEA,QAAI;AACF,YAAM,OAAO,IAAI,KAAK,GAAG,IAAI;AAAA,IAC/B,SAAS,GAAY;AACnB,YAAM,OAAO,mBAAmB;AAChC,UAAI,QAAQ,MAAM;AAChB,cAAM,QAAQ,KAAK;AACnB,cAAM,UAAU,KAAK;AACrB,aAAK,gBAAgBA,KAAI;AAAA,MAC3B;AACA,YAAM;AAAA,IACR;AAEA,SAAK,WAAWA,KAAI;AAAA,EACtB;AAAA,EAEA,YAAyB;AACvB,QAAI,KAAK,WAAW,MAAM;AACxB,aAAO,KAAK;AAAA,IACd;AAEA,UAAM,QAAQ;AACd,SAAK,UAAU;AAAA,MACb,IAAOA,OAA2B;AAChC,eAAO,MAAM,QAAQA,KAAI;AAAA,MAC3B;AAAA,MACA,IAAqCA,UAA6B,MAAwB;AACxF,eAAO,MAAM,IAAIA,OAAM,GAAG,IAAI;AAAA,MAChC;AAAA,MACA,WAAWA,OAA2B;AACpC,cAAM,WAAWA,KAAI;AAAA,MACvB;AAAA,MACA,eAAe,OAA2C;AACxD,cAAM,eAAe,KAAK;AAAA,MAC5B;AAAA,MACA,UAAaA,OAAe,UAAqD;AAC/E,eAAO,MAAM,UAAUA,OAAM,MAAM;AACjC,mBAAS,MAAM,YAAYA,KAAI,CAAC;AAAA,QAClC,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO,KAAK;AAAA,EACd;AACF;AAEO,SAAS,cAAqB;AACnC,SAAO,IAAI,MAAM;AACnB;AAEA,SAAS,aAAa,GAAqB;AACzC,SAAO,aAAa,gBAAgB,EAAE,SAAS;AACjD;AAEA,SAAS,qBAAwB,OAAoC;AACnE,MAAI,MAAM,WAAW,WAAW,MAAM,WAAW,SAAS;AACxD,WAAO,MAAM;AAAA,EACf;AACA,MAAI,MAAM,WAAW,SAAS;AAC5B,WAAO,MAAM;AAAA,EACf;AACA,SAAO;AACT;","names":["atom"]}
package/dist/family.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as Atom, S as StalePolicy, a as AtomContext } from './store-DwEjhc31.js';
2
- export { b as AtomConfig, c as AtomState, d as AtomValue, W as WritableAtom, e as createStore } from './store-DwEjhc31.js';
1
+ import { A as Atom, S as StalePolicy, a as AtomContext } from './store-CAz6uZcN.js';
2
+ export { b as AtomConfig, c as AtomState, d as AtomValue, W as WritableAtom, e as createStore } from './store-CAz6uZcN.js';
3
3
  export { atom } from './index.js';
4
4
 
5
5
  type AtomFamilyOptions<Param, Value, Deps extends Record<string, Atom<unknown>>, Args extends readonly unknown[]> = {
package/dist/family.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  atom,
3
3
  atomFamily,
4
4
  createStore
5
- } from "./chunk-6WIZ6MFN.js";
5
+ } from "./chunk-QWP5ITSY.js";
6
6
  export {
7
7
  atom,
8
8
  atomFamily,
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { A as Atom, b as AtomConfig, a as AtomContext, W as WritableAtom } from './store-DwEjhc31.js';
2
- export { h as AtomArgs, c as AtomState, d as AtomValue, I as IsWritable, S as StalePolicy, g as StoreClient, e as createStore } from './store-DwEjhc31.js';
1
+ import { A as Atom, b as AtomConfig, h as WritableAtomContext, W as WritableAtom } from './store-CAz6uZcN.js';
2
+ export { i as AtomArgs, a as AtomContext, c as AtomState, d as AtomValue, I as IsWritable, S as StalePolicy, g as StoreClient, e as createStore } from './store-CAz6uZcN.js';
3
3
 
4
4
  type WritableConfig<Value, Deps extends Record<string, Atom<unknown>>, Args extends readonly unknown[]> = AtomConfig<Value, Deps, Args> & {
5
- set: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;
5
+ set: (ctx: WritableAtomContext<Deps, Value>, ...args: Args) => Promise<void>;
6
6
  };
7
7
  type ReadonlyConfig<Value, Deps extends Record<string, Atom<unknown>>> = Omit<AtomConfig<Value, Deps, readonly []>, "set">;
8
8
  declare function atom<Value, Deps extends Record<string, Atom<unknown>> = Record<never, never>, Args extends readonly unknown[] = readonly []>(config: WritableConfig<Value, Deps, Args>): WritableAtom<Value, Args>;
9
9
  declare function atom<Value, Deps extends Record<string, Atom<unknown>> = Record<never, never>>(config: ReadonlyConfig<Value, Deps>): Atom<Value>;
10
10
 
11
- export { Atom, AtomConfig, AtomContext, WritableAtom, atom };
11
+ export { Atom, AtomConfig, WritableAtom, WritableAtomContext, atom };
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  atom,
3
3
  createStore
4
- } from "./chunk-6WIZ6MFN.js";
4
+ } from "./chunk-QWP5ITSY.js";
5
5
  export {
6
6
  atom,
7
7
  createStore
@@ -1,5 +1,5 @@
1
1
  import { ReactNode } from 'react';
2
- import { f as Store, A as Atom, W as WritableAtom, g as StoreClient } from '../store-DwEjhc31.js';
2
+ import { f as Store, A as Atom, W as WritableAtom, g as StoreClient } from '../store-CAz6uZcN.js';
3
3
 
4
4
  type ProviderProps = {
5
5
  store: Store;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CONFIG
3
- } from "../chunk-6WIZ6MFN.js";
3
+ } from "../chunk-QWP5ITSY.js";
4
4
 
5
5
  // src/react/provider.tsx
6
6
  import { createContext, useContext } from "react";
@@ -22,19 +22,24 @@ type AtomContext<Deps extends Record<string, Atom<unknown>>> = {
22
22
  get: <K extends keyof Deps>(key: K) => Promise<AtomValue<Deps[K]>>;
23
23
  signal: AbortSignal;
24
24
  };
25
+ type WritableAtomContext<Deps extends Record<string, Atom<unknown>>, Value> = AtomContext<Deps> & {
26
+ setOptimisticValue: (value: Value) => void;
27
+ };
25
28
  type AtomConfig<Value, Deps extends Record<string, Atom<unknown>>, Args extends readonly unknown[]> = {
26
29
  dependencies?: Deps;
27
30
  stalePolicy?: StalePolicy;
28
31
  debugLabel?: string;
29
32
  get: (ctx: AtomContext<Deps>) => Promise<Value>;
30
- set?: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;
33
+ set?: (ctx: WritableAtomContext<Deps, Value>, ...args: Args) => Promise<void>;
34
+ onMount?: (set: (value: Value) => void) => (() => void) | void;
31
35
  };
32
36
  interface InternalAtomConfig<Value> {
33
37
  dependencies?: Record<string, Atom<unknown>>;
34
38
  stalePolicy?: StalePolicy;
35
39
  debugLabel?: string;
36
40
  get: (ctx: AtomContext<Record<string, Atom<unknown>>>) => Promise<Value>;
37
- set?: (ctx: AtomContext<Record<string, Atom<unknown>>>, ...args: readonly unknown[]) => Promise<void>;
41
+ set?: (ctx: WritableAtomContext<Record<string, Atom<unknown>>, Value>, ...args: readonly unknown[]) => Promise<void>;
42
+ onMount?: (set: (value: unknown) => void) => (() => void) | void;
38
43
  }
39
44
  interface Atom<out Value> {
40
45
  readonly [CONFIG]: InternalAtomConfig<Value>;
@@ -67,4 +72,4 @@ declare class Store {
67
72
  }
68
73
  declare function createStore(): Store;
69
74
 
70
- export { type Atom as A, type IsWritable as I, type StalePolicy as S, type WritableAtom as W, type AtomContext as a, type AtomConfig as b, type AtomState as c, type AtomValue as d, createStore as e, Store as f, type StoreClient as g, type AtomArgs as h };
75
+ export { type Atom as A, type IsWritable as I, type StalePolicy as S, type WritableAtom as W, type AtomContext as a, type AtomConfig as b, type AtomState as c, type AtomValue as d, createStore as e, Store as f, type StoreClient as g, type WritableAtomContext as h, type AtomArgs as i };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kdeveloper/kvark",
3
- "version": "0.2.0",
3
+ "version": "0.3.1",
4
4
  "description": "Atomic state management with explicit dependency graphs",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -22,7 +22,7 @@
22
22
  }
23
23
  },
24
24
  "scripts": {
25
- "build": "tsup",
25
+ "build": "tsc --noEmit && tsup",
26
26
  "lint": "oxlint --deny-warnings",
27
27
  "lint:fix": "oxlint --fix --deny-warnings",
28
28
  "lint:types": "tsc --noEmit",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/internal/types.ts","../src/internal/atom.ts","../src/internal/family.ts","../src/internal/store.ts"],"sourcesContent":["export const CONFIG = Symbol(\"kvark.config\");\nexport const WRITABLE = Symbol(\"kvark.writable\");\nexport const FAMILY_LINK = Symbol(\"kvark.familyLink\");\n\nexport type AtomState<Value> =\n | { status: \"pending\"; value: undefined; error: undefined }\n | { status: \"stale\"; value: Value; error: undefined }\n | { status: \"fresh\"; value: Value; error: undefined }\n | { status: \"error\"; value: Value | undefined; error: unknown };\n\nexport type StalePolicy = \"keep\" | \"suspend\" | \"reset\";\n\nexport type AtomContext<Deps extends Record<string, Atom<unknown>>> = {\n get: <K extends keyof Deps>(key: K) => Promise<AtomValue<Deps[K]>>;\n signal: AbortSignal;\n};\n\nexport type AtomConfig<\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = {\n dependencies?: Deps;\n stalePolicy?: StalePolicy;\n debugLabel?: string;\n get: (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n};\n\nexport interface InternalAtomConfig<Value> {\n dependencies?: Record<string, Atom<unknown>>;\n stalePolicy?: StalePolicy;\n debugLabel?: string;\n get: (ctx: AtomContext<Record<string, Atom<unknown>>>) => Promise<Value>;\n set?: (\n ctx: AtomContext<Record<string, Atom<unknown>>>,\n ...args: readonly unknown[]\n ) => Promise<void>;\n}\n\nexport interface Atom<out Value> {\n readonly [CONFIG]: InternalAtomConfig<Value>;\n readonly debugLabel: string | undefined;\n}\n\nexport interface WritableAtom<\n out Value,\n in out Args extends readonly unknown[],\n> extends Atom<Value> {\n readonly [WRITABLE]: Args;\n}\n\nexport type AtomValue<A> = A extends Atom<infer V> ? V : never;\nexport type IsWritable<A> = A extends WritableAtom<unknown, readonly unknown[]> ? true : false;\nexport type AtomArgs<A> = A extends WritableAtom<unknown, infer Args> ? Args : never;\n","import type { Atom, AtomConfig, AtomContext, InternalAtomConfig, WritableAtom } from \"./types.js\";\nimport { CONFIG, WRITABLE } from \"./types.js\";\n\ntype WritableConfig<\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = AtomConfig<Value, Deps, Args> & {\n set: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n};\n\ntype ReadonlyConfig<Value, Deps extends Record<string, Atom<unknown>>> = Omit<\n AtomConfig<Value, Deps, readonly []>,\n \"set\"\n>;\n\nexport function atom<\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(config: WritableConfig<Value, Deps, Args>): WritableAtom<Value, Args>;\n\nexport function atom<Value, Deps extends Record<string, Atom<unknown>> = Record<never, never>>(\n config: ReadonlyConfig<Value, Deps>,\n): Atom<Value>;\n\nexport function atom<\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(config: AtomConfig<Value, Deps, Args>): Atom<Value> {\n const internal: InternalAtomConfig<Value> = Object.create(null) as InternalAtomConfig<Value>;\n internal.get = config.get as InternalAtomConfig<Value>[\"get\"];\n\n if (config.debugLabel != null) {\n internal.debugLabel = config.debugLabel;\n }\n if (config.dependencies != null) {\n internal.dependencies = config.dependencies as Record<string, Atom<unknown>>;\n }\n if (config.stalePolicy != null) {\n internal.stalePolicy = config.stalePolicy;\n }\n if (config.set != null) {\n internal.set = config.set as NonNullable<InternalAtomConfig<Value>[\"set\"]>;\n }\n\n if (config.set != null) {\n return {\n [CONFIG]: internal,\n [WRITABLE]: [] as unknown as Args,\n debugLabel: config.debugLabel,\n } as unknown as Atom<Value>;\n }\n\n return {\n [CONFIG]: internal,\n debugLabel: config.debugLabel,\n } as Atom<Value>;\n}\n","import type { Atom, AtomContext, StalePolicy } from \"./types.js\";\nimport { FAMILY_LINK } from \"./types.js\";\nimport { atom } from \"./atom.js\";\n\nexport type AtomFamilyOptions<\n Param,\n Value,\n Deps extends Record<string, Atom<unknown>>,\n Args extends readonly unknown[],\n> = {\n dependencies?: (param: Param) => Deps;\n stalePolicy?: StalePolicy;\n cachePolicy?: \"keep-all\" | \"lru\";\n lruSize?: number;\n debugLabel?: string;\n get: (param: Param) => (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (param: Param) => (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n};\n\nexport interface AtomFamily<Param, Value> {\n (param: Param): Atom<Value>;\n invalidate(param: Param): void;\n invalidateAll(): void;\n remove(param: Param): void;\n getCache(): ReadonlyMap<Param, Atom<Value>>;\n}\n\nexport interface FamilyLink {\n invalidateAtom: (atom: Atom<unknown>) => void;\n registerStore: (cb: (atom: Atom<unknown>) => void) => () => void;\n}\n\nexport function getFamilyLink(target: Atom<unknown>): FamilyLink | undefined {\n return (target as unknown as Record<symbol, FamilyLink | undefined>)[FAMILY_LINK];\n}\n\nexport function atomFamily<\n Param,\n Value,\n Deps extends Record<string, Atom<unknown>> = Record<never, never>,\n Args extends readonly unknown[] = readonly [],\n>(options: AtomFamilyOptions<Param, Value, Deps, Args>): AtomFamily<Param, Value> {\n const cachePolicy = options.cachePolicy ?? \"keep-all\";\n const lruSize = options.lruSize ?? 100;\n const cache = new Map<Param, Atom<Value>>();\n const lruOrder: Param[] = [];\n const storeCallbacks = new Set<(atom: Atom<unknown>) => void>();\n\n const link: FamilyLink = {\n invalidateAtom(target: Atom<unknown>): void {\n for (const cb of storeCallbacks) {\n cb(target);\n }\n },\n registerStore(cb: (atom: Atom<unknown>) => void): () => void {\n storeCallbacks.add(cb);\n return () => {\n storeCallbacks.delete(cb);\n };\n },\n };\n\n function attachLink(target: Atom<Value>): void {\n (target as unknown as Record<symbol, FamilyLink>)[FAMILY_LINK] = link;\n }\n\n function getOrCreate(param: Param): Atom<Value> {\n const cached = cache.get(param);\n if (cached != null) {\n if (cachePolicy === \"lru\") {\n touchLru(param);\n }\n return cached;\n }\n\n const deps = options.dependencies?.(param);\n const getFn = options.get(param);\n const setFn = options.set?.(param);\n\n const label =\n options.debugLabel != null ? `${options.debugLabel}(${String(param)})` : undefined;\n\n const created = createAtom(label, deps, getFn, setFn);\n attachLink(created);\n cache.set(param, created);\n\n if (cachePolicy === \"lru\") {\n lruOrder.push(param);\n evictLru();\n }\n\n return created;\n }\n\n function createAtom(\n label: string | undefined,\n deps: Deps | undefined,\n getFn: (ctx: AtomContext<Deps>) => Promise<Value>,\n setFn: ((ctx: AtomContext<Deps>, ...args: Args) => Promise<void>) | undefined,\n ): Atom<Value> {\n const base = {\n get: getFn as (ctx: AtomContext<Deps>) => Promise<Value>,\n } as {\n debugLabel?: string;\n dependencies?: Deps;\n stalePolicy?: StalePolicy;\n get: (ctx: AtomContext<Deps>) => Promise<Value>;\n set?: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n };\n\n if (label != null) {\n base.debugLabel = label;\n }\n if (deps != null) {\n base.dependencies = deps;\n }\n if (options.stalePolicy != null) {\n base.stalePolicy = options.stalePolicy;\n }\n\n if (setFn != null) {\n base.set = setFn;\n return atom(\n base as typeof base & {\n set: (ctx: AtomContext<Deps>, ...args: Args) => Promise<void>;\n },\n ) as Atom<Value>;\n }\n\n return atom(base as Omit<typeof base, \"set\">);\n }\n\n function touchLru(param: Param): void {\n const idx = lruOrder.indexOf(param);\n if (idx !== -1) {\n lruOrder.splice(idx, 1);\n }\n lruOrder.push(param);\n }\n\n function evictLru(): void {\n while (lruOrder.length > lruSize) {\n const evicted = lruOrder.shift();\n if (evicted !== undefined) {\n cache.delete(evicted);\n }\n }\n }\n\n const family = getOrCreate as AtomFamily<Param, Value>;\n\n family.invalidate = (param: Param): void => {\n const cached = cache.get(param);\n if (cached != null) {\n link.invalidateAtom(cached);\n }\n };\n\n family.invalidateAll = (): void => {\n for (const cached of cache.values()) {\n link.invalidateAtom(cached);\n }\n };\n\n family.remove = (param: Param): void => {\n cache.delete(param);\n if (cachePolicy === \"lru\") {\n const idx = lruOrder.indexOf(param);\n if (idx !== -1) {\n lruOrder.splice(idx, 1);\n }\n }\n };\n\n family.getCache = (): ReadonlyMap<Param, Atom<Value>> => cache;\n\n return family;\n}\n","import type {\n Atom,\n AtomContext,\n AtomState,\n InternalAtomConfig,\n StalePolicy,\n WritableAtom,\n} from \"./types.js\";\nimport { CONFIG } from \"./types.js\";\nimport { getFamilyLink } from \"./family.js\";\n\ntype AtomEntry<V> = {\n state: AtomState<V>;\n version: number;\n promise: Promise<V> | null;\n listeners: Set<() => void>;\n};\n\nexport interface StoreClient {\n get<V>(atom: Atom<V>): Promise<V>;\n set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void>;\n invalidate(atom: Atom<unknown>): void;\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void;\n subscribe<V>(atom: Atom<V>, listener: (state: AtomState<V>) => void): () => void;\n}\n\nconst PENDING_STATE: AtomState<never> = {\n status: \"pending\",\n value: undefined,\n error: undefined,\n} as AtomState<never>;\n\nexport class Store {\n readonly #atoms = new WeakMap<Atom<unknown>, AtomEntry<unknown>>();\n readonly #rdeps = new Map<Atom<unknown>, Set<Atom<unknown>>>();\n readonly #pending = new Set<Atom<unknown>>();\n readonly #controllers = new WeakMap<Atom<unknown>, AbortController>();\n readonly #familyUnsubs = new Set<() => void>();\n readonly #registeredFamilies = new WeakSet<object>();\n #client: StoreClient | null = null;\n\n #getOrCreate<V>(atom: Atom<V>): AtomEntry<V> {\n let entry = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (entry == null) {\n entry = {\n state: PENDING_STATE as AtomState<V>,\n version: 0,\n promise: null,\n listeners: new Set(),\n };\n this.#atoms.set(atom, entry as AtomEntry<unknown>);\n }\n return entry;\n }\n\n resolve<V>(atom: Atom<V>): Promise<V> {\n const entry = this.#getOrCreate(atom);\n if (entry.promise != null) {\n return entry.promise;\n }\n\n const promise = this.#runGet(atom).finally(() => {\n const e = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (e != null) {\n e.promise = null;\n }\n });\n entry.promise = promise;\n return promise;\n }\n\n async #runGet<V>(atom: Atom<V>): Promise<V> {\n const config: InternalAtomConfig<V> = atom[CONFIG];\n const dependencies = config.dependencies;\n const stalePolicy: StalePolicy = config.stalePolicy ?? \"keep\";\n const entry = this.#getOrCreate(atom);\n\n if (dependencies != null) {\n await Promise.all(Object.values(dependencies).map((dep) => this.resolve(dep)));\n this.#registerRdeps(atom, dependencies);\n }\n\n const ctx = this.#makeCtx(atom, dependencies ?? {});\n\n try {\n const value = await config.get(ctx);\n entry.state = { status: \"fresh\", value, error: undefined };\n entry.version++;\n this.#scheduleNotify(atom);\n return value;\n } catch (e: unknown) {\n if (isAbortError(e)) {\n throw e;\n }\n const prevValue = extractPreviousValue<V>(entry.state);\n entry.state = {\n status: \"error\",\n value: stalePolicy === \"keep\" ? prevValue : undefined,\n error: e,\n } as AtomState<V>;\n entry.version++;\n this.#scheduleNotify(atom);\n throw e;\n }\n }\n\n invalidate(atom: Atom<unknown>): void {\n this.#markStale(atom);\n this.#pending.add(atom);\n if (this.#pending.size === 1) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void {\n for (const a of atoms) {\n this.#markStale(a);\n this.#pending.add(a);\n }\n if (this.#pending.size > 0) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n #markStale(atom: Atom<unknown>, visited = new Set<Atom<unknown>>()): void {\n if (visited.has(atom)) {\n return;\n }\n visited.add(atom);\n\n const entry = this.#atoms.get(atom);\n if (entry != null) {\n const stalePolicy: StalePolicy = atom[CONFIG].stalePolicy ?? \"keep\";\n\n if (entry.state.status === \"fresh\") {\n if (stalePolicy === \"reset\") {\n entry.state = PENDING_STATE;\n } else {\n entry.state = { ...entry.state, status: \"stale\" };\n }\n }\n\n entry.promise = null;\n this.#controllers.get(atom)?.abort();\n }\n\n const rdeps = this.#rdeps.get(atom);\n if (rdeps != null) {\n for (const dep of rdeps) {\n this.#markStale(dep, visited);\n }\n }\n }\n\n #flushPending(): void {\n const batch = [...this.#pending];\n this.#pending.clear();\n const toNotify = new Set<() => void>();\n for (const a of batch) {\n const entry = this.#atoms.get(a);\n if (entry != null) {\n for (const l of entry.listeners) {\n toNotify.add(l);\n }\n }\n }\n for (const l of toNotify) {\n l();\n }\n }\n\n #scheduleNotify(atom: Atom<unknown>): void {\n this.#pending.add(atom);\n if (this.#pending.size === 1) {\n queueMicrotask(() => {\n this.#flushPending();\n });\n }\n }\n\n #registerRdeps(atom: Atom<unknown>, deps: Record<string, Atom<unknown>>): void {\n for (const dep of Object.values(deps)) {\n let set = this.#rdeps.get(dep);\n if (set == null) {\n set = new Set();\n this.#rdeps.set(dep, set);\n }\n set.add(atom);\n }\n }\n\n #makeCtx(\n atom: Atom<unknown>,\n deps: Record<string, Atom<unknown>>,\n ): AtomContext<Record<string, Atom<unknown>>> {\n this.#controllers.get(atom)?.abort();\n const controller = new AbortController();\n this.#controllers.set(atom, controller);\n\n return {\n signal: controller.signal,\n get: async (key: string) => {\n const dep = deps[key];\n if (dep == null) {\n throw new Error(`Unknown dependency key: \"${String(key)}\"`);\n }\n return this.resolve(dep);\n },\n };\n }\n\n subscribe<V>(atom: Atom<V>, listener: () => void): () => void {\n const entry = this.#getOrCreate(atom);\n entry.listeners.add(listener);\n\n this.#autoRegisterFamily(atom);\n\n if (entry.state.status === \"pending\" && entry.promise == null) {\n void this.resolve(atom);\n }\n\n return () => {\n entry.listeners.delete(listener);\n };\n }\n\n #autoRegisterFamily(atom: Atom<unknown>): void {\n const link = getFamilyLink(atom);\n if (link == null) {\n return;\n }\n if (this.#registeredFamilies.has(link)) {\n return;\n }\n this.#registeredFamilies.add(link);\n const unsub = link.registerStore((target: Atom<unknown>) => {\n this.invalidate(target);\n });\n this.#familyUnsubs.add(unsub);\n }\n\n getSnapshot<V>(atom: Atom<V>): AtomState<V> {\n return this.#getOrCreate(atom).state;\n }\n\n getServerSnapshot<V>(atom: Atom<V>): AtomState<V> {\n const entry = this.#atoms.get(atom) as AtomEntry<V> | undefined;\n if (entry != null) {\n return entry.state;\n }\n return PENDING_STATE as AtomState<V>;\n }\n\n async set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void> {\n const config: InternalAtomConfig<V> = atom[CONFIG];\n if (config.set == null) {\n throw new Error(\n `Atom${atom.debugLabel != null ? ` \"${atom.debugLabel}\"` : \"\"} is not writable`,\n );\n }\n\n const deps = config.dependencies ?? {};\n const ctx = this.#makeCtx(atom, deps);\n await config.set(ctx, ...args);\n this.invalidate(atom);\n }\n\n getClient(): StoreClient {\n if (this.#client != null) {\n return this.#client;\n }\n\n const store = this;\n this.#client = {\n get<V>(atom: Atom<V>): Promise<V> {\n return store.resolve(atom);\n },\n set<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>, ...args: A): Promise<void> {\n return store.set(atom, ...args);\n },\n invalidate(atom: Atom<unknown>): void {\n store.invalidate(atom);\n },\n invalidateMany(atoms: ReadonlyArray<Atom<unknown>>): void {\n store.invalidateMany(atoms);\n },\n subscribe<V>(atom: Atom<V>, listener: (state: AtomState<V>) => void): () => void {\n return store.subscribe(atom, () => {\n listener(store.getSnapshot(atom));\n });\n },\n };\n return this.#client;\n }\n}\n\nexport function createStore(): Store {\n return new Store();\n}\n\nfunction isAbortError(e: unknown): boolean {\n return e instanceof DOMException && e.name === \"AbortError\";\n}\n\nfunction extractPreviousValue<V>(state: AtomState<V>): V | undefined {\n if (state.status === \"stale\" || state.status === \"fresh\") {\n return state.value;\n }\n if (state.status === \"error\") {\n return state.value;\n }\n return undefined;\n}\n"],"mappings":"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","names":["atom"]}