@byearlybird/starling 0.14.0 → 0.17.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/README.md +82 -103
- package/dist/index.d.ts +215 -52
- package/dist/index.js +580 -212
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ Requires TypeScript 5 or higher.
|
|
|
17
17
|
## Quick Example
|
|
18
18
|
|
|
19
19
|
```typescript
|
|
20
|
-
import { createStore } from "@byearlybird/starling";
|
|
20
|
+
import { createStore, collection } from "@byearlybird/starling";
|
|
21
21
|
import { z } from "zod";
|
|
22
22
|
|
|
23
23
|
const userSchema = z.object({
|
|
@@ -26,12 +26,10 @@ const userSchema = z.object({
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
const store = createStore({
|
|
29
|
-
|
|
30
|
-
users: { schema: userSchema },
|
|
31
|
-
},
|
|
29
|
+
users: collection(userSchema, (data) => data.id),
|
|
32
30
|
});
|
|
33
31
|
|
|
34
|
-
store.
|
|
32
|
+
store.put("users", { id: "1", name: "Alice" });
|
|
35
33
|
const user = store.get("users", "1"); // { id: "1", name: "Alice" }
|
|
36
34
|
```
|
|
37
35
|
|
|
@@ -52,53 +50,62 @@ const user = store.get("users", "1"); // { id: "1", name: "Alice" }
|
|
|
52
50
|
A store holds one or more collections. Each collection has a schema that defines what data it can store.
|
|
53
51
|
|
|
54
52
|
```typescript
|
|
55
|
-
import { createStore } from "@byearlybird/starling";
|
|
53
|
+
import { createStore, collection } from "@byearlybird/starling";
|
|
56
54
|
import { z } from "zod";
|
|
57
55
|
|
|
58
56
|
const store = createStore({
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
57
|
+
users: collection(
|
|
58
|
+
z.object({
|
|
59
|
+
id: z.string(),
|
|
60
|
+
name: z.string(),
|
|
61
|
+
email: z.string().optional(),
|
|
62
|
+
}),
|
|
63
|
+
(data) => data.id,
|
|
64
|
+
),
|
|
65
|
+
notes: collection(
|
|
66
|
+
z.object({
|
|
67
|
+
id: z.string(),
|
|
68
|
+
content: z.string(),
|
|
69
|
+
}),
|
|
70
|
+
(data) => data.id,
|
|
71
|
+
),
|
|
74
72
|
});
|
|
75
73
|
```
|
|
76
74
|
|
|
77
75
|
### Adding Documents
|
|
78
76
|
|
|
79
|
-
Add new items to a collection
|
|
77
|
+
Add new items to a collection using `put()`:
|
|
80
78
|
|
|
81
79
|
```typescript
|
|
82
|
-
store.
|
|
80
|
+
store.put("users", {
|
|
83
81
|
id: "1",
|
|
84
82
|
name: "Alice",
|
|
85
83
|
email: "alice@example.com",
|
|
86
84
|
});
|
|
87
85
|
```
|
|
88
86
|
|
|
87
|
+
Or use `transact()` for atomic multi-collection operations:
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
store.transact((tx) => {
|
|
91
|
+
tx.put("users", { id: "1", name: "Alice", email: "alice@example.com" });
|
|
92
|
+
tx.put("notes", { id: "n1", content: "Hello" });
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
89
96
|
### Updating Documents
|
|
90
97
|
|
|
91
|
-
Update existing items
|
|
98
|
+
Update existing items using `patch()`:
|
|
92
99
|
|
|
93
100
|
```typescript
|
|
94
|
-
store.
|
|
101
|
+
store.patch("users", "1", {
|
|
95
102
|
email: "newemail@example.com",
|
|
96
103
|
});
|
|
97
104
|
```
|
|
98
105
|
|
|
99
106
|
### Removing Documents
|
|
100
107
|
|
|
101
|
-
Remove items
|
|
108
|
+
Remove items using `remove()`:
|
|
102
109
|
|
|
103
110
|
```typescript
|
|
104
111
|
store.remove("users", "1");
|
|
@@ -106,14 +113,14 @@ store.remove("users", "1");
|
|
|
106
113
|
|
|
107
114
|
### Reading Data
|
|
108
115
|
|
|
109
|
-
|
|
116
|
+
Read data directly from the store:
|
|
110
117
|
|
|
111
118
|
```typescript
|
|
112
119
|
// Get a single item
|
|
113
120
|
const user = store.get("users", "1");
|
|
114
121
|
|
|
115
122
|
// Get all items as an array
|
|
116
|
-
const allUsers = store.
|
|
123
|
+
const allUsers = store.list("users");
|
|
117
124
|
|
|
118
125
|
// You can easily derive other operations:
|
|
119
126
|
const userIds = allUsers.map((u) => u.id);
|
|
@@ -122,75 +129,48 @@ const hasUser = allUsers.some((u) => u.id === "1");
|
|
|
122
129
|
|
|
123
130
|
### Listening to Changes
|
|
124
131
|
|
|
125
|
-
Subscribe to changes with `
|
|
132
|
+
Subscribe to changes with `subscribe()`:
|
|
126
133
|
|
|
127
134
|
```typescript
|
|
128
|
-
//
|
|
129
|
-
store.
|
|
130
|
-
console.log(
|
|
131
|
-
|
|
135
|
+
// Subscribe to specific collections
|
|
136
|
+
const unsubscribe = store.subscribe(["users"], (event) => {
|
|
137
|
+
console.log("Users collection changed:", event);
|
|
138
|
+
const allUsers = store.list("users");
|
|
139
|
+
// Update UI, invalidate queries, etc.
|
|
132
140
|
});
|
|
133
141
|
|
|
134
|
-
//
|
|
135
|
-
store.
|
|
136
|
-
|
|
137
|
-
console.log("User change:", event.event.type, event.event.id);
|
|
138
|
-
if (event.event.type === "add") {
|
|
139
|
-
console.log("New user:", event.event.data);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
+
// Or subscribe to all changes
|
|
143
|
+
store.subscribe((event) => {
|
|
144
|
+
console.log("Store changed:", event);
|
|
142
145
|
});
|
|
146
|
+
|
|
147
|
+
// Later, unsubscribe
|
|
148
|
+
unsubscribe();
|
|
143
149
|
```
|
|
144
150
|
|
|
145
151
|
## Merging Data
|
|
146
152
|
|
|
147
153
|
Starling's core feature is conflict-free merging. When data changes in multiple places, Starling automatically resolves conflicts using timestamps.
|
|
148
154
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
Get the current state of your store:
|
|
155
|
+
Merge snapshots directly:
|
|
152
156
|
|
|
153
157
|
```typescript
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
### Merging a Snapshot
|
|
158
|
+
// Get current state as a snapshot
|
|
159
|
+
const snapshot = store.getState();
|
|
159
160
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
```typescript
|
|
163
|
-
// Get snapshot from another device
|
|
164
|
-
const otherSnapshot = await fetchFromServer();
|
|
161
|
+
// Send to server or save locally
|
|
162
|
+
await sendToServer(snapshot);
|
|
165
163
|
|
|
166
|
-
//
|
|
167
|
-
|
|
164
|
+
// Later, merge a snapshot from another source
|
|
165
|
+
const remoteSnapshot = await fetchFromServer();
|
|
166
|
+
store.merge(remoteSnapshot);
|
|
168
167
|
```
|
|
169
168
|
|
|
170
169
|
Starling automatically resolves conflicts. If the same field was changed in both places, it keeps the change with the newer timestamp (Last-Write-Wins).
|
|
171
170
|
|
|
172
|
-
### Syncing Between Two Stores
|
|
173
|
-
|
|
174
|
-
Here's a simple example of syncing between two stores:
|
|
175
|
-
|
|
176
|
-
```typescript
|
|
177
|
-
const store1 = createStore({ collections: { users: { schema: userSchema } } });
|
|
178
|
-
const store2 = createStore({ collections: { users: { schema: userSchema } } });
|
|
179
|
-
|
|
180
|
-
// Add data to store1
|
|
181
|
-
store1.add("users", { id: "1", name: "Alice" });
|
|
182
|
-
|
|
183
|
-
// Sync to store2
|
|
184
|
-
const snapshot = store1.getSnapshot();
|
|
185
|
-
store2.merge(snapshot);
|
|
186
|
-
|
|
187
|
-
// Now store2 has the same data
|
|
188
|
-
console.log(store2.get("users", "1")); // { id: "1", name: "Alice" }
|
|
189
|
-
```
|
|
190
|
-
|
|
191
171
|
## Reactivity Integration
|
|
192
172
|
|
|
193
|
-
Starling is framework-agnostic. Use `
|
|
173
|
+
Starling is framework-agnostic. Use `subscribe()` to integrate with your reactive system:
|
|
194
174
|
|
|
195
175
|
### React with TanStack Query
|
|
196
176
|
|
|
@@ -201,16 +181,14 @@ function useUsers() {
|
|
|
201
181
|
const queryClient = useQueryClient();
|
|
202
182
|
|
|
203
183
|
useEffect(() => {
|
|
204
|
-
return store.
|
|
205
|
-
|
|
206
|
-
queryClient.invalidateQueries({ queryKey: ["users"] });
|
|
207
|
-
}
|
|
184
|
+
return store.subscribe(["users"], () => {
|
|
185
|
+
queryClient.invalidateQueries({ queryKey: ["users"] });
|
|
208
186
|
});
|
|
209
187
|
}, []);
|
|
210
188
|
|
|
211
189
|
return useQuery({
|
|
212
190
|
queryKey: ["users"],
|
|
213
|
-
queryFn: () => store.
|
|
191
|
+
queryFn: () => store.list("users"),
|
|
214
192
|
});
|
|
215
193
|
}
|
|
216
194
|
```
|
|
@@ -222,11 +200,8 @@ import { useSyncExternalStore } from "react";
|
|
|
222
200
|
|
|
223
201
|
function useUsers() {
|
|
224
202
|
return useSyncExternalStore(
|
|
225
|
-
(callback) =>
|
|
226
|
-
|
|
227
|
-
if (event.collection === "users") callback();
|
|
228
|
-
}),
|
|
229
|
-
() => store.getAll("users"),
|
|
203
|
+
(callback) => store.subscribe(["users"], () => callback()),
|
|
204
|
+
() => store.list("users"),
|
|
230
205
|
);
|
|
231
206
|
}
|
|
232
207
|
```
|
|
@@ -236,11 +211,9 @@ function useUsers() {
|
|
|
236
211
|
```typescript
|
|
237
212
|
import { writable } from "svelte/store";
|
|
238
213
|
|
|
239
|
-
const users = writable(store.
|
|
240
|
-
store.
|
|
241
|
-
|
|
242
|
-
users.set(store.getAll("users"));
|
|
243
|
-
}
|
|
214
|
+
const users = writable(store.list("users"));
|
|
215
|
+
store.subscribe(["users"], () => {
|
|
216
|
+
users.set(store.list("users"));
|
|
244
217
|
});
|
|
245
218
|
```
|
|
246
219
|
|
|
@@ -249,11 +222,9 @@ store.onChange((event) => {
|
|
|
249
222
|
```typescript
|
|
250
223
|
import { ref } from "vue";
|
|
251
224
|
|
|
252
|
-
const users = ref(store.
|
|
253
|
-
store.
|
|
254
|
-
|
|
255
|
-
users.value = store.getAll("users");
|
|
256
|
-
}
|
|
225
|
+
const users = ref(store.list("users"));
|
|
226
|
+
store.subscribe(["users"], () => {
|
|
227
|
+
users.value = store.list("users");
|
|
257
228
|
});
|
|
258
229
|
```
|
|
259
230
|
|
|
@@ -290,17 +261,25 @@ const schema = type({ id: "string", name: "string" });
|
|
|
290
261
|
|
|
291
262
|
### Store Methods
|
|
292
263
|
|
|
293
|
-
- `add(collection, data)` - Add a new document to a collection
|
|
294
264
|
- `get(collection, id)` - Get a document by ID from a collection
|
|
295
|
-
- `
|
|
296
|
-
- `
|
|
297
|
-
- `
|
|
298
|
-
- `
|
|
299
|
-
- `
|
|
300
|
-
- `
|
|
265
|
+
- `list(collection)` - Get all documents as an array from a collection
|
|
266
|
+
- `put(collection, data)` - Insert or replace a document (upsert, revives tombstoned IDs)
|
|
267
|
+
- `patch(collection, id, data)` - Partially update an existing document (throws if ID missing)
|
|
268
|
+
- `remove(collection, id)` - Remove a document
|
|
269
|
+
- `transact(callback)` - Execute operations atomically. Collections are cloned lazily on first access.
|
|
270
|
+
- `subscribe(callback)` - Subscribe to all collection changes
|
|
271
|
+
- `subscribe(collections, callback)` - Subscribe to changes in specific collections
|
|
272
|
+
- `getState()` - Get current store state as a snapshot
|
|
273
|
+
- `merge(snapshot)` - Merge a snapshot into the store
|
|
301
274
|
|
|
302
275
|
For full type definitions, see the TypeScript types exported from the package.
|
|
303
276
|
|
|
277
|
+
## Package structure
|
|
278
|
+
|
|
279
|
+
- **`lib/core/`** – CRDT primitives: hybrid logical clock, per-field atoms (LWW), tombstones, and document/collection merging.
|
|
280
|
+
- **`lib/store/`** – Store API with collections, batching, queries, and change subscriptions.
|
|
281
|
+
- **`lib/middleware/`** – Optional middleware (e.g. persistence).
|
|
282
|
+
|
|
304
283
|
## Development
|
|
305
284
|
|
|
306
285
|
```bash
|
package/dist/index.d.ts
CHANGED
|
@@ -5,22 +5,92 @@ type Clock = {
|
|
|
5
5
|
ms: number;
|
|
6
6
|
seq: number;
|
|
7
7
|
};
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Branded type for HLC timestamps.
|
|
10
|
+
* Stamps are lexicographically sortable hex strings encoding (ms, seq, nonce).
|
|
11
|
+
*/
|
|
12
|
+
type Stamp = string & {
|
|
13
|
+
readonly __brand: "Stamp";
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
+
declare function advanceClock(current: Clock, next: Clock): Clock;
|
|
16
|
+
declare function makeStamp(ms: number, seq: number): Stamp;
|
|
17
|
+
/**
|
|
18
|
+
* Parse and validate a string as a Stamp. Use when deserializing stored state.
|
|
19
|
+
* Throws if the value is not a valid 24-character hex string.
|
|
20
|
+
*/
|
|
21
|
+
declare function asStamp(value: string): Stamp;
|
|
15
22
|
//#endregion
|
|
16
23
|
//#region lib/core/tombstone.d.ts
|
|
17
|
-
type Tombstones = Record<string,
|
|
24
|
+
type Tombstones = Record<string, Stamp>;
|
|
25
|
+
declare function isDeleted(id: string, tombstones: Tombstones): boolean;
|
|
26
|
+
declare function mergeTombstones(target: Tombstones, source: Tombstones): Tombstones;
|
|
18
27
|
//#endregion
|
|
19
|
-
//#region lib/core/
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
//#region lib/core/types.d.ts
|
|
29
|
+
declare const KEYS: {
|
|
30
|
+
readonly VAL: "~val";
|
|
31
|
+
readonly TS: "~ts";
|
|
23
32
|
};
|
|
33
|
+
/**
|
|
34
|
+
* Base constraint for plain document shapes - any object with string keys.
|
|
35
|
+
*/
|
|
36
|
+
type Document = Record<string, unknown>;
|
|
37
|
+
type Atom<T> = {
|
|
38
|
+
[KEYS.VAL]: T;
|
|
39
|
+
[KEYS.TS]: Stamp;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Atomized document with plain shape T. Each key K is stored as Atom<T[K]> (per-field).
|
|
43
|
+
*/
|
|
44
|
+
type AtomizedDocument<T extends Document> = { [K in keyof T]: Atom<T[K]> };
|
|
45
|
+
/**
|
|
46
|
+
* Collection of atomized documents with shape T. Map from document ID to AtomizedDocument<T>.
|
|
47
|
+
*/
|
|
48
|
+
type Collection<T extends Document> = Record<string, AtomizedDocument<T>>;
|
|
49
|
+
/**
|
|
50
|
+
* Collection state containing documents and tombstones.
|
|
51
|
+
*/
|
|
52
|
+
type CollectionState<T extends Document> = {
|
|
53
|
+
documents: Collection<T>;
|
|
54
|
+
tombstones: Tombstones;
|
|
55
|
+
};
|
|
56
|
+
type StoreState = {
|
|
57
|
+
clock: Clock;
|
|
58
|
+
collections: Record<string, CollectionState<Document>>;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Type guard to check if a value is an AtomizedDocument.
|
|
62
|
+
* An AtomizedDocument is an object where all values are Atoms.
|
|
63
|
+
*/
|
|
64
|
+
declare function isAtomizedDocument<T extends Document>(value: unknown): value is AtomizedDocument<T>;
|
|
65
|
+
/**
|
|
66
|
+
* Type guard to check if a value is a Collection.
|
|
67
|
+
* A Collection is a Record where all values are AtomizedDocuments.
|
|
68
|
+
*/
|
|
69
|
+
declare function isCollection<T extends Document>(value: unknown): value is Collection<T>;
|
|
70
|
+
/**
|
|
71
|
+
* Type guard to check if a value is a CollectionState.
|
|
72
|
+
* A CollectionState has documents (Collection) and tombstones (Record<string, string>).
|
|
73
|
+
*/
|
|
74
|
+
declare function isCollectionState<T extends Document>(value: unknown): value is CollectionState<T>;
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region lib/core/atomizer.d.ts
|
|
77
|
+
declare function pack<T>(value: T, timestamp: Stamp): Atom<T>;
|
|
78
|
+
declare function unpack<T>(node: unknown): T | undefined;
|
|
79
|
+
declare function isAtom(node: unknown): node is Atom<unknown>;
|
|
80
|
+
declare function atomize<T extends Document>(data: T, timestamp: Stamp): AtomizedDocument<T>;
|
|
81
|
+
//#endregion
|
|
82
|
+
//#region lib/core/lens.d.ts
|
|
83
|
+
declare function createReadLens<T extends Document>(doc: AtomizedDocument<T>): T;
|
|
84
|
+
//#endregion
|
|
85
|
+
//#region lib/core/merge.d.ts
|
|
86
|
+
/** Merges incoming doc fields into local. Adds new keys from incoming; LWW on conflicts. */
|
|
87
|
+
declare function mergeDocs<T extends Document>(local: AtomizedDocument<T>, incoming: Partial<AtomizedDocument<T>>): AtomizedDocument<T>;
|
|
88
|
+
/**
|
|
89
|
+
* Merges two collection states, respecting tombstones.
|
|
90
|
+
* Documents that are tombstoned are excluded from the result.
|
|
91
|
+
* For documents that exist in both collections, fields are merged using LWW semantics.
|
|
92
|
+
*/
|
|
93
|
+
declare function mergeCollections<T extends Document>(local: CollectionState<T>, incoming: CollectionState<T>): CollectionState<T>;
|
|
24
94
|
//#endregion
|
|
25
95
|
//#region lib/store/schema.d.ts
|
|
26
96
|
/**
|
|
@@ -28,51 +98,144 @@ type Collection = {
|
|
|
28
98
|
*/
|
|
29
99
|
type AnyObject = StandardSchemaV1<Record<string, any>>;
|
|
30
100
|
type Output<T extends AnyObject> = StandardSchemaV1.InferOutput<T>;
|
|
31
|
-
type
|
|
101
|
+
type CollectionDef<T extends Document = Document, Id extends string = string> = {
|
|
102
|
+
readonly "~docType": T;
|
|
103
|
+
readonly "~idType": Id;
|
|
104
|
+
schema: AnyObject;
|
|
105
|
+
getId: (data: T) => Id;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Helper function to create a collection definition with proper type inference.
|
|
109
|
+
* This captures the schema output type directly as the document type.
|
|
110
|
+
*
|
|
111
|
+
* @example
|
|
112
|
+
* ```ts
|
|
113
|
+
* const store = createStore({
|
|
114
|
+
* users: define(userSchema, (data) => data.id),
|
|
115
|
+
* });
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
declare function define<S extends AnyObject, Id extends string = string>(schema: S, getId: (data: Output<S>) => Id): CollectionDef<Output<S>, Id>;
|
|
119
|
+
/**
|
|
120
|
+
* Extract the document type from a CollectionDef
|
|
121
|
+
*/
|
|
122
|
+
type DocType<C extends CollectionDef> = C["~docType"];
|
|
123
|
+
/**
|
|
124
|
+
* Extract the ID type from a CollectionDef
|
|
125
|
+
*/
|
|
126
|
+
type IdType<C extends CollectionDef> = C["~idType"];
|
|
127
|
+
/**
|
|
128
|
+
* Configuration for all collections in a store.
|
|
129
|
+
* All collection definitions must be created using the `collection()` helper.
|
|
130
|
+
*
|
|
131
|
+
* Uses `any` for type parameters to allow any CollectionDef to be stored,
|
|
132
|
+
* while still preserving specific types through inference in createStore.
|
|
133
|
+
*/
|
|
134
|
+
type StoreConfig = Record<string, CollectionDef<any, any>>;
|
|
135
|
+
/**
|
|
136
|
+
* Valid collection name from a store config
|
|
137
|
+
*/
|
|
138
|
+
type CollectionName<T extends StoreConfig> = keyof T & string;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region lib/store/types.d.ts
|
|
141
|
+
type StoreChangeEvent<T extends StoreConfig> = { [K in keyof T]?: true };
|
|
32
142
|
//#endregion
|
|
33
143
|
//#region lib/store/store.d.ts
|
|
34
|
-
type
|
|
35
|
-
|
|
36
|
-
|
|
144
|
+
type TransactionAPI<T extends StoreConfig> = {
|
|
145
|
+
get<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>): DocType<T[N]> | undefined;
|
|
146
|
+
list<N extends CollectionName<T>>(collection: N): DocType<T[N]>[];
|
|
147
|
+
put<N extends CollectionName<T>>(collection: N, data: DocType<T[N]>): DocType<T[N]>;
|
|
148
|
+
patch<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>, data: Partial<DocType<T[N]>>): DocType<T[N]>;
|
|
149
|
+
remove<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>): void;
|
|
37
150
|
};
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
151
|
+
declare class Store<T extends StoreConfig> {
|
|
152
|
+
#private;
|
|
153
|
+
constructor(config: T);
|
|
154
|
+
get<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>): DocType<T[N]> | undefined;
|
|
155
|
+
list<N extends CollectionName<T>>(collection: N): DocType<T[N]>[];
|
|
156
|
+
put<N extends CollectionName<T>>(collection: N, data: DocType<T[N]>): DocType<T[N]>;
|
|
157
|
+
patch<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>, data: Partial<DocType<T[N]>>): DocType<T[N]>;
|
|
158
|
+
remove<N extends CollectionName<T>>(collection: N, id: IdType<T[N]>): void;
|
|
159
|
+
subscribe(callback: (event: StoreChangeEvent<T>) => void): () => void;
|
|
160
|
+
getState(): StoreState;
|
|
161
|
+
merge(snapshot: StoreState): StoreChangeEvent<T>;
|
|
162
|
+
transact<R>(callback: (tx: TransactionAPI<T>) => R): R;
|
|
163
|
+
}
|
|
164
|
+
declare function createStore<T extends StoreConfig>(config: T): Store<T>;
|
|
165
|
+
//#endregion
|
|
166
|
+
//#region lib/middleware/idb-persister.d.ts
|
|
167
|
+
interface IPersister {
|
|
168
|
+
init(): Promise<void>;
|
|
169
|
+
dispose(): Promise<void>;
|
|
170
|
+
}
|
|
171
|
+
type PersistenceOptions = {
|
|
172
|
+
key: string;
|
|
173
|
+
storeName?: string;
|
|
174
|
+
debounceMs?: number;
|
|
175
|
+
channelName?: string;
|
|
176
|
+
serialize?: (state: StoreState) => string;
|
|
177
|
+
deserialize?: (serialized: string) => StoreState;
|
|
42
178
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
179
|
+
declare class IdbPersister<T extends StoreConfig> implements IPersister {
|
|
180
|
+
#private;
|
|
181
|
+
constructor(store: Store<T>, options: PersistenceOptions);
|
|
182
|
+
init(): Promise<void>;
|
|
183
|
+
dispose(): Promise<void>;
|
|
184
|
+
subscribe(listener: (serialized: string) => void): () => void;
|
|
185
|
+
}
|
|
186
|
+
//#endregion
|
|
187
|
+
//#region lib/middleware/debouncer.d.ts
|
|
188
|
+
/**
|
|
189
|
+
* A simple debouncer that delays callback execution until after
|
|
190
|
+
* a specified time has elapsed since the last trigger.
|
|
191
|
+
*/
|
|
192
|
+
declare class Debouncer {
|
|
193
|
+
#private;
|
|
194
|
+
constructor(ms: number, callback: () => void | Promise<void>);
|
|
195
|
+
/**
|
|
196
|
+
* Trigger the debouncer. Resets the timer if already pending.
|
|
197
|
+
*/
|
|
198
|
+
trigger(): void;
|
|
199
|
+
/**
|
|
200
|
+
* Cancel any pending execution without invoking the callback.
|
|
201
|
+
*/
|
|
202
|
+
cancel(): void;
|
|
203
|
+
/**
|
|
204
|
+
* Immediately execute the callback if there's a pending timer,
|
|
205
|
+
* then cancel the timer. Does nothing if no timer is pending.
|
|
206
|
+
*/
|
|
207
|
+
flush(): void | Promise<void>;
|
|
208
|
+
/**
|
|
209
|
+
* Check if there's a pending execution.
|
|
210
|
+
*/
|
|
211
|
+
get pending(): boolean;
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region lib/middleware/broadcast-sync.d.ts
|
|
215
|
+
type BroadcastSyncOptions = {
|
|
216
|
+
channelName: string;
|
|
217
|
+
onMessage: (state: StoreState) => void;
|
|
72
218
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Cross-tab synchronization via BroadcastChannel.
|
|
221
|
+
* Handles environments where BroadcastChannel is unavailable.
|
|
222
|
+
*/
|
|
223
|
+
declare class BroadcastSync {
|
|
224
|
+
#private;
|
|
225
|
+
constructor(options: BroadcastSyncOptions);
|
|
226
|
+
/**
|
|
227
|
+
* Broadcast state to other tabs. Does nothing if BroadcastChannel is unavailable.
|
|
228
|
+
*/
|
|
229
|
+
broadcast(state: StoreState): void;
|
|
230
|
+
/**
|
|
231
|
+
* Close the BroadcastChannel and clean up resources.
|
|
232
|
+
*/
|
|
233
|
+
close(): void;
|
|
234
|
+
/**
|
|
235
|
+
* Check if BroadcastChannel is available and connected.
|
|
236
|
+
*/
|
|
237
|
+
get available(): boolean;
|
|
238
|
+
}
|
|
76
239
|
//#endregion
|
|
77
|
-
export { type AnyObject, type
|
|
240
|
+
export { type AnyObject, Atom, AtomizedDocument, BroadcastSync, type BroadcastSyncOptions, Clock, Collection, type CollectionDef, CollectionState, Debouncer, type DocType, Document, type IPersister, type IdType, IdbPersister, KEYS, type PersistenceOptions, Stamp, Store, type StoreChangeEvent, type StoreConfig, type StoreState, Tombstones, type TransactionAPI, advanceClock, asStamp, atomize, define as collection, createReadLens, createStore, isAtom, isAtomizedDocument, isCollection, isCollectionState, isDeleted, makeStamp, mergeCollections, mergeDocs, mergeTombstones, pack, unpack };
|
|
78
241
|
//# sourceMappingURL=index.d.ts.map
|