silosdk 0.0.9 → 0.0.10
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 +16 -5
- package/dist/internal/indexed-db.cjs +44 -0
- package/dist/internal/indexed-db.mjs +41 -0
- package/dist/media/shared.cjs +99 -0
- package/dist/media/shared.d.cts +26 -0
- package/dist/media/shared.d.mts +26 -0
- package/dist/media/shared.mjs +93 -0
- package/dist/media/storage.native.cjs +102 -0
- package/dist/media/storage.native.mjs +101 -0
- package/dist/media/storage.web.cjs +79 -0
- package/dist/media/storage.web.mjs +78 -0
- package/dist/media.cjs +5 -163
- package/dist/media.d.cts +7 -27
- package/dist/media.d.mts +7 -27
- package/dist/media.mjs +4 -161
- package/dist/media.web.cjs +9 -0
- package/dist/media.web.d.cts +12 -0
- package/dist/media.web.d.mts +12 -0
- package/dist/media.web.mjs +8 -0
- package/dist/settings/shared.cjs +63 -0
- package/dist/settings/shared.d.cts +19 -0
- package/dist/settings/shared.d.mts +19 -0
- package/dist/settings/shared.mjs +59 -0
- package/dist/settings/storage.native.cjs +24 -0
- package/dist/settings/storage.native.mjs +22 -0
- package/dist/settings/storage.web.cjs +34 -0
- package/dist/settings/storage.web.mjs +34 -0
- package/dist/settings.cjs +4 -66
- package/dist/settings.d.cts +4 -18
- package/dist/settings.d.mts +4 -18
- package/dist/settings.mjs +4 -64
- package/dist/settings.web.cjs +8 -0
- package/dist/settings.web.d.cts +6 -0
- package/dist/settings.web.d.mts +6 -0
- package/dist/settings.web.mjs +8 -0
- package/dist/store/shared.cjs +338 -0
- package/dist/store/shared.d.cts +58 -0
- package/dist/store/shared.d.mts +58 -0
- package/dist/store/shared.mjs +333 -0
- package/dist/store/storage.native.cjs +133 -0
- package/dist/store/storage.native.mjs +132 -0
- package/dist/store/storage.web.cjs +142 -0
- package/dist/store/storage.web.mjs +142 -0
- package/dist/store.cjs +4 -421
- package/dist/store.d.cts +4 -58
- package/dist/store.d.mts +4 -58
- package/dist/store.mjs +4 -421
- package/dist/store.web.cjs +15 -0
- package/dist/store.web.d.cts +7 -0
- package/dist/store.web.d.mts +7 -0
- package/dist/store.web.mjs +12 -0
- package/package.json +31 -1
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/store/shared.ts
|
|
3
|
+
const sourceNames = /* @__PURE__ */ new Set();
|
|
4
|
+
const sourceNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
5
|
+
const reservedDocumentFields = new Set([
|
|
6
|
+
"id",
|
|
7
|
+
"data",
|
|
8
|
+
"set"
|
|
9
|
+
]);
|
|
10
|
+
const deletedDocs = /* @__PURE__ */ new WeakSet();
|
|
11
|
+
function createStoreApi(storage) {
|
|
12
|
+
function createStore(defaults) {
|
|
13
|
+
const names = Object.keys(defaults);
|
|
14
|
+
const nameSet = new Set(names);
|
|
15
|
+
for (const name of names) {
|
|
16
|
+
assertSourceName(name);
|
|
17
|
+
assertFlatDefaults(name, defaults[name]);
|
|
18
|
+
}
|
|
19
|
+
if (new Set(names).size !== names.length) throw new Error("Store collection names must be unique.");
|
|
20
|
+
return {
|
|
21
|
+
names,
|
|
22
|
+
collectionOptions(name, options = {}) {
|
|
23
|
+
assertStoreCollectionName(nameSet, name);
|
|
24
|
+
return createSourceCollectionOptions(storage, name, defaults[name], options);
|
|
25
|
+
},
|
|
26
|
+
linkCollectionOptions(options = {}) {
|
|
27
|
+
return createLinkCollectionOptions(storage, names, nameSet, options);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
function source(name, defaults) {
|
|
32
|
+
assertSourceName(name);
|
|
33
|
+
assertFlatDefaults(name, defaults);
|
|
34
|
+
if (sourceNames.has(name)) throw new Error(`Source "${name}" is already registered. Define each source once and import the existing source instead.`);
|
|
35
|
+
sourceNames.add(name);
|
|
36
|
+
return {
|
|
37
|
+
name,
|
|
38
|
+
defaults,
|
|
39
|
+
schema: createSourceSchema(name, defaults),
|
|
40
|
+
collectionOptions(options = {}) {
|
|
41
|
+
return createSourceCollectionOptions(storage, name, defaults, options);
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
createStore,
|
|
47
|
+
source
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function createSourceCollectionOptions(storage, 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
|
+
Promise.resolve(storage.loadRows(name)).then((rows) => {
|
|
58
|
+
begin();
|
|
59
|
+
truncate();
|
|
60
|
+
for (const row of rows) write({
|
|
61
|
+
type: "insert",
|
|
62
|
+
value: row
|
|
63
|
+
});
|
|
64
|
+
commit();
|
|
65
|
+
markReady();
|
|
66
|
+
}).catch((error) => {
|
|
67
|
+
reportSyncError(name, error);
|
|
68
|
+
begin();
|
|
69
|
+
truncate();
|
|
70
|
+
commit();
|
|
71
|
+
markReady();
|
|
72
|
+
});
|
|
73
|
+
} },
|
|
74
|
+
onInsert: async ({ transaction }) => {
|
|
75
|
+
await storage.insertRows(name, transaction.mutations.map((mutation) => mutation.modified));
|
|
76
|
+
},
|
|
77
|
+
onUpdate: async ({ transaction }) => {
|
|
78
|
+
await storage.updateRows(name, transaction.mutations);
|
|
79
|
+
},
|
|
80
|
+
onDelete: async ({ transaction }) => {
|
|
81
|
+
await storage.deleteRows(name, transaction.mutations);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function createLinkCollectionOptions(storage, names, nameSet, options = {}) {
|
|
86
|
+
const schema = createLinkSchema();
|
|
87
|
+
let syncBegin;
|
|
88
|
+
let syncWrite;
|
|
89
|
+
let syncCommit;
|
|
90
|
+
const writeSyncedLink = (type, row) => {
|
|
91
|
+
if (!syncBegin || !syncWrite || !syncCommit) return;
|
|
92
|
+
syncBegin();
|
|
93
|
+
syncWrite({
|
|
94
|
+
type,
|
|
95
|
+
value: row
|
|
96
|
+
});
|
|
97
|
+
syncCommit();
|
|
98
|
+
};
|
|
99
|
+
return {
|
|
100
|
+
id: "silo-links",
|
|
101
|
+
getKey: (item) => item.id,
|
|
102
|
+
schema,
|
|
103
|
+
startSync: options.startSync,
|
|
104
|
+
sync: { sync({ begin, write, commit, markReady, truncate }) {
|
|
105
|
+
syncBegin = begin;
|
|
106
|
+
syncWrite = write;
|
|
107
|
+
syncCommit = commit;
|
|
108
|
+
Promise.resolve(storage.loadLinkRows()).then((storedRows) => {
|
|
109
|
+
const rows = storedRows.map((row) => createLinkRow(names, row));
|
|
110
|
+
begin();
|
|
111
|
+
truncate();
|
|
112
|
+
for (const row of rows) write({
|
|
113
|
+
type: "insert",
|
|
114
|
+
value: row
|
|
115
|
+
});
|
|
116
|
+
commit();
|
|
117
|
+
markReady();
|
|
118
|
+
}).catch((error) => {
|
|
119
|
+
reportSyncError("silo-links", error);
|
|
120
|
+
begin();
|
|
121
|
+
truncate();
|
|
122
|
+
commit();
|
|
123
|
+
markReady();
|
|
124
|
+
});
|
|
125
|
+
} },
|
|
126
|
+
onInsert: async ({ transaction }) => {
|
|
127
|
+
for (const mutation of transaction.mutations) await storage.insertLinkRow(extractStoredLinkRow(mutation.modified));
|
|
128
|
+
},
|
|
129
|
+
onDelete: async ({ transaction }) => {
|
|
130
|
+
for (const mutation of transaction.mutations) await storage.deleteLinkRow(String(mutation.key));
|
|
131
|
+
},
|
|
132
|
+
utils: {
|
|
133
|
+
async link(collectionA, idA, collectionB, idB) {
|
|
134
|
+
const row = createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB);
|
|
135
|
+
if (!await storage.insertLinkRow(row)) writeSyncedLink("insert", createLinkRow(names, row));
|
|
136
|
+
},
|
|
137
|
+
async unlink(collectionA, idA, collectionB, idB) {
|
|
138
|
+
const row = createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB);
|
|
139
|
+
if (await storage.deleteLinkRow(row.id)) writeSyncedLink("delete", createLinkRow(names, row));
|
|
140
|
+
},
|
|
141
|
+
has(collectionA, idA, collectionB, idB) {
|
|
142
|
+
const row = createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB);
|
|
143
|
+
return storage.hasLinkRow(row.id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
function assertSourceName(name) {
|
|
149
|
+
if (!sourceNamePattern.test(name)) throw new Error(`Source names must start with a letter and contain only letters, numbers, underscores, or hyphens. Received "${name}".`);
|
|
150
|
+
}
|
|
151
|
+
function assertStoreCollectionName(nameSet, name) {
|
|
152
|
+
if (!nameSet.has(name)) throw new Error(`Store collection "${name}" is not registered.`);
|
|
153
|
+
}
|
|
154
|
+
function assertLinkCollectionName(nameSet, name) {
|
|
155
|
+
if (!nameSet.has(name)) throw new Error(`Cannot link unknown collection "${name}".`);
|
|
156
|
+
}
|
|
157
|
+
function createStoredLinkRow(nameSet, collectionA, idA, collectionB, idB) {
|
|
158
|
+
assertLinkCollectionName(nameSet, collectionA);
|
|
159
|
+
assertLinkCollectionName(nameSet, collectionB);
|
|
160
|
+
if (collectionA === collectionB) throw new Error(`Links between the same collection are not supported. Received "${collectionA}".`);
|
|
161
|
+
const [firstCollection, firstId, secondCollection, secondId] = collectionA < collectionB ? [
|
|
162
|
+
collectionA,
|
|
163
|
+
idA,
|
|
164
|
+
collectionB,
|
|
165
|
+
idB
|
|
166
|
+
] : [
|
|
167
|
+
collectionB,
|
|
168
|
+
idB,
|
|
169
|
+
collectionA,
|
|
170
|
+
idA
|
|
171
|
+
];
|
|
172
|
+
const collections = encodePair(firstCollection, secondCollection);
|
|
173
|
+
const ids = encodePair(firstId, secondId);
|
|
174
|
+
return {
|
|
175
|
+
id: encodePair(collections, ids),
|
|
176
|
+
collections,
|
|
177
|
+
ids,
|
|
178
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
function createLinkRow(names, row) {
|
|
182
|
+
const [firstCollection, secondCollection] = decodePair(row.collections);
|
|
183
|
+
const [firstId, secondId] = decodePair(row.ids);
|
|
184
|
+
const result = {
|
|
185
|
+
id: row.id,
|
|
186
|
+
collections: row.collections,
|
|
187
|
+
ids: row.ids,
|
|
188
|
+
createdAt: row.createdAt
|
|
189
|
+
};
|
|
190
|
+
for (const other of names) {
|
|
191
|
+
const byCurrent = {};
|
|
192
|
+
for (const current of names) {
|
|
193
|
+
let id = null;
|
|
194
|
+
if (other !== current) {
|
|
195
|
+
if (current === firstCollection && other === secondCollection) id = firstId;
|
|
196
|
+
else if (current === secondCollection && other === firstCollection) id = secondId;
|
|
197
|
+
}
|
|
198
|
+
byCurrent[current] = { id };
|
|
199
|
+
}
|
|
200
|
+
result[other] = byCurrent;
|
|
201
|
+
}
|
|
202
|
+
return result;
|
|
203
|
+
}
|
|
204
|
+
function extractStoredLinkRow(row) {
|
|
205
|
+
if (!isRecord(row)) throw new Error("Expected a link row object.");
|
|
206
|
+
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.");
|
|
207
|
+
return {
|
|
208
|
+
id: row.id,
|
|
209
|
+
collections: row.collections,
|
|
210
|
+
ids: row.ids,
|
|
211
|
+
createdAt: row.createdAt
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function encodePair(first, second) {
|
|
215
|
+
return `${encodePart(first)}:${encodePart(second)}`;
|
|
216
|
+
}
|
|
217
|
+
function decodePair(value) {
|
|
218
|
+
const parts = value.split(":");
|
|
219
|
+
if (parts.length !== 2) throw new Error(`Invalid encoded link pair "${value}".`);
|
|
220
|
+
return [decodePart(parts[0]), decodePart(parts[1])];
|
|
221
|
+
}
|
|
222
|
+
function encodePart(value) {
|
|
223
|
+
return encodeURIComponent(value);
|
|
224
|
+
}
|
|
225
|
+
function decodePart(value) {
|
|
226
|
+
return decodeURIComponent(value);
|
|
227
|
+
}
|
|
228
|
+
function assertFlatDefaults(sourceName, defaults) {
|
|
229
|
+
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
230
|
+
if (reservedDocumentFields.has(field)) throw new Error(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`);
|
|
231
|
+
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.`);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function createSourceSchema(sourceName, defaults) {
|
|
235
|
+
return { "~standard": {
|
|
236
|
+
version: 1,
|
|
237
|
+
vendor: "silo",
|
|
238
|
+
validate(value) {
|
|
239
|
+
if (!isRecord(value)) return failure("Expected a flat source row object.");
|
|
240
|
+
if (typeof value.id !== "string") return failure("Expected source document field \"id\" to be a string.", ["id"]);
|
|
241
|
+
const inputData = isRecord(value.data) ? value.data : extractDocumentData(value);
|
|
242
|
+
const data = {};
|
|
243
|
+
for (const [field, fieldValue] of Object.entries(inputData)) {
|
|
244
|
+
if (reservedDocumentFields.has(field)) return failure(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`, ["data", field]);
|
|
245
|
+
if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, ["data", field]);
|
|
246
|
+
data[field] = fieldValue;
|
|
247
|
+
}
|
|
248
|
+
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
249
|
+
if (field in data) continue;
|
|
250
|
+
data[field] = resolveDefault(defaultValue);
|
|
251
|
+
}
|
|
252
|
+
return { value: createDoc(value.id, data) };
|
|
253
|
+
}
|
|
254
|
+
} };
|
|
255
|
+
}
|
|
256
|
+
function createLinkSchema() {
|
|
257
|
+
return { "~standard": {
|
|
258
|
+
version: 1,
|
|
259
|
+
vendor: "silo",
|
|
260
|
+
validate(value) {
|
|
261
|
+
try {
|
|
262
|
+
extractStoredLinkRow(value);
|
|
263
|
+
return { value };
|
|
264
|
+
} catch (error) {
|
|
265
|
+
return failure(error instanceof Error ? error.message : "Expected a link row.");
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} };
|
|
269
|
+
}
|
|
270
|
+
function createDoc(id, data) {
|
|
271
|
+
const doc = {};
|
|
272
|
+
Object.defineProperty(doc, "id", {
|
|
273
|
+
value: id,
|
|
274
|
+
enumerable: true,
|
|
275
|
+
writable: false,
|
|
276
|
+
configurable: false
|
|
277
|
+
});
|
|
278
|
+
for (const [field, value] of Object.entries(data)) Object.defineProperty(doc, field, {
|
|
279
|
+
value,
|
|
280
|
+
enumerable: true,
|
|
281
|
+
writable: true,
|
|
282
|
+
configurable: true
|
|
283
|
+
});
|
|
284
|
+
Object.defineProperty(doc, "data", {
|
|
285
|
+
value() {
|
|
286
|
+
return deletedDocs.has(this) ? null : extractDocumentData(this);
|
|
287
|
+
},
|
|
288
|
+
enumerable: true,
|
|
289
|
+
writable: false,
|
|
290
|
+
configurable: false
|
|
291
|
+
});
|
|
292
|
+
Object.defineProperty(doc, "set", {
|
|
293
|
+
get() {
|
|
294
|
+
return function setDocumentData(patch) {
|
|
295
|
+
Object.assign(this, patch);
|
|
296
|
+
};
|
|
297
|
+
},
|
|
298
|
+
enumerable: true,
|
|
299
|
+
configurable: false
|
|
300
|
+
});
|
|
301
|
+
return doc;
|
|
302
|
+
}
|
|
303
|
+
function extractDocumentData(value) {
|
|
304
|
+
if (!isRecord(value)) return {};
|
|
305
|
+
const data = {};
|
|
306
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
307
|
+
if (reservedDocumentFields.has(field) || field.startsWith("$") || typeof fieldValue === "function") continue;
|
|
308
|
+
if (isFieldValue(fieldValue)) data[field] = fieldValue;
|
|
309
|
+
}
|
|
310
|
+
return data;
|
|
311
|
+
}
|
|
312
|
+
function resolveDefault(value) {
|
|
313
|
+
const resolved = typeof value === "function" ? value() : value;
|
|
314
|
+
if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
|
|
315
|
+
return resolved;
|
|
316
|
+
}
|
|
317
|
+
function isRecord(value) {
|
|
318
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
319
|
+
}
|
|
320
|
+
function isFieldValue(value) {
|
|
321
|
+
return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
|
|
322
|
+
}
|
|
323
|
+
function failure(message, path) {
|
|
324
|
+
return { issues: [{
|
|
325
|
+
message,
|
|
326
|
+
path
|
|
327
|
+
}] };
|
|
328
|
+
}
|
|
329
|
+
function reportSyncError(collectionName, error) {
|
|
330
|
+
console.error(`Failed to sync Silo collection "${collectionName}".`, error);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
//#endregion
|
|
334
|
+
exports.createDoc = createDoc;
|
|
335
|
+
exports.createStoreApi = createStoreApi;
|
|
336
|
+
exports.decodePair = decodePair;
|
|
337
|
+
exports.deletedDocs = deletedDocs;
|
|
338
|
+
exports.extractDocumentData = extractDocumentData;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { CollectionConfig } from "@tanstack/react-db";
|
|
3
|
+
|
|
4
|
+
//#region src/store/shared.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
|
+
//#endregion
|
|
58
|
+
export { DefaultValue, Doc, DocumentData, FieldValue, LinkRow, LinkUtils, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, Store, StoreDefaults };
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
|
+
import { CollectionConfig } from "@tanstack/react-db";
|
|
3
|
+
|
|
4
|
+
//#region src/store/shared.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
|
+
//#endregion
|
|
58
|
+
export { DefaultValue, Doc, DocumentData, FieldValue, LinkRow, LinkUtils, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, Store, StoreDefaults };
|