anchordb-react 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AnchorDB contributors
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,51 @@
1
+ # anchordb-react
2
+
3
+ React and React Native hooks for [AnchorDB](https://www.npmjs.com/package/anchordb).
4
+
5
+ ```bash
6
+ npm install anchordb-react
7
+ ```
8
+
9
+ ```tsx
10
+ import { useFind, useSyncStatus } from "anchordb-react";
11
+
12
+ function Users({ db, User }) {
13
+ const { data, loading, refresh } = useFind(db, User, { age: { $gte: 18 } }, { sort: "-createdAt" });
14
+ const { status, pending, sync } = useSyncStatus(db);
15
+
16
+ if (loading) return <Spinner />;
17
+ return (
18
+ <>
19
+ <StatusBar status={status} pending={pending} onSync={sync} />
20
+ <FlatList data={data} onRefresh={refresh} />
21
+ </>
22
+ );
23
+ }
24
+ ```
25
+
26
+ ## Hooks
27
+
28
+ | Hook | Purpose |
29
+ | --- | --- |
30
+ | `useFind` | live list query; re-runs on every matching change |
31
+ | `useFindOne` | live single document |
32
+ | `useCount` | live count, without fetching documents |
33
+ | `useAggregate` | live aggregation pipeline |
34
+ | `useSyncStatus` | status, pending count and a `sync()` trigger |
35
+ | `useAnchorEvent` | subscribe to any AnchorDB event |
36
+
37
+ All are built on the same `observeQuery` primitive as the Angular bindings and the inspector's push
38
+ events, so a change visible to one is visible to all three.
39
+
40
+ ## Two things worth knowing
41
+
42
+ **Filters are keyed on serialised content, not object identity.** Writing
43
+ `useFind(db, User, { age: { $gte: 18 } })` creates a fresh object every render; depending on
44
+ identity would resubscribe on each render and loop forever.
45
+
46
+ **`useSyncStatus` works in local-only mode**, reporting `"disabled"` with zero pending — so a status
47
+ indicator can be rendered unconditionally instead of every screen branching on whether sync exists.
48
+
49
+ ## Status
50
+
51
+ **0.1.0.** React is a peer dependency (>=18). MIT.
@@ -0,0 +1,61 @@
1
+ import { type AnchorDB, type Filter, type ModelConstructor, type SyncStatus } from "anchordb";
2
+ /**
3
+ * React and React Native bindings.
4
+ *
5
+ * Built on the same `observeQuery` primitive as the Angular bindings and the inspector's push
6
+ * events, so all three see exactly the same changes. A hook that could miss an update the
7
+ * equivalent observable caught would be a source of bugs nobody could reproduce.
8
+ */
9
+ export interface QueryState<T> {
10
+ data: T | undefined;
11
+ loading: boolean;
12
+ error: unknown;
13
+ /** Re-run immediately, e.g. from a pull-to-refresh. */
14
+ refresh: () => void;
15
+ }
16
+ export interface UseFindOptions {
17
+ sort?: unknown;
18
+ skip?: number;
19
+ limit?: number;
20
+ select?: string | Record<string, 0 | 1>;
21
+ populate?: string | string[];
22
+ lean?: boolean;
23
+ debounceMs?: number;
24
+ /** Skip the query entirely — for dependent queries that are not ready yet. */
25
+ enabled?: boolean;
26
+ }
27
+ /** Live list query. Re-runs whenever the collection changes. */
28
+ export declare function useFind<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: UseFindOptions): QueryState<T[]>;
29
+ /** Live single-document query. */
30
+ export declare function useFindOne<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: UseFindOptions): QueryState<T | null>;
31
+ /** Live document count — cheaper than fetching documents just to count them. */
32
+ export declare function useCount<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: {
33
+ debounceMs?: number;
34
+ }): QueryState<number>;
35
+ /** Live aggregation. */
36
+ export declare function useAggregate<R = Record<string, unknown>>(db: AnchorDB, model: ModelConstructor, pipeline: unknown[], options?: {
37
+ debounceMs?: number;
38
+ enabled?: boolean;
39
+ }): QueryState<R[]>;
40
+ export interface SyncState {
41
+ status: SyncStatus;
42
+ pending: number;
43
+ enabled: boolean;
44
+ sync: () => Promise<void>;
45
+ }
46
+ /**
47
+ * Live sync status.
48
+ *
49
+ * Works in local-only mode too, reporting `"disabled"` with zero pending — so a status indicator
50
+ * can be rendered unconditionally instead of every screen branching on whether sync exists.
51
+ */
52
+ export declare function useSyncStatus(db: AnchorDB): SyncState;
53
+ /** Subscribe to any AnchorDB event from a component. */
54
+ export declare function useAnchorEvent<K extends Parameters<AnchorDB["on"]>[0]>(db: AnchorDB, event: K, handler: (payload: unknown) => void): void;
55
+ /**
56
+ * Snapshot of connection state, via `useSyncExternalStore` so it is concurrent-safe and never
57
+ * tears between renders.
58
+ */
59
+ export declare function useAnchorReady(db: AnchorDB): boolean;
60
+ export type { AnchorDB, ModelConstructor };
61
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useFind = useFind;
4
+ exports.useFindOne = useFindOne;
5
+ exports.useCount = useCount;
6
+ exports.useAggregate = useAggregate;
7
+ exports.useSyncStatus = useSyncStatus;
8
+ exports.useAnchorEvent = useAnchorEvent;
9
+ exports.useAnchorReady = useAnchorReady;
10
+ const react_1 = require("react");
11
+ const anchordb_1 = require("anchordb");
12
+ /**
13
+ * Stable key for a filter/options object.
14
+ *
15
+ * Callers naturally write `useFind(User, { age: { $gte: 18 } })`, creating a fresh object on every
16
+ * render. Depending on the object identity would resubscribe on each render and loop forever, so
17
+ * dependencies are keyed on serialised content instead.
18
+ */
19
+ function useStableKey(value) {
20
+ return (0, react_1.useMemo)(() => JSON.stringify(value ?? null), [value]);
21
+ }
22
+ /** Live list query. Re-runs whenever the collection changes. */
23
+ function useFind(db, model, filter = {}, options = {}) {
24
+ const filterKey = useStableKey(filter);
25
+ const optionsKey = useStableKey({ ...options, enabled: undefined, debounceMs: undefined });
26
+ const [state, setState] = (0, react_1.useState)({
27
+ data: undefined,
28
+ loading: options.enabled !== false,
29
+ error: undefined,
30
+ });
31
+ const [nonce, setNonce] = (0, react_1.useState)(0);
32
+ (0, react_1.useEffect)(() => {
33
+ if (options.enabled === false) {
34
+ setState({ data: undefined, loading: false, error: undefined });
35
+ return;
36
+ }
37
+ setState((s) => ({ ...s, loading: true }));
38
+ const subscription = (0, anchordb_1.observeQuery)(db, model.collectionName, async () => {
39
+ const query = model.find(JSON.parse(filterKey));
40
+ if (options.sort)
41
+ query.sort(options.sort);
42
+ if (options.skip !== undefined)
43
+ query.skip(options.skip);
44
+ if (options.limit !== undefined)
45
+ query.limit(options.limit);
46
+ if (options.select)
47
+ query.select(options.select);
48
+ if (options.populate)
49
+ query.populate(options.populate);
50
+ if (options.lean)
51
+ query.lean();
52
+ return (await query.exec());
53
+ }, {
54
+ next: (data) => setState({ data, loading: false, error: undefined }),
55
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
56
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
57
+ return () => subscription.unsubscribe();
58
+ // eslint-disable-next-line react-hooks/exhaustive-deps
59
+ }, [db, model, filterKey, optionsKey, options.enabled, options.debounceMs, nonce]);
60
+ const refresh = (0, react_1.useCallback)(() => setNonce((n) => n + 1), []);
61
+ return { ...state, refresh };
62
+ }
63
+ /** Live single-document query. */
64
+ function useFindOne(db, model, filter = {}, options = {}) {
65
+ const result = useFind(db, model, filter, { ...options, limit: 1 });
66
+ return (0, react_1.useMemo)(() => ({
67
+ data: result.data === undefined ? undefined : (result.data[0] ?? null),
68
+ loading: result.loading,
69
+ error: result.error,
70
+ refresh: result.refresh,
71
+ }), [result.data, result.loading, result.error, result.refresh]);
72
+ }
73
+ /** Live document count — cheaper than fetching documents just to count them. */
74
+ function useCount(db, model, filter = {}, options = {}) {
75
+ const filterKey = useStableKey(filter);
76
+ const [state, setState] = (0, react_1.useState)({
77
+ data: undefined,
78
+ loading: true,
79
+ error: undefined,
80
+ });
81
+ const [nonce, setNonce] = (0, react_1.useState)(0);
82
+ (0, react_1.useEffect)(() => {
83
+ const subscription = (0, anchordb_1.observeQuery)(db, model.collectionName, () => model.countDocuments(JSON.parse(filterKey)), {
84
+ next: (data) => setState({ data, loading: false, error: undefined }),
85
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
86
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
87
+ return () => subscription.unsubscribe();
88
+ // eslint-disable-next-line react-hooks/exhaustive-deps
89
+ }, [db, model, filterKey, options.debounceMs, nonce]);
90
+ return { ...state, refresh: (0, react_1.useCallback)(() => setNonce((n) => n + 1), []) };
91
+ }
92
+ /** Live aggregation. */
93
+ function useAggregate(db, model, pipeline, options = {}) {
94
+ const pipelineKey = useStableKey(pipeline);
95
+ const [state, setState] = (0, react_1.useState)({
96
+ data: undefined,
97
+ loading: options.enabled !== false,
98
+ error: undefined,
99
+ });
100
+ const [nonce, setNonce] = (0, react_1.useState)(0);
101
+ (0, react_1.useEffect)(() => {
102
+ if (options.enabled === false)
103
+ return;
104
+ const subscription = (0, anchordb_1.observeQuery)(db, model.collectionName, () => model.aggregate(JSON.parse(pipelineKey)), {
105
+ next: (data) => setState({ data, loading: false, error: undefined }),
106
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
107
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
108
+ return () => subscription.unsubscribe();
109
+ // eslint-disable-next-line react-hooks/exhaustive-deps
110
+ }, [db, model, pipelineKey, options.enabled, options.debounceMs, nonce]);
111
+ return { ...state, refresh: (0, react_1.useCallback)(() => setNonce((n) => n + 1), []) };
112
+ }
113
+ /**
114
+ * Live sync status.
115
+ *
116
+ * Works in local-only mode too, reporting `"disabled"` with zero pending — so a status indicator
117
+ * can be rendered unconditionally instead of every screen branching on whether sync exists.
118
+ */
119
+ function useSyncStatus(db) {
120
+ const [state, setState] = (0, react_1.useState)({
121
+ status: db.sync.status(),
122
+ pending: 0,
123
+ });
124
+ (0, react_1.useEffect)(() => {
125
+ const subscription = (0, anchordb_1.observeSyncStatus)(db, {
126
+ next: (value) => setState({ status: value.status, pending: value.pending }),
127
+ });
128
+ return () => subscription.unsubscribe();
129
+ }, [db]);
130
+ const sync = (0, react_1.useCallback)(async () => {
131
+ await db.sync.now();
132
+ }, [db]);
133
+ return { ...state, enabled: db.syncEnabled, sync };
134
+ }
135
+ /** Subscribe to any AnchorDB event from a component. */
136
+ function useAnchorEvent(db, event, handler) {
137
+ const ref = (0, react_1.useRef)(handler);
138
+ ref.current = handler;
139
+ (0, react_1.useEffect)(() => db.on(event, (payload) => ref.current(payload)), [db, event]);
140
+ }
141
+ /**
142
+ * Snapshot of connection state, via `useSyncExternalStore` so it is concurrent-safe and never
143
+ * tears between renders.
144
+ */
145
+ function useAnchorReady(db) {
146
+ const subscribe = (0, react_1.useCallback)((onChange) => {
147
+ let cancelled = false;
148
+ void db.open().then(() => {
149
+ if (!cancelled)
150
+ onChange();
151
+ });
152
+ return () => {
153
+ cancelled = true;
154
+ };
155
+ }, [db]);
156
+ const ready = (0, react_1.useRef)(false);
157
+ return (0, react_1.useSyncExternalStore)(subscribe, () => {
158
+ if (!ready.current) {
159
+ void db.open().then(() => {
160
+ ready.current = true;
161
+ });
162
+ }
163
+ return ready.current;
164
+ }, () => false);
165
+ }
166
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "commonjs"
3
+ }
@@ -0,0 +1,61 @@
1
+ import { type AnchorDB, type Filter, type ModelConstructor, type SyncStatus } from "anchordb";
2
+ /**
3
+ * React and React Native bindings.
4
+ *
5
+ * Built on the same `observeQuery` primitive as the Angular bindings and the inspector's push
6
+ * events, so all three see exactly the same changes. A hook that could miss an update the
7
+ * equivalent observable caught would be a source of bugs nobody could reproduce.
8
+ */
9
+ export interface QueryState<T> {
10
+ data: T | undefined;
11
+ loading: boolean;
12
+ error: unknown;
13
+ /** Re-run immediately, e.g. from a pull-to-refresh. */
14
+ refresh: () => void;
15
+ }
16
+ export interface UseFindOptions {
17
+ sort?: unknown;
18
+ skip?: number;
19
+ limit?: number;
20
+ select?: string | Record<string, 0 | 1>;
21
+ populate?: string | string[];
22
+ lean?: boolean;
23
+ debounceMs?: number;
24
+ /** Skip the query entirely — for dependent queries that are not ready yet. */
25
+ enabled?: boolean;
26
+ }
27
+ /** Live list query. Re-runs whenever the collection changes. */
28
+ export declare function useFind<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: UseFindOptions): QueryState<T[]>;
29
+ /** Live single-document query. */
30
+ export declare function useFindOne<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: UseFindOptions): QueryState<T | null>;
31
+ /** Live document count — cheaper than fetching documents just to count them. */
32
+ export declare function useCount<T extends Record<string, unknown>>(db: AnchorDB, model: ModelConstructor<T>, filter?: Filter, options?: {
33
+ debounceMs?: number;
34
+ }): QueryState<number>;
35
+ /** Live aggregation. */
36
+ export declare function useAggregate<R = Record<string, unknown>>(db: AnchorDB, model: ModelConstructor, pipeline: unknown[], options?: {
37
+ debounceMs?: number;
38
+ enabled?: boolean;
39
+ }): QueryState<R[]>;
40
+ export interface SyncState {
41
+ status: SyncStatus;
42
+ pending: number;
43
+ enabled: boolean;
44
+ sync: () => Promise<void>;
45
+ }
46
+ /**
47
+ * Live sync status.
48
+ *
49
+ * Works in local-only mode too, reporting `"disabled"` with zero pending — so a status indicator
50
+ * can be rendered unconditionally instead of every screen branching on whether sync exists.
51
+ */
52
+ export declare function useSyncStatus(db: AnchorDB): SyncState;
53
+ /** Subscribe to any AnchorDB event from a component. */
54
+ export declare function useAnchorEvent<K extends Parameters<AnchorDB["on"]>[0]>(db: AnchorDB, event: K, handler: (payload: unknown) => void): void;
55
+ /**
56
+ * Snapshot of connection state, via `useSyncExternalStore` so it is concurrent-safe and never
57
+ * tears between renders.
58
+ */
59
+ export declare function useAnchorReady(db: AnchorDB): boolean;
60
+ export type { AnchorDB, ModelConstructor };
61
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import { observeQuery, observeSyncStatus, } from "anchordb";
3
+ /**
4
+ * Stable key for a filter/options object.
5
+ *
6
+ * Callers naturally write `useFind(User, { age: { $gte: 18 } })`, creating a fresh object on every
7
+ * render. Depending on the object identity would resubscribe on each render and loop forever, so
8
+ * dependencies are keyed on serialised content instead.
9
+ */
10
+ function useStableKey(value) {
11
+ return useMemo(() => JSON.stringify(value ?? null), [value]);
12
+ }
13
+ /** Live list query. Re-runs whenever the collection changes. */
14
+ export function useFind(db, model, filter = {}, options = {}) {
15
+ const filterKey = useStableKey(filter);
16
+ const optionsKey = useStableKey({ ...options, enabled: undefined, debounceMs: undefined });
17
+ const [state, setState] = useState({
18
+ data: undefined,
19
+ loading: options.enabled !== false,
20
+ error: undefined,
21
+ });
22
+ const [nonce, setNonce] = useState(0);
23
+ useEffect(() => {
24
+ if (options.enabled === false) {
25
+ setState({ data: undefined, loading: false, error: undefined });
26
+ return;
27
+ }
28
+ setState((s) => ({ ...s, loading: true }));
29
+ const subscription = observeQuery(db, model.collectionName, async () => {
30
+ const query = model.find(JSON.parse(filterKey));
31
+ if (options.sort)
32
+ query.sort(options.sort);
33
+ if (options.skip !== undefined)
34
+ query.skip(options.skip);
35
+ if (options.limit !== undefined)
36
+ query.limit(options.limit);
37
+ if (options.select)
38
+ query.select(options.select);
39
+ if (options.populate)
40
+ query.populate(options.populate);
41
+ if (options.lean)
42
+ query.lean();
43
+ return (await query.exec());
44
+ }, {
45
+ next: (data) => setState({ data, loading: false, error: undefined }),
46
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
47
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
48
+ return () => subscription.unsubscribe();
49
+ // eslint-disable-next-line react-hooks/exhaustive-deps
50
+ }, [db, model, filterKey, optionsKey, options.enabled, options.debounceMs, nonce]);
51
+ const refresh = useCallback(() => setNonce((n) => n + 1), []);
52
+ return { ...state, refresh };
53
+ }
54
+ /** Live single-document query. */
55
+ export function useFindOne(db, model, filter = {}, options = {}) {
56
+ const result = useFind(db, model, filter, { ...options, limit: 1 });
57
+ return useMemo(() => ({
58
+ data: result.data === undefined ? undefined : (result.data[0] ?? null),
59
+ loading: result.loading,
60
+ error: result.error,
61
+ refresh: result.refresh,
62
+ }), [result.data, result.loading, result.error, result.refresh]);
63
+ }
64
+ /** Live document count — cheaper than fetching documents just to count them. */
65
+ export function useCount(db, model, filter = {}, options = {}) {
66
+ const filterKey = useStableKey(filter);
67
+ const [state, setState] = useState({
68
+ data: undefined,
69
+ loading: true,
70
+ error: undefined,
71
+ });
72
+ const [nonce, setNonce] = useState(0);
73
+ useEffect(() => {
74
+ const subscription = observeQuery(db, model.collectionName, () => model.countDocuments(JSON.parse(filterKey)), {
75
+ next: (data) => setState({ data, loading: false, error: undefined }),
76
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
77
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
78
+ return () => subscription.unsubscribe();
79
+ // eslint-disable-next-line react-hooks/exhaustive-deps
80
+ }, [db, model, filterKey, options.debounceMs, nonce]);
81
+ return { ...state, refresh: useCallback(() => setNonce((n) => n + 1), []) };
82
+ }
83
+ /** Live aggregation. */
84
+ export function useAggregate(db, model, pipeline, options = {}) {
85
+ const pipelineKey = useStableKey(pipeline);
86
+ const [state, setState] = useState({
87
+ data: undefined,
88
+ loading: options.enabled !== false,
89
+ error: undefined,
90
+ });
91
+ const [nonce, setNonce] = useState(0);
92
+ useEffect(() => {
93
+ if (options.enabled === false)
94
+ return;
95
+ const subscription = observeQuery(db, model.collectionName, () => model.aggregate(JSON.parse(pipelineKey)), {
96
+ next: (data) => setState({ data, loading: false, error: undefined }),
97
+ error: (error) => setState((s) => ({ ...s, loading: false, error })),
98
+ }, options.debounceMs !== undefined ? { debounceMs: options.debounceMs } : {});
99
+ return () => subscription.unsubscribe();
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [db, model, pipelineKey, options.enabled, options.debounceMs, nonce]);
102
+ return { ...state, refresh: useCallback(() => setNonce((n) => n + 1), []) };
103
+ }
104
+ /**
105
+ * Live sync status.
106
+ *
107
+ * Works in local-only mode too, reporting `"disabled"` with zero pending — so a status indicator
108
+ * can be rendered unconditionally instead of every screen branching on whether sync exists.
109
+ */
110
+ export function useSyncStatus(db) {
111
+ const [state, setState] = useState({
112
+ status: db.sync.status(),
113
+ pending: 0,
114
+ });
115
+ useEffect(() => {
116
+ const subscription = observeSyncStatus(db, {
117
+ next: (value) => setState({ status: value.status, pending: value.pending }),
118
+ });
119
+ return () => subscription.unsubscribe();
120
+ }, [db]);
121
+ const sync = useCallback(async () => {
122
+ await db.sync.now();
123
+ }, [db]);
124
+ return { ...state, enabled: db.syncEnabled, sync };
125
+ }
126
+ /** Subscribe to any AnchorDB event from a component. */
127
+ export function useAnchorEvent(db, event, handler) {
128
+ const ref = useRef(handler);
129
+ ref.current = handler;
130
+ useEffect(() => db.on(event, (payload) => ref.current(payload)), [db, event]);
131
+ }
132
+ /**
133
+ * Snapshot of connection state, via `useSyncExternalStore` so it is concurrent-safe and never
134
+ * tears between renders.
135
+ */
136
+ export function useAnchorReady(db) {
137
+ const subscribe = useCallback((onChange) => {
138
+ let cancelled = false;
139
+ void db.open().then(() => {
140
+ if (!cancelled)
141
+ onChange();
142
+ });
143
+ return () => {
144
+ cancelled = true;
145
+ };
146
+ }, [db]);
147
+ const ready = useRef(false);
148
+ return useSyncExternalStore(subscribe, () => {
149
+ if (!ready.current) {
150
+ void db.open().then(() => {
151
+ ready.current = true;
152
+ });
153
+ }
154
+ return ready.current;
155
+ }, () => false);
156
+ }
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "anchordb-react",
3
+ "version": "0.2.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "sideEffects": false,
7
+ "main": "./dist/cjs/index.js",
8
+ "module": "./dist/esm/index.js",
9
+ "types": "./dist/esm/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "import": {
13
+ "types": "./dist/esm/index.d.ts",
14
+ "default": "./dist/esm/index.js"
15
+ },
16
+ "require": {
17
+ "types": "./dist/cjs/index.d.ts",
18
+ "default": "./dist/cjs/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "files": [
24
+ "dist",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "scripts": {
29
+ "build": "rimraf dist && tsc -p tsconfig.build.json && tsc -p tsconfig.build.cjs.json && node ../core/scripts/finalize-dist.mjs",
30
+ "typecheck": "tsc --noEmit -p tsconfig.json",
31
+ "check:package": "publint && attw --pack . --profile node16",
32
+ "prepublishOnly": "npm run typecheck && npm run build"
33
+ },
34
+ "dependencies": {
35
+ "anchordb": "^0.2.0"
36
+ },
37
+ "devDependencies": {
38
+ "rimraf": "^6.0.1"
39
+ },
40
+ "publishConfig": {
41
+ "access": "public"
42
+ },
43
+ "description": "React and React Native hooks for AnchorDB.",
44
+ "peerDependencies": {
45
+ "react": ">=18"
46
+ },
47
+ "repository": {
48
+ "type": "git",
49
+ "url": "git+https://github.com/knnadeera/anchordb.git",
50
+ "directory": "packages/react"
51
+ },
52
+ "homepage": "https://github.com/knnadeera/anchordb#readme",
53
+ "bugs": {
54
+ "url": "https://github.com/knnadeera/anchordb/issues"
55
+ },
56
+ "engines": {
57
+ "node": ">=20"
58
+ },
59
+ "author": "Nisala Nadeera <knnadeera@gmail.com>"
60
+ }