@kdeveloper/kvark 0.6.0 → 0.6.2
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 +171 -17
- package/dist/vue/index.d.mts +6 -4
- package/dist/vue/index.d.mts.map +1 -1
- package/dist/vue/index.mjs +10 -2
- package/dist/vue/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# @kdeveloper/kvark
|
|
2
2
|
|
|
3
|
-
Atomic state management for React with **explicit dependency graphs**, stale-while-revalidate, and first-class external invalidation.
|
|
3
|
+
Atomic state management for **React** and **Vue 3** with **explicit dependency graphs**, stale-while-revalidate, and first-class external invalidation.
|
|
4
4
|
|
|
5
|
-
Inspired by Jotai, but built around a key difference: dependencies are declared upfront rather than inferred at runtime. This makes the data graph statically analysable, enables parallel loading, and allows invalidation from anywhere — WebSocket handlers, SSE streams, timers, Service Workers — without
|
|
5
|
+
Inspired by Jotai, but built around a key difference: dependencies are declared upfront rather than inferred at runtime. This makes the data graph statically analysable, enables parallel loading, and allows invalidation from anywhere — WebSocket handlers, SSE streams, timers, Service Workers — without framework hooks.
|
|
6
6
|
|
|
7
|
-
```
|
|
8
|
-
|
|
9
|
-
#
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @kdeveloper/kvark
|
|
9
|
+
# optional peers: react >=18 and/or vue >=3.4 (install the one you use)
|
|
10
10
|
```
|
|
11
11
|
|
|
12
12
|
## Why Kvark?
|
|
@@ -26,7 +26,7 @@ npm install @kdeveloper/kvark
|
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
pnpm add @kdeveloper/kvark
|
|
29
|
-
# react >=18
|
|
29
|
+
# optional peers: react >=18 and/or vue >=3.4
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## Quick Start
|
|
@@ -73,6 +73,69 @@ function UserCard() {
|
|
|
73
73
|
}
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
+
### Vue 3
|
|
77
|
+
|
|
78
|
+
The same atoms and store work with `@kdeveloper/kvark/vue`. Composables mirror the React hooks; `useAtomValue` returns an **awaitable** [`shallowRef`](https://vuejs.org/api/reactivity-advanced.html#shallowref) — use `.value` in `<script setup>` (templates unwrap refs automatically). The ref also implements `PromiseLike`, so `await useAtomValue(atom)` in an async `setup` suspends until the first `get` resolves — see [Suspense (Vue 3)](#suspense-vue-3).
|
|
79
|
+
|
|
80
|
+
Composables must run **inside** a `Provider` subtree. Put `useAtomValue` / `useAtom` in a child component (or a nested route), not in the same component that renders `Provider` without passing the store through a wrapper.
|
|
81
|
+
|
|
82
|
+
`atoms.ts` — lift the atom definitions from the Quick Start into a shared module:
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { atom } from "@kdeveloper/kvark";
|
|
86
|
+
|
|
87
|
+
export const userIdAtom = atom({
|
|
88
|
+
debugLabel: "userId",
|
|
89
|
+
get: async () => 1,
|
|
90
|
+
set: async () => {},
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
export const userAtom = atom({
|
|
94
|
+
debugLabel: "user",
|
|
95
|
+
dependencies: { userId: userIdAtom },
|
|
96
|
+
get: async (ctx) => {
|
|
97
|
+
const id = await ctx.get("userId");
|
|
98
|
+
const res = await fetch(`/api/users/${id}`, { signal: ctx.signal });
|
|
99
|
+
return res.json();
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`UserCard.vue`
|
|
105
|
+
|
|
106
|
+
```vue
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
import { useAtomValue } from "@kdeveloper/kvark/vue";
|
|
109
|
+
import { userAtom } from "./atoms";
|
|
110
|
+
|
|
111
|
+
const user = useAtomValue(userAtom);
|
|
112
|
+
</script>
|
|
113
|
+
|
|
114
|
+
<template>
|
|
115
|
+
<div>{{ user.name }}</div>
|
|
116
|
+
</template>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
`App.vue`
|
|
120
|
+
|
|
121
|
+
```vue
|
|
122
|
+
<script setup lang="ts">
|
|
123
|
+
import { createStore } from "@kdeveloper/kvark";
|
|
124
|
+
import { Provider } from "@kdeveloper/kvark/vue";
|
|
125
|
+
import UserCard from "./UserCard.vue";
|
|
126
|
+
|
|
127
|
+
const store = createStore();
|
|
128
|
+
</script>
|
|
129
|
+
|
|
130
|
+
<template>
|
|
131
|
+
<Provider :store="store">
|
|
132
|
+
<UserCard />
|
|
133
|
+
</Provider>
|
|
134
|
+
</template>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Unlike React, Vue’s `<Suspense>` only activates for **async** child components. Since `useAtomValue` and `useAtom` return `PromiseLike` refs, just **`await`** them in an async `setup` (or top-level `<script setup>`) to trigger Suspense — see [Suspense (Vue 3)](#suspense-vue-3).
|
|
138
|
+
|
|
76
139
|
## Core Concepts
|
|
77
140
|
|
|
78
141
|
### Atoms
|
|
@@ -174,7 +237,7 @@ Both can update an atom's cached value, but they serve different purposes:
|
|
|
174
237
|
|
|
175
238
|
### `onMount`
|
|
176
239
|
|
|
177
|
-
Optional lifecycle hook that runs when the atom **first gains a subscriber** in a store (for example when a
|
|
240
|
+
Optional lifecycle hook that runs when the atom **first gains a subscriber** in a store (for example when a 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`.
|
|
178
241
|
|
|
179
242
|
You may return a cleanup function; it runs when the **last** subscriber unsubscribes (for example when the last mounted consumer unmounts). If several components subscribe to the same atom, `onMount` runs once and the cleanup runs once after all of them unsubscribe.
|
|
180
243
|
|
|
@@ -300,10 +363,14 @@ A built-in helper that serialises plain objects and arrays into a deterministic
|
|
|
300
363
|
- Not supported: class instances (other than plain `Object`/`Array`), `Map`, `Set`, `Symbol`, functions, circular references.
|
|
301
364
|
- Memoises by object reference via a `WeakMap` — repeated calls with the **same object reference** are O(1). Treat param objects as **immutable** after the first call; mutating an object after it has been memoised will silently return the stale key.
|
|
302
365
|
|
|
303
|
-
## React
|
|
366
|
+
## React (`@kdeveloper/kvark/react`)
|
|
304
367
|
|
|
305
368
|
All hooks must be used inside a `<Provider>`.
|
|
306
369
|
|
|
370
|
+
### `useStore`
|
|
371
|
+
|
|
372
|
+
Returns the `Store` instance from context — for advanced cases (e.g. calling `store.resolve` in async setup patterns) or when you need the store outside atom helpers.
|
|
373
|
+
|
|
307
374
|
### `useAtomValue`
|
|
308
375
|
|
|
309
376
|
Reads an atom value and subscribes to updates. Suspends on first load.
|
|
@@ -346,9 +413,75 @@ const readBalance = useAtomContext(async (client) => {
|
|
|
346
413
|
const balance = await readBalance();
|
|
347
414
|
```
|
|
348
415
|
|
|
416
|
+
## Vue 3 (`@kdeveloper/kvark/vue`)
|
|
417
|
+
|
|
418
|
+
Same composable names and behaviour as React: `Provider`, `useStore`, `useAtomValue`, `useSetAtom`, `useAtom`, `useAtomContext`. Wrap your app (or subtree) with `Provider` and pass `:store="store"`.
|
|
419
|
+
|
|
420
|
+
`useAtomValue` / `useAtom` expose values as **awaitable shallow refs** (`PromiseLike`) — in script code use `.value`; in templates Vue unwraps refs for you. `await useAtomValue(atom)` in an async setup suspends until the atom's first `get` resolves, integrating with `<Suspense>`.
|
|
421
|
+
|
|
422
|
+
### Suspense (Vue 3)
|
|
423
|
+
|
|
424
|
+
Vue’s `<Suspense>` boundary applies to **async** components (e.g. **`async setup`** or [top-level `await` in `<script setup>`](https://vuejs.org/guide/built-ins/suspense.html#async-setup)), not to composables alone. Because `useAtomValue` and `useAtom` return `PromiseLike` refs, you can simply `await` them to suspend until the first `get` resolves.
|
|
425
|
+
|
|
426
|
+
`atoms.ts`
|
|
427
|
+
|
|
428
|
+
```ts
|
|
429
|
+
import { atom } from "@kdeveloper/kvark";
|
|
430
|
+
|
|
431
|
+
export const slowAtom = atom({
|
|
432
|
+
get: async () => {
|
|
433
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
434
|
+
return "ready";
|
|
435
|
+
},
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
`AsyncRead.vue` (under `Provider`; top-level `await` makes this component async for Suspense)
|
|
440
|
+
|
|
441
|
+
```vue
|
|
442
|
+
<script setup lang="ts">
|
|
443
|
+
import { useAtomValue } from "@kdeveloper/kvark/vue";
|
|
444
|
+
import { slowAtom } from "./atoms";
|
|
445
|
+
|
|
446
|
+
const value = await useAtomValue(slowAtom);
|
|
447
|
+
</script>
|
|
448
|
+
|
|
449
|
+
<template>
|
|
450
|
+
<div>{{ value }}</div>
|
|
451
|
+
</template>
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
`App.vue`
|
|
455
|
+
|
|
456
|
+
```vue
|
|
457
|
+
<script setup lang="ts">
|
|
458
|
+
import { Suspense } from "vue";
|
|
459
|
+
import { createStore } from "@kdeveloper/kvark";
|
|
460
|
+
import { Provider } from "@kdeveloper/kvark/vue";
|
|
461
|
+
import AsyncRead from "./AsyncRead.vue";
|
|
462
|
+
|
|
463
|
+
const store = createStore();
|
|
464
|
+
</script>
|
|
465
|
+
|
|
466
|
+
<template>
|
|
467
|
+
<Provider :store="store">
|
|
468
|
+
<Suspense>
|
|
469
|
+
<template #default>
|
|
470
|
+
<AsyncRead />
|
|
471
|
+
</template>
|
|
472
|
+
<template #fallback>
|
|
473
|
+
<div>Loading…</div>
|
|
474
|
+
</template>
|
|
475
|
+
</Suspense>
|
|
476
|
+
</Provider>
|
|
477
|
+
</template>
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Alternatively, use **`defineComponent({ async setup() { ... } })`** and `await useAtomValue(slowAtom)` before returning the render function — the same pattern as in `test/vue/hooks.test.ts`. `useAtom` is also awaitable: `const [ref, set] = await useAtom(writableAtom)`.
|
|
481
|
+
|
|
349
482
|
## External Invalidation
|
|
350
483
|
|
|
351
|
-
`StoreClient` exposes the store's full capabilities outside
|
|
484
|
+
`StoreClient` exposes the store's full capabilities outside your component tree — useful for WebSocket handlers, SSE streams, polling timers, and Service Workers.
|
|
352
485
|
|
|
353
486
|
```ts
|
|
354
487
|
import { createStore } from "@kdeveloper/kvark";
|
|
@@ -377,7 +510,7 @@ sse.addEventListener("prices.updated", () => {
|
|
|
377
510
|
// Polling
|
|
378
511
|
setInterval(() => client.invalidate(statusAtom), 30_000);
|
|
379
512
|
|
|
380
|
-
// Subscribe to state changes outside
|
|
513
|
+
// Subscribe to state changes outside components
|
|
381
514
|
const unsub = client.subscribe(userAtom, (state) => {
|
|
382
515
|
if (state.status === "fresh") {
|
|
383
516
|
analytics.identify(state.value.id);
|
|
@@ -424,6 +557,8 @@ interface StoreClient {
|
|
|
424
557
|
|
|
425
558
|
## Provider and Store Setup
|
|
426
559
|
|
|
560
|
+
**React**
|
|
561
|
+
|
|
427
562
|
```tsx
|
|
428
563
|
// store.ts
|
|
429
564
|
import { createStore } from "@kdeveloper/kvark";
|
|
@@ -442,6 +577,23 @@ export function App() {
|
|
|
442
577
|
}
|
|
443
578
|
```
|
|
444
579
|
|
|
580
|
+
**Vue 3**
|
|
581
|
+
|
|
582
|
+
```vue
|
|
583
|
+
<script setup lang="ts">
|
|
584
|
+
import { createStore } from "@kdeveloper/kvark";
|
|
585
|
+
import { Provider } from "@kdeveloper/kvark/vue";
|
|
586
|
+
|
|
587
|
+
const store = createStore();
|
|
588
|
+
</script>
|
|
589
|
+
|
|
590
|
+
<template>
|
|
591
|
+
<Provider :store="store">
|
|
592
|
+
<RouterView />
|
|
593
|
+
</Provider>
|
|
594
|
+
</template>
|
|
595
|
+
```
|
|
596
|
+
|
|
445
597
|
## Utility Types
|
|
446
598
|
|
|
447
599
|
```ts
|
|
@@ -454,19 +606,21 @@ type Writable = IsWritable<typeof countAtom>; // → true | false
|
|
|
454
606
|
|
|
455
607
|
## Package Structure
|
|
456
608
|
|
|
457
|
-
| Import | Contents
|
|
458
|
-
| -------------------------- |
|
|
459
|
-
| `@kdeveloper/kvark` | `atom`, `createStore`, all types
|
|
460
|
-
| `@kdeveloper/kvark/react` | `Provider`, `useAtomValue`, `useSetAtom`, `useAtom`, `useAtomContext`
|
|
461
|
-
| `@kdeveloper/kvark/
|
|
609
|
+
| Import | Contents |
|
|
610
|
+
| -------------------------- | ------------------------------------------------------------------------------------------- |
|
|
611
|
+
| `@kdeveloper/kvark` | `atom`, `createStore`, all types |
|
|
612
|
+
| `@kdeveloper/kvark/react` | `Provider`, `useStore`, `useAtomValue`, `useSetAtom`, `useAtom`, `useAtomContext` |
|
|
613
|
+
| `@kdeveloper/kvark/vue` | `Provider`, `useStore`, `useAtomValue`, `useSetAtom`, `useAtom`, `useAtomContext` |
|
|
614
|
+
| `@kdeveloper/kvark/family` | `atomFamily`, `stableFamilyKey`, re-exports `atom` / `createStore` and core types |
|
|
462
615
|
|
|
463
|
-
The core (`@kdeveloper/kvark`) has **zero runtime dependencies**. React
|
|
616
|
+
The core (`@kdeveloper/kvark`) has **zero runtime dependencies**. **React** and **Vue** are optional peer dependencies — install the framework you use and import from `/react` or `/vue`.
|
|
464
617
|
|
|
465
618
|
## Requirements
|
|
466
619
|
|
|
467
620
|
- **Node.js** ≥ 20
|
|
468
621
|
- **TypeScript** ≥ 6 (strict mode recommended)
|
|
469
|
-
- **React** ≥ 18 (for `@kdeveloper/kvark/react`)
|
|
622
|
+
- **React** ≥ 18 (optional peer, for `@kdeveloper/kvark/react`)
|
|
623
|
+
- **Vue** ≥ 3.4 (optional peer, for `@kdeveloper/kvark/vue`)
|
|
470
624
|
|
|
471
625
|
## License
|
|
472
626
|
|
package/dist/vue/index.d.mts
CHANGED
|
@@ -24,19 +24,21 @@ type ObservedValue<V> = {
|
|
|
24
24
|
isStale: boolean;
|
|
25
25
|
error: unknown;
|
|
26
26
|
};
|
|
27
|
-
|
|
27
|
+
type ThenableShallowRef<V> = Readonly<ShallowRef<V>> & PromiseLike<Readonly<ShallowRef<V>>>;
|
|
28
|
+
declare function useAtomValue<V>(atom: Atom<V>): ThenableShallowRef<V>;
|
|
28
29
|
declare function useAtomValue<V>(atom: Atom<V>, opts: {
|
|
29
30
|
observe: true;
|
|
30
|
-
}):
|
|
31
|
+
}): ThenableShallowRef<ObservedValue<V>>;
|
|
31
32
|
//#endregion
|
|
32
33
|
//#region src/vue/use-set-atom.d.ts
|
|
33
34
|
declare function useSetAtom<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>): (...args: A) => Promise<void>;
|
|
34
35
|
//#endregion
|
|
35
36
|
//#region src/vue/use-atom.d.ts
|
|
36
|
-
|
|
37
|
+
type ThenableAtomTuple<V, A extends readonly unknown[]> = readonly [ThenableShallowRef<V>, (...args: A) => Promise<void>] & PromiseLike<readonly [ThenableShallowRef<V>, (...args: A) => Promise<void>]>;
|
|
38
|
+
declare function useAtom<V, A extends readonly unknown[]>(atom: WritableAtom<V, A>): ThenableAtomTuple<V, A>;
|
|
37
39
|
//#endregion
|
|
38
40
|
//#region src/vue/use-atom-context.d.ts
|
|
39
41
|
declare function useAtomContext<R>(callback: (ctx: StoreClient) => Promise<R>): () => Promise<R>;
|
|
40
42
|
//#endregion
|
|
41
|
-
export { Provider, useAtom, useAtomContext, useAtomValue, useSetAtom, useStore };
|
|
43
|
+
export { Provider, type ThenableShallowRef, useAtom, useAtomContext, useAtomValue, useSetAtom, useStore };
|
|
42
44
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/vue/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vue/provider.ts","../../src/vue/use-atom-value.ts","../../src/vue/use-set-atom.ts","../../src/vue/use-atom.ts","../../src/vue/use-atom-context.ts"],"mappings":";;;;;cAWa,QAAA,QAAQ,eAAA,CAGgB,KAAA,CAHhB,gBAAA;;UAGQ,QAAA,CAAS,KAAA;;;sBAHjB,KAAA,CAAA,YAAA;;gIAGgB,KAAA,CAAA,gBAAA;;UAAR,QAAA,CAAS,KAAA;;;;iBAQtB,QAAA,CAAA,GAAY,KAAA;;;KClBvB,aAAA;EACH,KAAA,EAAO,CAAA;EACP,OAAA;EACA,KAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/vue/provider.ts","../../src/vue/use-atom-value.ts","../../src/vue/use-set-atom.ts","../../src/vue/use-atom.ts","../../src/vue/use-atom-context.ts"],"mappings":";;;;;cAWa,QAAA,QAAQ,eAAA,CAGgB,KAAA,CAHhB,gBAAA;;UAGQ,QAAA,CAAS,KAAA;;;sBAHjB,KAAA,CAAA,YAAA;;gIAGgB,KAAA,CAAA,gBAAA;;UAAR,QAAA,CAAS,KAAA;;;;iBAQtB,QAAA,CAAA,GAAY,KAAA;;;KClBvB,aAAA;EACH,KAAA,EAAO,CAAA;EACP,OAAA;EACA,KAAA;AAAA;AAAA,KAGU,kBAAA,MAAwB,QAAA,CAAS,UAAA,CAAW,CAAA,KACtD,WAAA,CAAY,QAAA,CAAS,UAAA,CAAW,CAAA;AAAA,iBAElB,YAAA,GAAA,CAAgB,IAAA,EAAM,IAAA,CAAK,CAAA,IAAK,kBAAA,CAAmB,CAAA;AAAA,iBACnD,YAAA,GAAA,CACd,IAAA,EAAM,IAAA,CAAK,CAAA,GACX,IAAA;EAAQ,OAAA;AAAA,IACP,kBAAA,CAAmB,aAAA,CAAc,CAAA;;;iBCdpB,UAAA,iCAAA,CACd,IAAA,EAAM,YAAA,CAAa,CAAA,EAAG,CAAA,QACjB,IAAA,EAAM,CAAA,KAAM,OAAA;;;KCDd,iBAAA,8CACH,kBAAA,CAAmB,CAAA,OACf,IAAA,EAAM,CAAA,KAAM,OAAA,UAEhB,WAAA,WACY,kBAAA,CAAmB,CAAA,OAAQ,IAAA,EAAM,CAAA,KAAM,OAAA;AAAA,iBAGrC,OAAA,iCAAA,CACd,IAAA,EAAM,YAAA,CAAa,CAAA,EAAG,CAAA,IACrB,iBAAA,CAAkB,CAAA,EAAG,CAAA;;;iBCXR,cAAA,GAAA,CAAkB,QAAA,GAAW,GAAA,EAAK,WAAA,KAAgB,OAAA,CAAQ,CAAA,UAAW,OAAA,CAAQ,CAAA"}
|
package/dist/vue/index.mjs
CHANGED
|
@@ -61,7 +61,11 @@ function useAtomValue(atom, opts) {
|
|
|
61
61
|
result.value = derive(store.getSnapshot(atom));
|
|
62
62
|
}));
|
|
63
63
|
});
|
|
64
|
-
|
|
64
|
+
const ref = readonly(result);
|
|
65
|
+
return new Proxy(ref, { get(target, prop, receiver) {
|
|
66
|
+
if (prop === "then") return (onFulfilled, onRejected) => store.resolve(atom).then(() => ref, onRejected).then(onFulfilled);
|
|
67
|
+
return Reflect.get(target, prop, receiver);
|
|
68
|
+
} });
|
|
65
69
|
}
|
|
66
70
|
//#endregion
|
|
67
71
|
//#region src/vue/use-set-atom.ts
|
|
@@ -72,7 +76,11 @@ function useSetAtom(atom) {
|
|
|
72
76
|
//#endregion
|
|
73
77
|
//#region src/vue/use-atom.ts
|
|
74
78
|
function useAtom(atom) {
|
|
75
|
-
|
|
79
|
+
const value = useAtomValue(atom);
|
|
80
|
+
const setter = useSetAtom(atom);
|
|
81
|
+
return Object.assign([value, setter], { then(onFulfilled, onRejected) {
|
|
82
|
+
return Promise.resolve(value).then(() => [value, setter], onRejected).then(onFulfilled);
|
|
83
|
+
} });
|
|
76
84
|
}
|
|
77
85
|
//#endregion
|
|
78
86
|
//#region src/vue/use-atom-context.ts
|
package/dist/vue/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vue/provider.ts","../../src/vue/use-atom-value.ts","../../src/vue/use-set-atom.ts","../../src/vue/use-atom.ts","../../src/vue/use-atom-context.ts"],"sourcesContent":["import {\n type InjectionKey,\n type PropType,\n defineComponent,\n inject,\n provide,\n} from \"vue\";\nimport { Store } from \"@/internal/store.js\";\n\nconst StoreKey: InjectionKey<Store> = Symbol(\"kvark-store\");\n\nexport const Provider = defineComponent({\n name: \"KvarkProvider\",\n props: {\n store: { type: Object as PropType<Store>, required: true },\n },\n setup(props, { slots }) {\n provide(StoreKey, props.store);\n return () => slots[\"default\"]?.();\n },\n});\n\nexport function useStore(): Store {\n const store = inject(StoreKey);\n if (store == null) {\n throw new Error(\"useStore must be used within a <Provider>\");\n }\n return store;\n}\n","import { type ShallowRef, readonly, shallowRef, watchEffect } from \"vue\";\nimport type { Atom, AtomState } from \"@/internal/types.js\";\nimport { useStore } from \"@/vue/provider.js\";\n\ntype ObservedValue<V> = {\n value: V;\n isStale: boolean;\n error: unknown;\n};\n\nexport function useAtomValue<V>(atom: Atom<V>):
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/vue/provider.ts","../../src/vue/use-atom-value.ts","../../src/vue/use-set-atom.ts","../../src/vue/use-atom.ts","../../src/vue/use-atom-context.ts"],"sourcesContent":["import {\n type InjectionKey,\n type PropType,\n defineComponent,\n inject,\n provide,\n} from \"vue\";\nimport { Store } from \"@/internal/store.js\";\n\nconst StoreKey: InjectionKey<Store> = Symbol(\"kvark-store\");\n\nexport const Provider = defineComponent({\n name: \"KvarkProvider\",\n props: {\n store: { type: Object as PropType<Store>, required: true },\n },\n setup(props, { slots }) {\n provide(StoreKey, props.store);\n return () => slots[\"default\"]?.();\n },\n});\n\nexport function useStore(): Store {\n const store = inject(StoreKey);\n if (store == null) {\n throw new Error(\"useStore must be used within a <Provider>\");\n }\n return store;\n}\n","import { type ShallowRef, readonly, shallowRef, watchEffect } from \"vue\";\nimport type { Atom, AtomState } from \"@/internal/types.js\";\nimport { useStore } from \"@/vue/provider.js\";\n\ntype ObservedValue<V> = {\n value: V;\n isStale: boolean;\n error: unknown;\n};\n\nexport type ThenableShallowRef<V> = Readonly<ShallowRef<V>> &\n PromiseLike<Readonly<ShallowRef<V>>>;\n\nexport function useAtomValue<V>(atom: Atom<V>): ThenableShallowRef<V>;\nexport function useAtomValue<V>(\n atom: Atom<V>,\n opts: { observe: true },\n): ThenableShallowRef<ObservedValue<V>>;\nexport function useAtomValue<V>(\n atom: Atom<V>,\n opts?: { observe: true },\n): ThenableShallowRef<V | ObservedValue<V>> {\n const store = useStore();\n\n function derive(snapshot: AtomState<V>): V | ObservedValue<V> {\n if (snapshot.status === \"pending\") {\n void store.resolve(atom);\n if (opts?.observe === true) {\n return { value: undefined as V, isStale: false, error: undefined };\n }\n return undefined as V;\n }\n\n if (snapshot.status === \"stale\") {\n void store.resolve(atom);\n if (opts?.observe === true) {\n return { value: snapshot.value, isStale: true, error: undefined };\n }\n return snapshot.value;\n }\n\n if (snapshot.status === \"error\") {\n if (opts?.observe === true && snapshot.value !== undefined) {\n return { value: snapshot.value as V, isStale: false, error: snapshot.error };\n }\n throw snapshot.error;\n }\n\n // status === \"fresh\"\n if (opts?.observe === true) {\n return { value: snapshot.value, isStale: false, error: undefined };\n }\n return snapshot.value;\n }\n\n const result = shallowRef(derive(store.getSnapshot(atom)));\n\n watchEffect((onCleanup) => {\n const unsub = store.subscribe(atom, () => {\n result.value = derive(store.getSnapshot(atom));\n });\n onCleanup(unsub);\n });\n\n const ref = readonly(result) as Readonly<ShallowRef<V | ObservedValue<V>>>;\n\n return new Proxy(ref, {\n get(target, prop, receiver) {\n if (prop === \"then\") {\n return (\n onFulfilled?: (v: typeof ref) => unknown,\n onRejected?: (e: unknown) => unknown,\n ) =>\n store\n .resolve(atom)\n .then(() => ref, onRejected)\n .then(onFulfilled as never);\n }\n return Reflect.get(target, prop, receiver);\n },\n }) as ThenableShallowRef<V | ObservedValue<V>>;\n}\n","import type { WritableAtom } from \"@/internal/types.js\";\nimport { useStore } from \"@/vue/provider.js\";\n\nexport function useSetAtom<V, A extends readonly unknown[]>(\n atom: WritableAtom<V, A>,\n): (...args: A) => Promise<void> {\n const store = useStore();\n return (...args: A): Promise<void> => store.set(atom, ...args);\n}\n","import type { WritableAtom } from \"@/internal/types.js\";\nimport { type ThenableShallowRef, useAtomValue } from \"@/vue/use-atom-value.js\";\nimport { useSetAtom } from \"@/vue/use-set-atom.js\";\n\ntype ThenableAtomTuple<V, A extends readonly unknown[]> = readonly [\n ThenableShallowRef<V>,\n (...args: A) => Promise<void>,\n] &\n PromiseLike<\n readonly [ThenableShallowRef<V>, (...args: A) => Promise<void>]\n >;\n\nexport function useAtom<V, A extends readonly unknown[]>(\n atom: WritableAtom<V, A>,\n): ThenableAtomTuple<V, A> {\n const value = useAtomValue(atom);\n const setter = useSetAtom(atom);\n const tuple = [value, setter] as const;\n\n return Object.assign(tuple, {\n then(\n onFulfilled?: (v: typeof tuple) => unknown,\n onRejected?: (e: unknown) => unknown,\n ) {\n return Promise.resolve(value).then(\n () => [value, setter] as const,\n onRejected,\n ).then(onFulfilled as never);\n },\n }) as ThenableAtomTuple<V, A>;\n}\n","import type { StoreClient } from \"@/internal/store.js\";\nimport { useStore } from \"@/vue/provider.js\";\n\nexport function useAtomContext<R>(callback: (ctx: StoreClient) => Promise<R>): () => Promise<R> {\n const store = useStore();\n return (): Promise<R> => callback(store.getClient());\n}\n"],"mappings":";;AASA,MAAM,WAAgC,OAAO,cAAc;AAE3D,MAAa,WAAW,gBAAgB;CACtC,MAAM;CACN,OAAO,EACL,OAAO;EAAE,MAAM;EAA2B,UAAU;EAAM,EAC3D;CACD,MAAM,OAAO,EAAE,SAAS;AACtB,UAAQ,UAAU,MAAM,MAAM;AAC9B,eAAa,MAAM,cAAc;;CAEpC,CAAC;AAEF,SAAgB,WAAkB;CAChC,MAAM,QAAQ,OAAO,SAAS;AAC9B,KAAI,SAAS,KACX,OAAM,IAAI,MAAM,4CAA4C;AAE9D,QAAO;;;;ACTT,SAAgB,aACd,MACA,MAC0C;CAC1C,MAAM,QAAQ,UAAU;CAExB,SAAS,OAAO,UAA8C;AAC5D,MAAI,SAAS,WAAW,WAAW;AAC5B,SAAM,QAAQ,KAAK;AACxB,OAAI,MAAM,YAAY,KACpB,QAAO;IAAE,OAAO,KAAA;IAAgB,SAAS;IAAO,OAAO,KAAA;IAAW;AAEpE;;AAGF,MAAI,SAAS,WAAW,SAAS;AAC1B,SAAM,QAAQ,KAAK;AACxB,OAAI,MAAM,YAAY,KACpB,QAAO;IAAE,OAAO,SAAS;IAAO,SAAS;IAAM,OAAO,KAAA;IAAW;AAEnE,UAAO,SAAS;;AAGlB,MAAI,SAAS,WAAW,SAAS;AAC/B,OAAI,MAAM,YAAY,QAAQ,SAAS,UAAU,KAAA,EAC/C,QAAO;IAAE,OAAO,SAAS;IAAY,SAAS;IAAO,OAAO,SAAS;IAAO;AAE9E,SAAM,SAAS;;AAIjB,MAAI,MAAM,YAAY,KACpB,QAAO;GAAE,OAAO,SAAS;GAAO,SAAS;GAAO,OAAO,KAAA;GAAW;AAEpE,SAAO,SAAS;;CAGlB,MAAM,SAAS,WAAW,OAAO,MAAM,YAAY,KAAK,CAAC,CAAC;AAE1D,cAAa,cAAc;AAIzB,YAHc,MAAM,UAAU,YAAY;AACxC,UAAO,QAAQ,OAAO,MAAM,YAAY,KAAK,CAAC;IAC9C,CACc;GAChB;CAEF,MAAM,MAAM,SAAS,OAAO;AAE5B,QAAO,IAAI,MAAM,KAAK,EACpB,IAAI,QAAQ,MAAM,UAAU;AAC1B,MAAI,SAAS,OACX,SACE,aACA,eAEA,MACG,QAAQ,KAAK,CACb,WAAW,KAAK,WAAW,CAC3B,KAAK,YAAqB;AAEjC,SAAO,QAAQ,IAAI,QAAQ,MAAM,SAAS;IAE7C,CAAC;;;;AC7EJ,SAAgB,WACd,MAC+B;CAC/B,MAAM,QAAQ,UAAU;AACxB,SAAQ,GAAG,SAA2B,MAAM,IAAI,MAAM,GAAG,KAAK;;;;ACKhE,SAAgB,QACd,MACyB;CACzB,MAAM,QAAQ,aAAa,KAAK;CAChC,MAAM,SAAS,WAAW,KAAK;AAG/B,QAAO,OAAO,OAFA,CAAC,OAAO,OAAO,EAED,EAC1B,KACE,aACA,YACA;AACA,SAAO,QAAQ,QAAQ,MAAM,CAAC,WACtB,CAAC,OAAO,OAAO,EACrB,WACD,CAAC,KAAK,YAAqB;IAE/B,CAAC;;;;AC1BJ,SAAgB,eAAkB,UAA8D;CAC9F,MAAM,QAAQ,UAAU;AACxB,cAAyB,SAAS,MAAM,WAAW,CAAC"}
|