silosdk 0.0.6 → 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -25,14 +25,14 @@ TanStack owns:
25
25
  yarn add silosdk @tanstack/react-db @tanstack/react-query expo-crypto expo-sqlite expo-file-system
26
26
  ```
27
27
 
28
- ## Sources
28
+ ## Stores
29
29
 
30
- Sources are SQLite-backed document stores that produce options for TanStack DB
31
- collections.
30
+ Stores define SQLite-backed document collections and a link collection for
31
+ relationships between them. They produce options for TanStack DB collections.
32
32
 
33
33
  ```ts
34
34
  import { createCollection } from '@tanstack/react-db'
35
- import { createID, source } from 'silosdk/source'
35
+ import { createID, createStore } from 'silosdk/store'
36
36
 
37
37
  type Post = {
38
38
  title: string
@@ -41,14 +41,28 @@ type Post = {
41
41
  coverImage: string | null
42
42
  }
43
43
 
44
- export const postsSource = source<Post>('posts', {
45
- title: '',
46
- body: '',
47
- published: false,
48
- coverImage: null,
44
+ type Tag = {
45
+ name: string
46
+ }
47
+
48
+ export const store = createStore<{
49
+ posts: Post
50
+ tags: Tag
51
+ }>({
52
+ posts: {
53
+ title: '',
54
+ body: '',
55
+ published: false,
56
+ coverImage: null,
57
+ },
58
+ tags: {
59
+ name: '',
60
+ },
49
61
  })
50
62
 
51
- export const posts = createCollection(postsSource.collectionOptions())
63
+ export const posts = createCollection(store.collectionOptions('posts'))
64
+ export const tags = createCollection(store.collectionOptions('tags'))
65
+ export const links = createCollection(store.linkCollectionOptions())
52
66
  ```
53
67
 
54
68
  Query and mutate with TanStack DB directly:
@@ -95,6 +109,49 @@ Source rows are intentionally flat. Field values must be:
95
109
  type FieldValue = string | number | boolean | null
96
110
  ```
97
111
 
112
+ Create links with the link collection utilities:
113
+
114
+ ```ts
115
+ await links.utils.link('posts', postId, 'tags', tagId)
116
+ await links.utils.unlink('posts', postId, 'tags', tagId)
117
+
118
+ const isLinked = links.utils.has('posts', postId, 'tags', tagId)
119
+ ```
120
+
121
+ Link storage is flat and directionless. Collection names are sorted
122
+ lexicographically and IDs are stored in the same order. Query rows expose nested
123
+ paths for each collection pair:
124
+
125
+ ```tsx
126
+ import { eq, useLiveQuery } from '@tanstack/react-db'
127
+
128
+ const { data: taggedPosts = [] } = useLiveQuery((q) =>
129
+ q
130
+ .from({ posts })
131
+ .join({ links }, ({ posts, links }) =>
132
+ eq(posts.id, links.tags.posts.id),
133
+ )
134
+ .join({ tags }, ({ links, tags }) =>
135
+ eq(links.posts.tags.id, tags.id),
136
+ ),
137
+ )
138
+ ```
139
+
140
+ For a `posts` to `tags` link:
141
+
142
+ ```ts
143
+ links.tags.posts.id // post id
144
+ links.posts.tags.id // tag id
145
+ ```
146
+
147
+ Same-collection links are not supported. Define another collection when you need
148
+ a different semantic relationship.
149
+
150
+ ## Sources
151
+
152
+ The lower-level `source()` API remains available from `silosdk/store` when you
153
+ only need one collection definition.
154
+
98
155
  ## Settings
99
156
 
100
157
  Settings are small persisted primitive values backed by `expo-sqlite/kv-store`.
@@ -171,7 +228,8 @@ empty, invalid, unknown, or missing on disk.
171
228
  Silo exposes definition objects and option factories:
172
229
 
173
230
  ```ts
174
- createCollection(postsSource.collectionOptions())
231
+ createCollection(store.collectionOptions('posts'))
232
+ createCollection(store.linkCollectionOptions())
175
233
  useQuery(theme.queryOptions())
176
234
  useMutation(theme.mutationOptions({ queryClient }))
177
235
  useQuery(media.queryOptions(ref))
package/dist/store.cjs ADDED
@@ -0,0 +1,432 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let expo_crypto = require("expo-crypto");
3
+ let expo_sqlite = require("expo-sqlite");
4
+
5
+ //#region src/store/index.ts
6
+ const sourceNames = /* @__PURE__ */ new Set();
7
+ const sourceNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
8
+ const reservedDocumentFields = new Set([
9
+ "id",
10
+ "data",
11
+ "set"
12
+ ]);
13
+ const deletedDocs = /* @__PURE__ */ new WeakSet();
14
+ function createID() {
15
+ return (0, expo_crypto.randomUUID)();
16
+ }
17
+ function createStore(defaults) {
18
+ const names = Object.keys(defaults);
19
+ const nameSet = new Set(names);
20
+ for (const name of names) {
21
+ assertSourceName(name);
22
+ assertFlatDefaults(name, defaults[name]);
23
+ }
24
+ if (new Set(names).size !== names.length) throw new Error("Store collection names must be unique.");
25
+ return {
26
+ names,
27
+ collectionOptions(name, options = {}) {
28
+ assertStoreCollectionName(nameSet, name);
29
+ return createSourceCollectionOptions(name, defaults[name], options);
30
+ },
31
+ linkCollectionOptions(options = {}) {
32
+ return createLinkCollectionOptions(names, nameSet, options);
33
+ }
34
+ };
35
+ }
36
+ function source(name, defaults) {
37
+ assertSourceName(name);
38
+ assertFlatDefaults(name, defaults);
39
+ if (sourceNames.has(name)) throw new Error(`Source "${name}" is already registered. Define each source once and import the existing source instead.`);
40
+ sourceNames.add(name);
41
+ return {
42
+ name,
43
+ defaults,
44
+ schema: createSourceSchema(name, defaults),
45
+ collectionOptions(options = {}) {
46
+ return createSourceCollectionOptions(name, defaults, options);
47
+ }
48
+ };
49
+ }
50
+ function createSourceCollectionOptions(name, defaults, options = {}) {
51
+ return {
52
+ id: name,
53
+ getKey: (item) => item.id,
54
+ schema: createSourceSchema(name, defaults),
55
+ startSync: options.startSync,
56
+ sync: { sync({ begin, write, commit, markReady, truncate }) {
57
+ const rows = loadRows(name);
58
+ begin();
59
+ truncate();
60
+ for (const row of rows) write({
61
+ type: "insert",
62
+ value: row
63
+ });
64
+ commit();
65
+ markReady();
66
+ } },
67
+ onInsert: async ({ transaction }) => {
68
+ await insertRows(name, transaction.mutations.map((mutation) => mutation.modified));
69
+ },
70
+ onUpdate: async ({ transaction }) => {
71
+ await updateRows(name, transaction.mutations);
72
+ },
73
+ onDelete: async ({ transaction }) => {
74
+ await deleteRows(name, transaction.mutations);
75
+ }
76
+ };
77
+ }
78
+ function createLinkCollectionOptions(names, nameSet, options = {}) {
79
+ const schema = createLinkSchema();
80
+ let syncBegin;
81
+ let syncWrite;
82
+ let syncCommit;
83
+ const writeSyncedLink = (type, row) => {
84
+ if (!syncBegin || !syncWrite || !syncCommit) return;
85
+ syncBegin();
86
+ syncWrite({
87
+ type,
88
+ value: row
89
+ });
90
+ syncCommit();
91
+ };
92
+ return {
93
+ id: "silo-links",
94
+ getKey: (item) => item.id,
95
+ schema,
96
+ startSync: options.startSync,
97
+ sync: { sync({ begin, write, commit, markReady, truncate }) {
98
+ syncBegin = begin;
99
+ syncWrite = write;
100
+ syncCommit = commit;
101
+ const rows = loadLinkRows().map((row) => createLinkRow(names, row));
102
+ begin();
103
+ truncate();
104
+ for (const row of rows) write({
105
+ type: "insert",
106
+ value: row
107
+ });
108
+ commit();
109
+ markReady();
110
+ } },
111
+ onInsert: async ({ transaction }) => {
112
+ for (const mutation of transaction.mutations) insertLinkRow(extractStoredLinkRow(mutation.modified));
113
+ },
114
+ onDelete: async ({ transaction }) => {
115
+ for (const mutation of transaction.mutations) deleteLinkRow(String(mutation.key));
116
+ },
117
+ utils: {
118
+ async link(collectionA, idA, collectionB, idB) {
119
+ const row = createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB);
120
+ const exists = hasLinkRow(row.id);
121
+ insertLinkRow(row);
122
+ if (!exists) writeSyncedLink("insert", createLinkRow(names, row));
123
+ },
124
+ async unlink(collectionA, idA, collectionB, idB) {
125
+ const row = createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB);
126
+ const exists = hasLinkRow(row.id);
127
+ deleteLinkRow(row.id);
128
+ if (exists) writeSyncedLink("delete", createLinkRow(names, row));
129
+ },
130
+ has(collectionA, idA, collectionB, idB) {
131
+ return hasLinkRow(createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB).id);
132
+ }
133
+ }
134
+ };
135
+ }
136
+ function assertSourceName(name) {
137
+ if (!sourceNamePattern.test(name)) throw new Error(`Source names must start with a letter and contain only letters, numbers, underscores, or hyphens. Received "${name}".`);
138
+ }
139
+ function assertStoreCollectionName(nameSet, name) {
140
+ if (!nameSet.has(name)) throw new Error(`Store collection "${name}" is not registered.`);
141
+ }
142
+ function assertLinkCollectionName(nameSet, name) {
143
+ if (!nameSet.has(name)) throw new Error(`Cannot link unknown collection "${name}".`);
144
+ }
145
+ function createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB) {
146
+ assertLinkCollectionName(nameSet, collectionA);
147
+ assertLinkCollectionName(nameSet, collectionB);
148
+ if (collectionA === collectionB) throw new Error(`Links between the same collection are not supported. Received "${collectionA}".`);
149
+ const [firstCollection, firstId, secondCollection, secondId] = collectionA < collectionB ? [
150
+ collectionA,
151
+ idA,
152
+ collectionB,
153
+ idB
154
+ ] : [
155
+ collectionB,
156
+ idB,
157
+ collectionA,
158
+ idA
159
+ ];
160
+ const collections = encodePair(firstCollection, secondCollection);
161
+ const ids = encodePair(firstId, secondId);
162
+ return {
163
+ id: encodePair(collections, ids),
164
+ collections,
165
+ ids,
166
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
167
+ };
168
+ }
169
+ function createLinkRow(names, row) {
170
+ const [firstCollection, secondCollection] = decodePair(row.collections);
171
+ const [firstId, secondId] = decodePair(row.ids);
172
+ const result = {
173
+ id: row.id,
174
+ collections: row.collections,
175
+ ids: row.ids,
176
+ createdAt: row.createdAt
177
+ };
178
+ for (const other of names) {
179
+ const byCurrent = {};
180
+ for (const current of names) {
181
+ let id = null;
182
+ if (other !== current) {
183
+ if (current === firstCollection && other === secondCollection) id = firstId;
184
+ else if (current === secondCollection && other === firstCollection) id = secondId;
185
+ }
186
+ byCurrent[current] = { id };
187
+ }
188
+ result[other] = byCurrent;
189
+ }
190
+ return result;
191
+ }
192
+ function extractStoredLinkRow(row) {
193
+ if (!isRecord(row)) throw new Error("Expected a link row object.");
194
+ if (typeof row.id !== "string" || typeof row.collections !== "string" || typeof row.ids !== "string" || typeof row.createdAt !== "string") throw new Error("Link rows must include string id, collections, ids, and createdAt fields.");
195
+ return {
196
+ id: row.id,
197
+ collections: row.collections,
198
+ ids: row.ids,
199
+ createdAt: row.createdAt
200
+ };
201
+ }
202
+ function encodePair(first, second) {
203
+ return `${encodePart(first)}:${encodePart(second)}`;
204
+ }
205
+ function decodePair(value) {
206
+ const parts = value.split(":");
207
+ if (parts.length !== 2) throw new Error(`Invalid encoded link pair "${value}".`);
208
+ return [decodePart(parts[0]), decodePart(parts[1])];
209
+ }
210
+ function encodePart(value) {
211
+ return encodeURIComponent(value);
212
+ }
213
+ function decodePart(value) {
214
+ return decodeURIComponent(value);
215
+ }
216
+ function assertFlatDefaults(sourceName, defaults) {
217
+ for (const [field, defaultValue] of Object.entries(defaults)) {
218
+ if (reservedDocumentFields.has(field)) throw new Error(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`);
219
+ if (!isFieldValue(resolveDefault(defaultValue))) throw new Error(`Source "${sourceName}" field "${field}" must default to a string, number, boolean, null, or a function returning one of those values.`);
220
+ }
221
+ }
222
+ function createSourceSchema(sourceName, defaults) {
223
+ return { "~standard": {
224
+ version: 1,
225
+ vendor: "silo",
226
+ validate(value) {
227
+ if (!isRecord(value)) return failure("Expected a flat source row object.");
228
+ if (typeof value.id !== "string") return failure("Expected source document field \"id\" to be a string.", ["id"]);
229
+ const inputData = isRecord(value.data) ? value.data : extractDocumentData(value);
230
+ const data = {};
231
+ for (const [field, fieldValue] of Object.entries(inputData)) {
232
+ if (reservedDocumentFields.has(field)) return failure(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`, ["data", field]);
233
+ if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, ["data", field]);
234
+ data[field] = fieldValue;
235
+ }
236
+ for (const [field, defaultValue] of Object.entries(defaults)) {
237
+ if (field in data) continue;
238
+ data[field] = resolveDefault(defaultValue);
239
+ }
240
+ return { value: createDoc(value.id, data) };
241
+ }
242
+ } };
243
+ }
244
+ function createLinkSchema() {
245
+ return { "~standard": {
246
+ version: 1,
247
+ vendor: "silo",
248
+ validate(value) {
249
+ try {
250
+ extractStoredLinkRow(value);
251
+ return { value };
252
+ } catch (error) {
253
+ return failure(error instanceof Error ? error.message : "Expected a link row.");
254
+ }
255
+ }
256
+ } };
257
+ }
258
+ function createDoc(id, data) {
259
+ const doc = {};
260
+ Object.defineProperty(doc, "id", {
261
+ value: id,
262
+ enumerable: true,
263
+ writable: false,
264
+ configurable: false
265
+ });
266
+ for (const [field, value] of Object.entries(data)) Object.defineProperty(doc, field, {
267
+ value,
268
+ enumerable: true,
269
+ writable: true,
270
+ configurable: true
271
+ });
272
+ Object.defineProperty(doc, "data", {
273
+ value() {
274
+ return deletedDocs.has(this) ? null : extractDocumentData(this);
275
+ },
276
+ enumerable: true,
277
+ writable: false,
278
+ configurable: false
279
+ });
280
+ Object.defineProperty(doc, "set", {
281
+ get() {
282
+ return function setDocumentData(patch) {
283
+ Object.assign(this, patch);
284
+ };
285
+ },
286
+ enumerable: true,
287
+ configurable: false
288
+ });
289
+ return doc;
290
+ }
291
+ function extractDocumentData(value) {
292
+ if (!isRecord(value)) return {};
293
+ const data = {};
294
+ for (const [field, fieldValue] of Object.entries(value)) {
295
+ if (reservedDocumentFields.has(field) || field.startsWith("$") || typeof fieldValue === "function") continue;
296
+ if (isFieldValue(fieldValue)) data[field] = fieldValue;
297
+ }
298
+ return data;
299
+ }
300
+ function resolveDefault(value) {
301
+ const resolved = typeof value === "function" ? value() : value;
302
+ if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
303
+ return resolved;
304
+ }
305
+ function isRecord(value) {
306
+ return typeof value === "object" && value !== null && !Array.isArray(value);
307
+ }
308
+ function isFieldValue(value) {
309
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
310
+ }
311
+ function failure(message, path) {
312
+ return { issues: [{
313
+ message,
314
+ path
315
+ }] };
316
+ }
317
+ let database;
318
+ function getDatabase() {
319
+ if (!database) {
320
+ database = (0, expo_sqlite.openDatabaseSync)("silo.db");
321
+ database.execSync(`
322
+ PRAGMA journal_mode = WAL;
323
+
324
+ CREATE TABLE IF NOT EXISTS sources (
325
+ source TEXT NOT NULL,
326
+ id TEXT NOT NULL,
327
+ data TEXT NOT NULL,
328
+ createdAt TEXT NOT NULL,
329
+ updatedAt TEXT NOT NULL,
330
+ version INTEGER NOT NULL DEFAULT 1,
331
+ PRIMARY KEY (source, id)
332
+ );
333
+
334
+ CREATE TABLE IF NOT EXISTS links (
335
+ id TEXT PRIMARY KEY,
336
+ collections TEXT NOT NULL,
337
+ ids TEXT NOT NULL,
338
+ createdAt TEXT NOT NULL
339
+ );
340
+
341
+ CREATE UNIQUE INDEX IF NOT EXISTS links_collections_ids_idx
342
+ ON links(collections, ids);
343
+
344
+ CREATE INDEX IF NOT EXISTS links_collections_idx
345
+ ON links(collections);
346
+ `);
347
+ }
348
+ return database;
349
+ }
350
+ function loadRows(sourceName) {
351
+ return getDatabase().getAllSync(`SELECT id, data FROM sources WHERE source = ?`, [sourceName]).map((row) => createDoc(row.id, JSON.parse(row.data)));
352
+ }
353
+ function insertRows(sourceName, rows) {
354
+ const db = getDatabase();
355
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
356
+ db.withTransactionSync(() => {
357
+ for (const row of rows) {
358
+ const data = extractDocumentData(row);
359
+ db.runSync(`INSERT INTO sources (source, id, data, createdAt, updatedAt, version)
360
+ VALUES (?, ?, ?, ?, ?, 1)`, [
361
+ sourceName,
362
+ row.id,
363
+ JSON.stringify(data),
364
+ timestamp,
365
+ timestamp
366
+ ]);
367
+ }
368
+ });
369
+ return Promise.resolve();
370
+ }
371
+ function updateRows(sourceName, mutations) {
372
+ const db = getDatabase();
373
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
374
+ db.withTransactionSync(() => {
375
+ for (const mutation of mutations) {
376
+ const data = extractDocumentData(mutation.modified);
377
+ mutation.modified = createDoc(String(mutation.key), data);
378
+ db.runSync(`UPDATE sources
379
+ SET data = ?, updatedAt = ?, version = version + 1
380
+ WHERE source = ? AND id = ?`, [
381
+ JSON.stringify(data),
382
+ timestamp,
383
+ sourceName,
384
+ String(mutation.key)
385
+ ]);
386
+ }
387
+ });
388
+ return Promise.resolve();
389
+ }
390
+ function deleteRows(sourceName, mutations) {
391
+ const db = getDatabase();
392
+ db.withTransactionSync(() => {
393
+ for (const mutation of mutations) {
394
+ if (mutation.original) deletedDocs.add(mutation.original);
395
+ deletedDocs.add(mutation.modified);
396
+ db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(mutation.key)]);
397
+ deleteLinksForSourceRow(sourceName, String(mutation.key));
398
+ }
399
+ });
400
+ return Promise.resolve();
401
+ }
402
+ function loadLinkRows() {
403
+ return getDatabase().getAllSync(`SELECT id, collections, ids, createdAt FROM links`);
404
+ }
405
+ function insertLinkRow(row) {
406
+ getDatabase().runSync(`INSERT OR IGNORE INTO links (id, collections, ids, createdAt)
407
+ VALUES (?, ?, ?, ?)`, [
408
+ row.id,
409
+ row.collections,
410
+ row.ids,
411
+ row.createdAt
412
+ ]);
413
+ }
414
+ function deleteLinkRow(id) {
415
+ getDatabase().runSync(`DELETE FROM links WHERE id = ?`, [id]);
416
+ }
417
+ function hasLinkRow(id) {
418
+ return !!getDatabase().getFirstSync(`SELECT id FROM links WHERE id = ?`, [id]);
419
+ }
420
+ function deleteLinksForSourceRow(sourceName, id) {
421
+ const rows = loadLinkRows();
422
+ for (const row of rows) {
423
+ const [firstCollection, secondCollection] = decodePair(row.collections);
424
+ const [firstId, secondId] = decodePair(row.ids);
425
+ if (firstCollection === sourceName && firstId === id || secondCollection === sourceName && secondId === id) deleteLinkRow(row.id);
426
+ }
427
+ }
428
+
429
+ //#endregion
430
+ exports.createID = createID;
431
+ exports.createStore = createStore;
432
+ exports.source = source;
@@ -0,0 +1,61 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { CollectionConfig } from "@tanstack/react-db";
3
+
4
+ //#region src/store/index.d.ts
5
+ type FieldValue = string | number | boolean | null;
6
+ type OptionalKeys<T extends object> = { [K in keyof T]-?: object extends Pick<T, K> ? K : never }[keyof T];
7
+ type RequiredKeys<T extends object> = Exclude<keyof T, OptionalKeys<T>>;
8
+ type InvalidDocumentKeys<T extends object> = { [K in keyof T]-?: Exclude<T[K], undefined> extends FieldValue ? never : K }[keyof T];
9
+ type DocumentData<T extends object> = InvalidDocumentKeys<T> extends never ? T : never;
10
+ type DefaultValue<T> = Exclude<T, undefined> | (() => Exclude<T, undefined>);
11
+ type SourceDefaults<T extends object> = { [K in RequiredKeys<DocumentData<T>>]: DefaultValue<DocumentData<T>[K]> } & { [K in OptionalKeys<DocumentData<T>>]?: DefaultValue<DocumentData<T>[K]> };
12
+ type SourceInput<T extends object> = {
13
+ id: string;
14
+ data: Partial<DocumentData<T>>;
15
+ };
16
+ type Doc<T extends object> = {
17
+ readonly id: string;
18
+ data(): DocumentData<T> | null;
19
+ readonly set: (patch: Partial<DocumentData<T>>) => void;
20
+ } & DocumentData<T>;
21
+ type SourceRow<T extends object> = Doc<T>;
22
+ type SourceCollectionOptions<T extends object> = {
23
+ startSync?: boolean;
24
+ };
25
+ type StoreDefaults<TCollections extends Record<string, object>> = { [Name in keyof TCollections]: SourceDefaults<TCollections[Name]> };
26
+ type LinkRow<TName extends string = string> = {
27
+ readonly id: string;
28
+ readonly collections: string;
29
+ readonly ids: string;
30
+ readonly createdAt: string;
31
+ } & { readonly [Other in TName]: { readonly [Current in TName]: {
32
+ readonly id: string | null;
33
+ } } };
34
+ type LinkUtils<TName extends string = string> = {
35
+ link(collectionA: TName, idA: string, collectionB: TName, idB: string): Promise<void>;
36
+ unlink(collectionA: TName, idA: string, collectionB: TName, idB: string): Promise<void>;
37
+ has(collectionA: TName, idA: string, collectionB: TName, idB: string): boolean;
38
+ };
39
+ type Store<TCollections extends Record<string, object>> = {
40
+ readonly names: ReadonlyArray<keyof TCollections & string>;
41
+ collectionOptions<Name$1 extends keyof TCollections & string>(name: Name$1, options?: SourceCollectionOptions<TCollections[Name$1]>): CollectionConfig<SourceRow<TCollections[Name$1]>, string, StandardSchemaV1<any, SourceRow<TCollections[Name$1]>>> & {
42
+ schema: StandardSchemaV1<any, SourceRow<TCollections[Name$1]>>;
43
+ };
44
+ linkCollectionOptions(options?: SourceCollectionOptions<LinkRow<keyof TCollections & string>>): CollectionConfig<LinkRow<keyof TCollections & string>, string, StandardSchemaV1<any, LinkRow<keyof TCollections & string>>, LinkUtils<keyof TCollections & string>> & {
45
+ schema: StandardSchemaV1<any, LinkRow<keyof TCollections & string>>;
46
+ utils: LinkUtils<keyof TCollections & string>;
47
+ };
48
+ };
49
+ type Source<T extends object> = {
50
+ readonly name: string;
51
+ readonly defaults: SourceDefaults<T>;
52
+ readonly schema: StandardSchemaV1<any, SourceRow<T>>;
53
+ collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<any, SourceRow<T>>> & {
54
+ schema: StandardSchemaV1<any, SourceRow<T>>;
55
+ };
56
+ };
57
+ declare function createID(): string;
58
+ declare function createStore<TCollections extends Record<string, object>>(defaults: StoreDefaults<TCollections>): Store<TCollections>;
59
+ declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
60
+ //#endregion
61
+ export { DefaultValue, Doc, DocumentData, FieldValue, LinkRow, LinkUtils, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, Store, StoreDefaults, createID, createStore, source };
@@ -0,0 +1,61 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { CollectionConfig } from "@tanstack/react-db";
3
+
4
+ //#region src/store/index.d.ts
5
+ type FieldValue = string | number | boolean | null;
6
+ type OptionalKeys<T extends object> = { [K in keyof T]-?: object extends Pick<T, K> ? K : never }[keyof T];
7
+ type RequiredKeys<T extends object> = Exclude<keyof T, OptionalKeys<T>>;
8
+ type InvalidDocumentKeys<T extends object> = { [K in keyof T]-?: Exclude<T[K], undefined> extends FieldValue ? never : K }[keyof T];
9
+ type DocumentData<T extends object> = InvalidDocumentKeys<T> extends never ? T : never;
10
+ type DefaultValue<T> = Exclude<T, undefined> | (() => Exclude<T, undefined>);
11
+ type SourceDefaults<T extends object> = { [K in RequiredKeys<DocumentData<T>>]: DefaultValue<DocumentData<T>[K]> } & { [K in OptionalKeys<DocumentData<T>>]?: DefaultValue<DocumentData<T>[K]> };
12
+ type SourceInput<T extends object> = {
13
+ id: string;
14
+ data: Partial<DocumentData<T>>;
15
+ };
16
+ type Doc<T extends object> = {
17
+ readonly id: string;
18
+ data(): DocumentData<T> | null;
19
+ readonly set: (patch: Partial<DocumentData<T>>) => void;
20
+ } & DocumentData<T>;
21
+ type SourceRow<T extends object> = Doc<T>;
22
+ type SourceCollectionOptions<T extends object> = {
23
+ startSync?: boolean;
24
+ };
25
+ type StoreDefaults<TCollections extends Record<string, object>> = { [Name in keyof TCollections]: SourceDefaults<TCollections[Name]> };
26
+ type LinkRow<TName extends string = string> = {
27
+ readonly id: string;
28
+ readonly collections: string;
29
+ readonly ids: string;
30
+ readonly createdAt: string;
31
+ } & { readonly [Other in TName]: { readonly [Current in TName]: {
32
+ readonly id: string | null;
33
+ } } };
34
+ type LinkUtils<TName extends string = string> = {
35
+ link(collectionA: TName, idA: string, collectionB: TName, idB: string): Promise<void>;
36
+ unlink(collectionA: TName, idA: string, collectionB: TName, idB: string): Promise<void>;
37
+ has(collectionA: TName, idA: string, collectionB: TName, idB: string): boolean;
38
+ };
39
+ type Store<TCollections extends Record<string, object>> = {
40
+ readonly names: ReadonlyArray<keyof TCollections & string>;
41
+ collectionOptions<Name$1 extends keyof TCollections & string>(name: Name$1, options?: SourceCollectionOptions<TCollections[Name$1]>): CollectionConfig<SourceRow<TCollections[Name$1]>, string, StandardSchemaV1<any, SourceRow<TCollections[Name$1]>>> & {
42
+ schema: StandardSchemaV1<any, SourceRow<TCollections[Name$1]>>;
43
+ };
44
+ linkCollectionOptions(options?: SourceCollectionOptions<LinkRow<keyof TCollections & string>>): CollectionConfig<LinkRow<keyof TCollections & string>, string, StandardSchemaV1<any, LinkRow<keyof TCollections & string>>, LinkUtils<keyof TCollections & string>> & {
45
+ schema: StandardSchemaV1<any, LinkRow<keyof TCollections & string>>;
46
+ utils: LinkUtils<keyof TCollections & string>;
47
+ };
48
+ };
49
+ type Source<T extends object> = {
50
+ readonly name: string;
51
+ readonly defaults: SourceDefaults<T>;
52
+ readonly schema: StandardSchemaV1<any, SourceRow<T>>;
53
+ collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<any, SourceRow<T>>> & {
54
+ schema: StandardSchemaV1<any, SourceRow<T>>;
55
+ };
56
+ };
57
+ declare function createID(): string;
58
+ declare function createStore<TCollections extends Record<string, object>>(defaults: StoreDefaults<TCollections>): Store<TCollections>;
59
+ declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
60
+ //#endregion
61
+ export { DefaultValue, Doc, DocumentData, FieldValue, LinkRow, LinkUtils, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, Store, StoreDefaults, createID, createStore, source };