@noy-db/in-svelte 0.1.0-pre.3

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 vLannaAi
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,33 @@
1
+ # @noy-db/in-svelte
2
+
3
+ [![npm](https://img.shields.io/npm/v/%40noy-db/in-svelte.svg)](https://www.npmjs.com/package/@noy-db/in-svelte)
4
+
5
+ > Svelte stores for noy-db
6
+
7
+ Part of [**`@noy-db/hub`**](https://www.npmjs.com/package/@noy-db/hub) — the zero-knowledge, offline-first, encrypted document store.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ pnpm add @noy-db/hub @noy-db/in-svelte
13
+ ```
14
+
15
+ ## What it is
16
+
17
+ Svelte stores for noy-db — collectionStore / queryStore / syncStore backed by noy-db change events. Works with Svelte 4 store contract and Svelte 5 runes (via $store subscription interop).
18
+
19
+ ## Status
20
+
21
+ **Pre-release** (`0.1.0-pre.1`). API may change before `1.0`.
22
+
23
+ ## Documentation
24
+
25
+ See the [main repository](https://github.com/vLannaAi/noy-db#readme) for setup, examples, and the full subsystem catalog.
26
+
27
+ - Source — [`packages/in-svelte`](https://github.com/vLannaAi/noy-db/tree/main/packages/in-svelte)
28
+ - Issues — [github.com/vLannaAi/noy-db/issues](https://github.com/vLannaAi/noy-db/issues)
29
+ - Spec — [`SPEC.md`](https://github.com/vLannaAi/noy-db/blob/main/SPEC.md)
30
+
31
+ ## License
32
+
33
+ [MIT](./LICENSE) © vLannaAi
package/dist/index.cjs ADDED
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ collectionStore: () => collectionStore,
24
+ queryStore: () => queryStore,
25
+ syncStore: () => syncStore
26
+ });
27
+ module.exports = __toCommonJS(index_exports);
28
+ function writable(initial) {
29
+ let value = initial;
30
+ const subscribers = /* @__PURE__ */ new Set();
31
+ return {
32
+ set(next) {
33
+ value = next;
34
+ for (const s of subscribers) s(next);
35
+ },
36
+ subscribe(run) {
37
+ subscribers.add(run);
38
+ run(value);
39
+ return () => {
40
+ subscribers.delete(run);
41
+ };
42
+ }
43
+ };
44
+ }
45
+ function collectionStore(vault, collectionName) {
46
+ const inner = writable({ records: [], loading: true, error: null });
47
+ const coll = vault.collection(collectionName);
48
+ async function refresh() {
49
+ try {
50
+ const records = await coll.list();
51
+ inner.set({ records, loading: false, error: null });
52
+ } catch (err) {
53
+ inner.set({ records: [], loading: false, error: err });
54
+ }
55
+ }
56
+ void refresh();
57
+ const unsubscribe = coll.subscribe(() => {
58
+ void refresh();
59
+ });
60
+ return {
61
+ subscribe: (run) => inner.subscribe(run),
62
+ refresh,
63
+ stop: unsubscribe
64
+ };
65
+ }
66
+ function queryStore(vault, collectionName, builder) {
67
+ const inner = writable({ data: null, loading: true, error: null });
68
+ const coll = vault.collection(collectionName);
69
+ async function refresh() {
70
+ try {
71
+ const result = await Promise.resolve(builder(coll.query()));
72
+ inner.set({ data: result, loading: false, error: null });
73
+ } catch (err) {
74
+ inner.set({ data: null, loading: false, error: err });
75
+ }
76
+ }
77
+ void refresh();
78
+ const unsubscribe = coll.subscribe(() => {
79
+ void refresh();
80
+ });
81
+ return {
82
+ subscribe: (run) => inner.subscribe(run),
83
+ refresh,
84
+ stop: unsubscribe
85
+ };
86
+ }
87
+ function syncStore(db) {
88
+ const inner = writable({ lastEvent: null, error: null });
89
+ const handler = (event) => {
90
+ inner.set({ lastEvent: event, error: null });
91
+ };
92
+ db.on("change", handler);
93
+ return {
94
+ subscribe: (run) => inner.subscribe(run),
95
+ stop: () => db.off("change", handler)
96
+ };
97
+ }
98
+ // Annotate the CommonJS export names for ESM import in node:
99
+ 0 && (module.exports = {
100
+ collectionStore,
101
+ queryStore,
102
+ syncStore
103
+ });
104
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/in-svelte** — Svelte stores for noy-db.\n *\n * Three store factories that conform to Svelte's standard store\n * contract (`subscribe(fn) → unsubscribe`). Works with:\n *\n * - Svelte 4 components via the `$store` auto-subscription syntax\n * - Svelte 5 runes via `$state(await fromStore(s).current)` or a\n * small `fromStore()` shim\n * - Any framework that consumes the Svelte store contract\n * (e.g. `@gradio/client`, non-Svelte integrations)\n *\n * ## Factories\n *\n * - {@link collectionStore} — reactive list of records.\n * - {@link queryStore} — reactive result of a query builder.\n * - {@link syncStore} — reactive Noydb-level change feed.\n *\n * ## Zero dependencies\n *\n * Svelte's store contract is ~10 lines of TypeScript. We re-implement\n * it inline rather than take a `svelte` peer-dep — the contract is\n * stable across Svelte 4/5 and the extra dependency is unnecessary\n * for store-only consumers (e.g. SvelteKit load functions).\n *\n * @packageDocumentation\n */\n\nimport type { Noydb, Vault, ChangeEvent, Query } from '@noy-db/hub'\n\n// ─── Svelte store contract (re-implemented, no svelte peer dep) ─────────\n\ntype Subscriber<T> = (value: T) => void\ntype Unsubscriber = () => void\n\nexport interface Readable<T> {\n subscribe(run: Subscriber<T>): Unsubscriber\n}\n\nfunction writable<T>(initial: T): {\n set(value: T): void\n subscribe(run: Subscriber<T>): Unsubscriber\n} {\n let value = initial\n const subscribers = new Set<Subscriber<T>>()\n return {\n set(next) {\n value = next\n for (const s of subscribers) s(next)\n },\n subscribe(run) {\n subscribers.add(run)\n run(value)\n return () => { subscribers.delete(run) }\n },\n }\n}\n\n// ─── collectionStore ───────────────────────────────────────────────────\n\nexport interface CollectionStoreState<T> {\n readonly records: readonly T[]\n readonly loading: boolean\n readonly error: Error | null\n}\n\n/**\n * Reactive collection records. Re-emits on every change event from\n * the collection (put / delete / sync).\n *\n * ```svelte\n * <script>\n * import { collectionStore } from '@noy-db/in-svelte'\n * const invoices = collectionStore(vault, 'invoices')\n * </script>\n *\n * {#each $invoices.records as invoice}\n * {invoice.id}\n * {/each}\n * ```\n */\nexport function collectionStore<T>(\n vault: Vault,\n collectionName: string,\n): Readable<CollectionStoreState<T>> & { refresh(): Promise<void>; stop(): void } {\n const inner = writable<CollectionStoreState<T>>({ records: [], loading: true, error: null })\n const coll = vault.collection<T>(collectionName)\n\n async function refresh(): Promise<void> {\n try {\n const records = await coll.list()\n inner.set({ records, loading: false, error: null })\n } catch (err) {\n inner.set({ records: [], loading: false, error: err as Error })\n }\n }\n\n void refresh()\n const unsubscribe = coll.subscribe(() => { void refresh() })\n\n return {\n subscribe: (run) => inner.subscribe(run),\n refresh,\n stop: unsubscribe,\n }\n}\n\n// ─── queryStore ────────────────────────────────────────────────────────\n\nexport interface QueryStoreState<R> {\n readonly data: R | null\n readonly loading: boolean\n readonly error: Error | null\n}\n\n/**\n * Reactive result of a query builder. The builder is re-run on every\n * change event — useful for aggregates, joins, and filtered lists.\n *\n * ```svelte\n * <script>\n * import { queryStore } from '@noy-db/in-svelte'\n * const paid = queryStore(vault, 'invoices', q => q.where('status', '==', 'paid').toArray())\n * </script>\n *\n * {#if $paid.loading} Loading… {:else} {$paid.data?.length} paid {/if}\n * ```\n */\nexport function queryStore<T, R>(\n vault: Vault,\n collectionName: string,\n builder: (q: Query<T>) => Promise<R> | R,\n): Readable<QueryStoreState<R>> & { refresh(): Promise<void>; stop(): void } {\n const inner = writable<QueryStoreState<R>>({ data: null, loading: true, error: null })\n const coll = vault.collection<T>(collectionName)\n\n async function refresh(): Promise<void> {\n try {\n const result = await Promise.resolve(builder(coll.query() as unknown as Query<T>))\n inner.set({ data: result, loading: false, error: null })\n } catch (err) {\n inner.set({ data: null, loading: false, error: err as Error })\n }\n }\n\n void refresh()\n const unsubscribe = coll.subscribe(() => { void refresh() })\n\n return {\n subscribe: (run) => inner.subscribe(run),\n refresh,\n stop: unsubscribe,\n }\n}\n\n// ─── syncStore ─────────────────────────────────────────────────────────\n\nexport interface SyncStoreState {\n readonly lastEvent: ChangeEvent | null\n readonly error: Error | null\n}\n\n/**\n * Reactive Noydb-level change feed. Useful for top-level status\n * indicators (last-update-time, \"unsynced changes\", offline banners).\n */\nexport function syncStore(db: Noydb): Readable<SyncStoreState> & { stop(): void } {\n const inner = writable<SyncStoreState>({ lastEvent: null, error: null })\n\n const handler = (event: ChangeEvent): void => {\n inner.set({ lastEvent: event, error: null })\n }\n db.on('change', handler)\n\n return {\n subscribe: (run) => inner.subscribe(run),\n stop: () => db.off('change', handler),\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAuCA,SAAS,SAAY,SAGnB;AACA,MAAI,QAAQ;AACZ,QAAM,cAAc,oBAAI,IAAmB;AAC3C,SAAO;AAAA,IACL,IAAI,MAAM;AACR,cAAQ;AACR,iBAAW,KAAK,YAAa,GAAE,IAAI;AAAA,IACrC;AAAA,IACA,UAAU,KAAK;AACb,kBAAY,IAAI,GAAG;AACnB,UAAI,KAAK;AACT,aAAO,MAAM;AAAE,oBAAY,OAAO,GAAG;AAAA,MAAE;AAAA,IACzC;AAAA,EACF;AACF;AAyBO,SAAS,gBACd,OACA,gBACgF;AAChF,QAAM,QAAQ,SAAkC,EAAE,SAAS,CAAC,GAAG,SAAS,MAAM,OAAO,KAAK,CAAC;AAC3F,QAAM,OAAO,MAAM,WAAc,cAAc;AAE/C,iBAAe,UAAyB;AACtC,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,KAAK;AAChC,YAAM,IAAI,EAAE,SAAS,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACpD,SAAS,KAAK;AACZ,YAAM,IAAI,EAAE,SAAS,CAAC,GAAG,SAAS,OAAO,OAAO,IAAa,CAAC;AAAA,IAChE;AAAA,EACF;AAEA,OAAK,QAAQ;AACb,QAAM,cAAc,KAAK,UAAU,MAAM;AAAE,SAAK,QAAQ;AAAA,EAAE,CAAC;AAE3D,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAuBO,SAAS,WACd,OACA,gBACA,SAC2E;AAC3E,QAAM,QAAQ,SAA6B,EAAE,MAAM,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AACrF,QAAM,OAAO,MAAM,WAAc,cAAc;AAE/C,iBAAe,UAAyB;AACtC,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,QAAQ,KAAK,MAAM,CAAwB,CAAC;AACjF,YAAM,IAAI,EAAE,MAAM,QAAQ,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACzD,SAAS,KAAK;AACZ,YAAM,IAAI,EAAE,MAAM,MAAM,SAAS,OAAO,OAAO,IAAa,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,OAAK,QAAQ;AACb,QAAM,cAAc,KAAK,UAAU,MAAM;AAAE,SAAK,QAAQ;AAAA,EAAE,CAAC;AAE3D,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAaO,SAAS,UAAU,IAAwD;AAChF,QAAM,QAAQ,SAAyB,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAEvE,QAAM,UAAU,CAAC,UAA6B;AAC5C,UAAM,IAAI,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,EAC7C;AACA,KAAG,GAAG,UAAU,OAAO;AAEvB,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC,MAAM,MAAM,GAAG,IAAI,UAAU,OAAO;AAAA,EACtC;AACF;","names":[]}
@@ -0,0 +1,94 @@
1
+ import { ChangeEvent, Vault, Query, Noydb } from '@noy-db/hub';
2
+
3
+ /**
4
+ * **@noy-db/in-svelte** — Svelte stores for noy-db.
5
+ *
6
+ * Three store factories that conform to Svelte's standard store
7
+ * contract (`subscribe(fn) → unsubscribe`). Works with:
8
+ *
9
+ * - Svelte 4 components via the `$store` auto-subscription syntax
10
+ * - Svelte 5 runes via `$state(await fromStore(s).current)` or a
11
+ * small `fromStore()` shim
12
+ * - Any framework that consumes the Svelte store contract
13
+ * (e.g. `@gradio/client`, non-Svelte integrations)
14
+ *
15
+ * ## Factories
16
+ *
17
+ * - {@link collectionStore} — reactive list of records.
18
+ * - {@link queryStore} — reactive result of a query builder.
19
+ * - {@link syncStore} — reactive Noydb-level change feed.
20
+ *
21
+ * ## Zero dependencies
22
+ *
23
+ * Svelte's store contract is ~10 lines of TypeScript. We re-implement
24
+ * it inline rather than take a `svelte` peer-dep — the contract is
25
+ * stable across Svelte 4/5 and the extra dependency is unnecessary
26
+ * for store-only consumers (e.g. SvelteKit load functions).
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+
31
+ type Subscriber<T> = (value: T) => void;
32
+ type Unsubscriber = () => void;
33
+ interface Readable<T> {
34
+ subscribe(run: Subscriber<T>): Unsubscriber;
35
+ }
36
+ interface CollectionStoreState<T> {
37
+ readonly records: readonly T[];
38
+ readonly loading: boolean;
39
+ readonly error: Error | null;
40
+ }
41
+ /**
42
+ * Reactive collection records. Re-emits on every change event from
43
+ * the collection (put / delete / sync).
44
+ *
45
+ * ```svelte
46
+ * <script>
47
+ * import { collectionStore } from '@noy-db/in-svelte'
48
+ * const invoices = collectionStore(vault, 'invoices')
49
+ * </script>
50
+ *
51
+ * {#each $invoices.records as invoice}
52
+ * {invoice.id}
53
+ * {/each}
54
+ * ```
55
+ */
56
+ declare function collectionStore<T>(vault: Vault, collectionName: string): Readable<CollectionStoreState<T>> & {
57
+ refresh(): Promise<void>;
58
+ stop(): void;
59
+ };
60
+ interface QueryStoreState<R> {
61
+ readonly data: R | null;
62
+ readonly loading: boolean;
63
+ readonly error: Error | null;
64
+ }
65
+ /**
66
+ * Reactive result of a query builder. The builder is re-run on every
67
+ * change event — useful for aggregates, joins, and filtered lists.
68
+ *
69
+ * ```svelte
70
+ * <script>
71
+ * import { queryStore } from '@noy-db/in-svelte'
72
+ * const paid = queryStore(vault, 'invoices', q => q.where('status', '==', 'paid').toArray())
73
+ * </script>
74
+ *
75
+ * {#if $paid.loading} Loading… {:else} {$paid.data?.length} paid {/if}
76
+ * ```
77
+ */
78
+ declare function queryStore<T, R>(vault: Vault, collectionName: string, builder: (q: Query<T>) => Promise<R> | R): Readable<QueryStoreState<R>> & {
79
+ refresh(): Promise<void>;
80
+ stop(): void;
81
+ };
82
+ interface SyncStoreState {
83
+ readonly lastEvent: ChangeEvent | null;
84
+ readonly error: Error | null;
85
+ }
86
+ /**
87
+ * Reactive Noydb-level change feed. Useful for top-level status
88
+ * indicators (last-update-time, "unsynced changes", offline banners).
89
+ */
90
+ declare function syncStore(db: Noydb): Readable<SyncStoreState> & {
91
+ stop(): void;
92
+ };
93
+
94
+ export { type CollectionStoreState, type QueryStoreState, type Readable, type SyncStoreState, collectionStore, queryStore, syncStore };
@@ -0,0 +1,94 @@
1
+ import { ChangeEvent, Vault, Query, Noydb } from '@noy-db/hub';
2
+
3
+ /**
4
+ * **@noy-db/in-svelte** — Svelte stores for noy-db.
5
+ *
6
+ * Three store factories that conform to Svelte's standard store
7
+ * contract (`subscribe(fn) → unsubscribe`). Works with:
8
+ *
9
+ * - Svelte 4 components via the `$store` auto-subscription syntax
10
+ * - Svelte 5 runes via `$state(await fromStore(s).current)` or a
11
+ * small `fromStore()` shim
12
+ * - Any framework that consumes the Svelte store contract
13
+ * (e.g. `@gradio/client`, non-Svelte integrations)
14
+ *
15
+ * ## Factories
16
+ *
17
+ * - {@link collectionStore} — reactive list of records.
18
+ * - {@link queryStore} — reactive result of a query builder.
19
+ * - {@link syncStore} — reactive Noydb-level change feed.
20
+ *
21
+ * ## Zero dependencies
22
+ *
23
+ * Svelte's store contract is ~10 lines of TypeScript. We re-implement
24
+ * it inline rather than take a `svelte` peer-dep — the contract is
25
+ * stable across Svelte 4/5 and the extra dependency is unnecessary
26
+ * for store-only consumers (e.g. SvelteKit load functions).
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+
31
+ type Subscriber<T> = (value: T) => void;
32
+ type Unsubscriber = () => void;
33
+ interface Readable<T> {
34
+ subscribe(run: Subscriber<T>): Unsubscriber;
35
+ }
36
+ interface CollectionStoreState<T> {
37
+ readonly records: readonly T[];
38
+ readonly loading: boolean;
39
+ readonly error: Error | null;
40
+ }
41
+ /**
42
+ * Reactive collection records. Re-emits on every change event from
43
+ * the collection (put / delete / sync).
44
+ *
45
+ * ```svelte
46
+ * <script>
47
+ * import { collectionStore } from '@noy-db/in-svelte'
48
+ * const invoices = collectionStore(vault, 'invoices')
49
+ * </script>
50
+ *
51
+ * {#each $invoices.records as invoice}
52
+ * {invoice.id}
53
+ * {/each}
54
+ * ```
55
+ */
56
+ declare function collectionStore<T>(vault: Vault, collectionName: string): Readable<CollectionStoreState<T>> & {
57
+ refresh(): Promise<void>;
58
+ stop(): void;
59
+ };
60
+ interface QueryStoreState<R> {
61
+ readonly data: R | null;
62
+ readonly loading: boolean;
63
+ readonly error: Error | null;
64
+ }
65
+ /**
66
+ * Reactive result of a query builder. The builder is re-run on every
67
+ * change event — useful for aggregates, joins, and filtered lists.
68
+ *
69
+ * ```svelte
70
+ * <script>
71
+ * import { queryStore } from '@noy-db/in-svelte'
72
+ * const paid = queryStore(vault, 'invoices', q => q.where('status', '==', 'paid').toArray())
73
+ * </script>
74
+ *
75
+ * {#if $paid.loading} Loading… {:else} {$paid.data?.length} paid {/if}
76
+ * ```
77
+ */
78
+ declare function queryStore<T, R>(vault: Vault, collectionName: string, builder: (q: Query<T>) => Promise<R> | R): Readable<QueryStoreState<R>> & {
79
+ refresh(): Promise<void>;
80
+ stop(): void;
81
+ };
82
+ interface SyncStoreState {
83
+ readonly lastEvent: ChangeEvent | null;
84
+ readonly error: Error | null;
85
+ }
86
+ /**
87
+ * Reactive Noydb-level change feed. Useful for top-level status
88
+ * indicators (last-update-time, "unsynced changes", offline banners).
89
+ */
90
+ declare function syncStore(db: Noydb): Readable<SyncStoreState> & {
91
+ stop(): void;
92
+ };
93
+
94
+ export { type CollectionStoreState, type QueryStoreState, type Readable, type SyncStoreState, collectionStore, queryStore, syncStore };
package/dist/index.js ADDED
@@ -0,0 +1,77 @@
1
+ // src/index.ts
2
+ function writable(initial) {
3
+ let value = initial;
4
+ const subscribers = /* @__PURE__ */ new Set();
5
+ return {
6
+ set(next) {
7
+ value = next;
8
+ for (const s of subscribers) s(next);
9
+ },
10
+ subscribe(run) {
11
+ subscribers.add(run);
12
+ run(value);
13
+ return () => {
14
+ subscribers.delete(run);
15
+ };
16
+ }
17
+ };
18
+ }
19
+ function collectionStore(vault, collectionName) {
20
+ const inner = writable({ records: [], loading: true, error: null });
21
+ const coll = vault.collection(collectionName);
22
+ async function refresh() {
23
+ try {
24
+ const records = await coll.list();
25
+ inner.set({ records, loading: false, error: null });
26
+ } catch (err) {
27
+ inner.set({ records: [], loading: false, error: err });
28
+ }
29
+ }
30
+ void refresh();
31
+ const unsubscribe = coll.subscribe(() => {
32
+ void refresh();
33
+ });
34
+ return {
35
+ subscribe: (run) => inner.subscribe(run),
36
+ refresh,
37
+ stop: unsubscribe
38
+ };
39
+ }
40
+ function queryStore(vault, collectionName, builder) {
41
+ const inner = writable({ data: null, loading: true, error: null });
42
+ const coll = vault.collection(collectionName);
43
+ async function refresh() {
44
+ try {
45
+ const result = await Promise.resolve(builder(coll.query()));
46
+ inner.set({ data: result, loading: false, error: null });
47
+ } catch (err) {
48
+ inner.set({ data: null, loading: false, error: err });
49
+ }
50
+ }
51
+ void refresh();
52
+ const unsubscribe = coll.subscribe(() => {
53
+ void refresh();
54
+ });
55
+ return {
56
+ subscribe: (run) => inner.subscribe(run),
57
+ refresh,
58
+ stop: unsubscribe
59
+ };
60
+ }
61
+ function syncStore(db) {
62
+ const inner = writable({ lastEvent: null, error: null });
63
+ const handler = (event) => {
64
+ inner.set({ lastEvent: event, error: null });
65
+ };
66
+ db.on("change", handler);
67
+ return {
68
+ subscribe: (run) => inner.subscribe(run),
69
+ stop: () => db.off("change", handler)
70
+ };
71
+ }
72
+ export {
73
+ collectionStore,
74
+ queryStore,
75
+ syncStore
76
+ };
77
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * **@noy-db/in-svelte** — Svelte stores for noy-db.\n *\n * Three store factories that conform to Svelte's standard store\n * contract (`subscribe(fn) → unsubscribe`). Works with:\n *\n * - Svelte 4 components via the `$store` auto-subscription syntax\n * - Svelte 5 runes via `$state(await fromStore(s).current)` or a\n * small `fromStore()` shim\n * - Any framework that consumes the Svelte store contract\n * (e.g. `@gradio/client`, non-Svelte integrations)\n *\n * ## Factories\n *\n * - {@link collectionStore} — reactive list of records.\n * - {@link queryStore} — reactive result of a query builder.\n * - {@link syncStore} — reactive Noydb-level change feed.\n *\n * ## Zero dependencies\n *\n * Svelte's store contract is ~10 lines of TypeScript. We re-implement\n * it inline rather than take a `svelte` peer-dep — the contract is\n * stable across Svelte 4/5 and the extra dependency is unnecessary\n * for store-only consumers (e.g. SvelteKit load functions).\n *\n * @packageDocumentation\n */\n\nimport type { Noydb, Vault, ChangeEvent, Query } from '@noy-db/hub'\n\n// ─── Svelte store contract (re-implemented, no svelte peer dep) ─────────\n\ntype Subscriber<T> = (value: T) => void\ntype Unsubscriber = () => void\n\nexport interface Readable<T> {\n subscribe(run: Subscriber<T>): Unsubscriber\n}\n\nfunction writable<T>(initial: T): {\n set(value: T): void\n subscribe(run: Subscriber<T>): Unsubscriber\n} {\n let value = initial\n const subscribers = new Set<Subscriber<T>>()\n return {\n set(next) {\n value = next\n for (const s of subscribers) s(next)\n },\n subscribe(run) {\n subscribers.add(run)\n run(value)\n return () => { subscribers.delete(run) }\n },\n }\n}\n\n// ─── collectionStore ───────────────────────────────────────────────────\n\nexport interface CollectionStoreState<T> {\n readonly records: readonly T[]\n readonly loading: boolean\n readonly error: Error | null\n}\n\n/**\n * Reactive collection records. Re-emits on every change event from\n * the collection (put / delete / sync).\n *\n * ```svelte\n * <script>\n * import { collectionStore } from '@noy-db/in-svelte'\n * const invoices = collectionStore(vault, 'invoices')\n * </script>\n *\n * {#each $invoices.records as invoice}\n * {invoice.id}\n * {/each}\n * ```\n */\nexport function collectionStore<T>(\n vault: Vault,\n collectionName: string,\n): Readable<CollectionStoreState<T>> & { refresh(): Promise<void>; stop(): void } {\n const inner = writable<CollectionStoreState<T>>({ records: [], loading: true, error: null })\n const coll = vault.collection<T>(collectionName)\n\n async function refresh(): Promise<void> {\n try {\n const records = await coll.list()\n inner.set({ records, loading: false, error: null })\n } catch (err) {\n inner.set({ records: [], loading: false, error: err as Error })\n }\n }\n\n void refresh()\n const unsubscribe = coll.subscribe(() => { void refresh() })\n\n return {\n subscribe: (run) => inner.subscribe(run),\n refresh,\n stop: unsubscribe,\n }\n}\n\n// ─── queryStore ────────────────────────────────────────────────────────\n\nexport interface QueryStoreState<R> {\n readonly data: R | null\n readonly loading: boolean\n readonly error: Error | null\n}\n\n/**\n * Reactive result of a query builder. The builder is re-run on every\n * change event — useful for aggregates, joins, and filtered lists.\n *\n * ```svelte\n * <script>\n * import { queryStore } from '@noy-db/in-svelte'\n * const paid = queryStore(vault, 'invoices', q => q.where('status', '==', 'paid').toArray())\n * </script>\n *\n * {#if $paid.loading} Loading… {:else} {$paid.data?.length} paid {/if}\n * ```\n */\nexport function queryStore<T, R>(\n vault: Vault,\n collectionName: string,\n builder: (q: Query<T>) => Promise<R> | R,\n): Readable<QueryStoreState<R>> & { refresh(): Promise<void>; stop(): void } {\n const inner = writable<QueryStoreState<R>>({ data: null, loading: true, error: null })\n const coll = vault.collection<T>(collectionName)\n\n async function refresh(): Promise<void> {\n try {\n const result = await Promise.resolve(builder(coll.query() as unknown as Query<T>))\n inner.set({ data: result, loading: false, error: null })\n } catch (err) {\n inner.set({ data: null, loading: false, error: err as Error })\n }\n }\n\n void refresh()\n const unsubscribe = coll.subscribe(() => { void refresh() })\n\n return {\n subscribe: (run) => inner.subscribe(run),\n refresh,\n stop: unsubscribe,\n }\n}\n\n// ─── syncStore ─────────────────────────────────────────────────────────\n\nexport interface SyncStoreState {\n readonly lastEvent: ChangeEvent | null\n readonly error: Error | null\n}\n\n/**\n * Reactive Noydb-level change feed. Useful for top-level status\n * indicators (last-update-time, \"unsynced changes\", offline banners).\n */\nexport function syncStore(db: Noydb): Readable<SyncStoreState> & { stop(): void } {\n const inner = writable<SyncStoreState>({ lastEvent: null, error: null })\n\n const handler = (event: ChangeEvent): void => {\n inner.set({ lastEvent: event, error: null })\n }\n db.on('change', handler)\n\n return {\n subscribe: (run) => inner.subscribe(run),\n stop: () => db.off('change', handler),\n }\n}\n"],"mappings":";AAuCA,SAAS,SAAY,SAGnB;AACA,MAAI,QAAQ;AACZ,QAAM,cAAc,oBAAI,IAAmB;AAC3C,SAAO;AAAA,IACL,IAAI,MAAM;AACR,cAAQ;AACR,iBAAW,KAAK,YAAa,GAAE,IAAI;AAAA,IACrC;AAAA,IACA,UAAU,KAAK;AACb,kBAAY,IAAI,GAAG;AACnB,UAAI,KAAK;AACT,aAAO,MAAM;AAAE,oBAAY,OAAO,GAAG;AAAA,MAAE;AAAA,IACzC;AAAA,EACF;AACF;AAyBO,SAAS,gBACd,OACA,gBACgF;AAChF,QAAM,QAAQ,SAAkC,EAAE,SAAS,CAAC,GAAG,SAAS,MAAM,OAAO,KAAK,CAAC;AAC3F,QAAM,OAAO,MAAM,WAAc,cAAc;AAE/C,iBAAe,UAAyB;AACtC,QAAI;AACF,YAAM,UAAU,MAAM,KAAK,KAAK;AAChC,YAAM,IAAI,EAAE,SAAS,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACpD,SAAS,KAAK;AACZ,YAAM,IAAI,EAAE,SAAS,CAAC,GAAG,SAAS,OAAO,OAAO,IAAa,CAAC;AAAA,IAChE;AAAA,EACF;AAEA,OAAK,QAAQ;AACb,QAAM,cAAc,KAAK,UAAU,MAAM;AAAE,SAAK,QAAQ;AAAA,EAAE,CAAC;AAE3D,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAuBO,SAAS,WACd,OACA,gBACA,SAC2E;AAC3E,QAAM,QAAQ,SAA6B,EAAE,MAAM,MAAM,SAAS,MAAM,OAAO,KAAK,CAAC;AACrF,QAAM,OAAO,MAAM,WAAc,cAAc;AAE/C,iBAAe,UAAyB;AACtC,QAAI;AACF,YAAM,SAAS,MAAM,QAAQ,QAAQ,QAAQ,KAAK,MAAM,CAAwB,CAAC;AACjF,YAAM,IAAI,EAAE,MAAM,QAAQ,SAAS,OAAO,OAAO,KAAK,CAAC;AAAA,IACzD,SAAS,KAAK;AACZ,YAAM,IAAI,EAAE,MAAM,MAAM,SAAS,OAAO,OAAO,IAAa,CAAC;AAAA,IAC/D;AAAA,EACF;AAEA,OAAK,QAAQ;AACb,QAAM,cAAc,KAAK,UAAU,MAAM;AAAE,SAAK,QAAQ;AAAA,EAAE,CAAC;AAE3D,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC;AAAA,IACA,MAAM;AAAA,EACR;AACF;AAaO,SAAS,UAAU,IAAwD;AAChF,QAAM,QAAQ,SAAyB,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAEvE,QAAM,UAAU,CAAC,UAA6B;AAC5C,UAAM,IAAI,EAAE,WAAW,OAAO,OAAO,KAAK,CAAC;AAAA,EAC7C;AACA,KAAG,GAAG,UAAU,OAAO;AAEvB,SAAO;AAAA,IACL,WAAW,CAAC,QAAQ,MAAM,UAAU,GAAG;AAAA,IACvC,MAAM,MAAM,GAAG,IAAI,UAAU,OAAO;AAAA,EACtC;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@noy-db/in-svelte",
3
+ "version": "0.1.0-pre.3",
4
+ "description": "Svelte stores for noy-db — collectionStore / queryStore / syncStore backed by noy-db change events. Works with Svelte 4 store contract and Svelte 5 runes (via $store subscription interop).",
5
+ "license": "MIT",
6
+ "author": "vLannaAi <vicio@lanna.ai>",
7
+ "homepage": "https://github.com/vLannaAi/noy-db/tree/main/packages/in-svelte#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/vLannaAi/noy-db.git",
11
+ "directory": "packages/in-svelte"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/vLannaAi/noy-db/issues"
15
+ },
16
+ "type": "module",
17
+ "sideEffects": false,
18
+ "exports": {
19
+ ".": {
20
+ "import": {
21
+ "types": "./dist/index.d.ts",
22
+ "default": "./dist/index.js"
23
+ },
24
+ "require": {
25
+ "types": "./dist/index.d.cts",
26
+ "default": "./dist/index.cjs"
27
+ }
28
+ }
29
+ },
30
+ "main": "./dist/index.cjs",
31
+ "module": "./dist/index.js",
32
+ "types": "./dist/index.d.ts",
33
+ "files": [
34
+ "dist",
35
+ "README.md",
36
+ "LICENSE"
37
+ ],
38
+ "engines": {
39
+ "node": ">=18.0.0"
40
+ },
41
+ "peerDependencies": {
42
+ "@noy-db/hub": "0.1.0-pre.3"
43
+ },
44
+ "devDependencies": {
45
+ "@noy-db/hub": "0.1.0-pre.3"
46
+ },
47
+ "keywords": [
48
+ "noy-db",
49
+ "in-svelte",
50
+ "svelte",
51
+ "stores",
52
+ "sveltekit"
53
+ ],
54
+ "publishConfig": {
55
+ "access": "public",
56
+ "tag": "latest"
57
+ },
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "test": "vitest run",
61
+ "lint": "eslint src/",
62
+ "typecheck": "tsc --noEmit"
63
+ }
64
+ }