silosdk 0.0.4 → 0.0.5
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 +26 -8
- package/dist/media.cjs +1 -1
- package/dist/media.mjs +1 -1
- package/dist/source.cjs +79 -20
- package/dist/source.d.cts +14 -7
- package/dist/source.d.mts +14 -7
- package/dist/source.mjs +79 -21
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ collections.
|
|
|
31
31
|
|
|
32
32
|
```ts
|
|
33
33
|
import { createCollection } from '@tanstack/react-db'
|
|
34
|
-
import { source } from 'silosdk/source'
|
|
34
|
+
import { createID, source } from 'silosdk/source'
|
|
35
35
|
|
|
36
36
|
type Post = {
|
|
37
37
|
title: string
|
|
@@ -55,19 +55,37 @@ Query and mutate with TanStack DB directly:
|
|
|
55
55
|
```tsx
|
|
56
56
|
import { eq, useLiveQuery } from '@tanstack/react-db'
|
|
57
57
|
|
|
58
|
-
const { data = [] } = useLiveQuery((q) =>
|
|
58
|
+
const { data: docs = [] } = useLiveQuery((q) =>
|
|
59
59
|
q.from({ posts }).where(({ posts }) => eq(posts.published, true)),
|
|
60
60
|
)
|
|
61
61
|
|
|
62
|
+
const doc = docs[0]
|
|
63
|
+
const post = doc?.data()
|
|
64
|
+
|
|
65
|
+
doc?.id
|
|
66
|
+
post?.title
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
const id = createID()
|
|
71
|
+
|
|
62
72
|
const tx = posts.insert({
|
|
63
|
-
id
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
73
|
+
id,
|
|
74
|
+
data: {
|
|
75
|
+
title: 'Hello',
|
|
76
|
+
body: '',
|
|
77
|
+
published: false,
|
|
78
|
+
coverImage: null,
|
|
79
|
+
},
|
|
68
80
|
})
|
|
69
81
|
|
|
70
82
|
await tx.isPersisted.promise
|
|
83
|
+
|
|
84
|
+
await posts.update(id, (doc) => {
|
|
85
|
+
doc.set({ published: true })
|
|
86
|
+
}).isPersisted.promise
|
|
87
|
+
|
|
88
|
+
await posts.delete(id).isPersisted.promise
|
|
71
89
|
```
|
|
72
90
|
|
|
73
91
|
Source rows are intentionally flat. Field values must be:
|
|
@@ -134,7 +152,7 @@ Store the returned `media://...` ref in source rows:
|
|
|
134
152
|
|
|
135
153
|
```ts
|
|
136
154
|
posts.update(postId, (draft) => {
|
|
137
|
-
draft.coverImage
|
|
155
|
+
draft.set({ coverImage })
|
|
138
156
|
})
|
|
139
157
|
```
|
|
140
158
|
|
package/dist/media.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
2
|
let expo_sqlite = require("expo-sqlite");
|
|
3
|
+
let nanoid = require("nanoid");
|
|
3
4
|
let __tanstack_react_query = require("@tanstack/react-query");
|
|
4
5
|
let expo_file_system = require("expo-file-system");
|
|
5
|
-
let nanoid = require("nanoid");
|
|
6
6
|
|
|
7
7
|
//#region src/media/index.ts
|
|
8
8
|
const media = {
|
package/dist/media.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { openDatabaseSync } from "expo-sqlite";
|
|
2
|
+
import { nanoid } from "nanoid";
|
|
2
3
|
import { mutationOptions, queryOptions } from "@tanstack/react-query";
|
|
3
4
|
import { Directory, File, Paths } from "expo-file-system";
|
|
4
|
-
import { nanoid } from "nanoid";
|
|
5
5
|
|
|
6
6
|
//#region src/media/index.ts
|
|
7
7
|
const media = {
|
package/dist/source.cjs
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
2
2
|
let expo_sqlite = require("expo-sqlite");
|
|
3
|
+
let nanoid = require("nanoid");
|
|
3
4
|
|
|
4
5
|
//#region src/source/index.ts
|
|
5
6
|
const sourceNames = /* @__PURE__ */ new Set();
|
|
6
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, nanoid.nanoid)();
|
|
16
|
+
}
|
|
7
17
|
function source(name, defaults) {
|
|
8
18
|
assertSourceName(name);
|
|
9
19
|
assertFlatDefaults(name, defaults);
|
|
@@ -38,7 +48,7 @@ function source(name, defaults) {
|
|
|
38
48
|
await updateRows(name, transaction.mutations);
|
|
39
49
|
},
|
|
40
50
|
onDelete: async ({ transaction }) => {
|
|
41
|
-
await deleteRows(name, transaction.mutations
|
|
51
|
+
await deleteRows(name, transaction.mutations);
|
|
42
52
|
}
|
|
43
53
|
};
|
|
44
54
|
}
|
|
@@ -48,7 +58,10 @@ function assertSourceName(name) {
|
|
|
48
58
|
if (!sourceNamePattern.test(name)) throw new Error(`Source names must start with a letter and contain only letters, numbers, underscores, or hyphens. Received "${name}".`);
|
|
49
59
|
}
|
|
50
60
|
function assertFlatDefaults(sourceName, defaults) {
|
|
51
|
-
for (const [field, defaultValue] of Object.entries(defaults))
|
|
61
|
+
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
62
|
+
if (reservedDocumentFields.has(field)) throw new Error(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`);
|
|
63
|
+
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.`);
|
|
64
|
+
}
|
|
52
65
|
}
|
|
53
66
|
function createSourceSchema(sourceName, defaults) {
|
|
54
67
|
return { "~standard": {
|
|
@@ -56,21 +69,64 @@ function createSourceSchema(sourceName, defaults) {
|
|
|
56
69
|
vendor: "silo",
|
|
57
70
|
validate(value) {
|
|
58
71
|
if (!isRecord(value)) return failure("Expected a flat source row object.");
|
|
59
|
-
if (typeof value.id !== "string") return failure("Expected source
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
|
|
72
|
+
if (typeof value.id !== "string") return failure("Expected source document field \"id\" to be a string.", ["id"]);
|
|
73
|
+
const inputData = isRecord(value.data) ? value.data : extractDocumentData(value);
|
|
74
|
+
const data = {};
|
|
75
|
+
for (const [field, fieldValue] of Object.entries(inputData)) {
|
|
76
|
+
if (reservedDocumentFields.has(field)) return failure(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`, ["data", field]);
|
|
77
|
+
if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, ["data", field]);
|
|
78
|
+
data[field] = fieldValue;
|
|
65
79
|
}
|
|
66
80
|
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
67
|
-
if (field in
|
|
68
|
-
|
|
81
|
+
if (field in data) continue;
|
|
82
|
+
data[field] = resolveDefault(defaultValue);
|
|
69
83
|
}
|
|
70
|
-
return { value:
|
|
84
|
+
return { value: createDoc(value.id, data) };
|
|
71
85
|
}
|
|
72
86
|
} };
|
|
73
87
|
}
|
|
88
|
+
function createDoc(id, data) {
|
|
89
|
+
const doc = {};
|
|
90
|
+
Object.defineProperty(doc, "id", {
|
|
91
|
+
value: id,
|
|
92
|
+
enumerable: true,
|
|
93
|
+
writable: false,
|
|
94
|
+
configurable: false
|
|
95
|
+
});
|
|
96
|
+
for (const [field, value] of Object.entries(data)) Object.defineProperty(doc, field, {
|
|
97
|
+
value,
|
|
98
|
+
enumerable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
configurable: true
|
|
101
|
+
});
|
|
102
|
+
Object.defineProperty(doc, "data", {
|
|
103
|
+
value() {
|
|
104
|
+
return deletedDocs.has(this) ? null : extractDocumentData(this);
|
|
105
|
+
},
|
|
106
|
+
enumerable: true,
|
|
107
|
+
writable: false,
|
|
108
|
+
configurable: false
|
|
109
|
+
});
|
|
110
|
+
Object.defineProperty(doc, "set", {
|
|
111
|
+
get() {
|
|
112
|
+
return function setDocumentData(patch) {
|
|
113
|
+
Object.assign(this, patch);
|
|
114
|
+
};
|
|
115
|
+
},
|
|
116
|
+
enumerable: true,
|
|
117
|
+
configurable: false
|
|
118
|
+
});
|
|
119
|
+
return doc;
|
|
120
|
+
}
|
|
121
|
+
function extractDocumentData(value) {
|
|
122
|
+
if (!isRecord(value)) return {};
|
|
123
|
+
const data = {};
|
|
124
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
125
|
+
if (reservedDocumentFields.has(field) || field.startsWith("$") || typeof fieldValue === "function") continue;
|
|
126
|
+
if (isFieldValue(fieldValue)) data[field] = fieldValue;
|
|
127
|
+
}
|
|
128
|
+
return data;
|
|
129
|
+
}
|
|
74
130
|
function resolveDefault(value) {
|
|
75
131
|
const resolved = typeof value === "function" ? value() : value;
|
|
76
132
|
if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
|
|
@@ -109,21 +165,18 @@ function getDatabase() {
|
|
|
109
165
|
return database;
|
|
110
166
|
}
|
|
111
167
|
function loadRows(sourceName) {
|
|
112
|
-
return getDatabase().getAllSync(`SELECT id, data FROM sources WHERE source = ?`, [sourceName]).map((row) => (
|
|
113
|
-
id: row.id,
|
|
114
|
-
...JSON.parse(row.data)
|
|
115
|
-
}));
|
|
168
|
+
return getDatabase().getAllSync(`SELECT id, data FROM sources WHERE source = ?`, [sourceName]).map((row) => createDoc(row.id, JSON.parse(row.data)));
|
|
116
169
|
}
|
|
117
170
|
function insertRows(sourceName, rows) {
|
|
118
171
|
const db = getDatabase();
|
|
119
172
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
120
173
|
db.withTransactionSync(() => {
|
|
121
174
|
for (const row of rows) {
|
|
122
|
-
const
|
|
175
|
+
const data = extractDocumentData(row);
|
|
123
176
|
db.runSync(`INSERT INTO sources (source, id, data, createdAt, updatedAt, version)
|
|
124
177
|
VALUES (?, ?, ?, ?, ?, 1)`, [
|
|
125
178
|
sourceName,
|
|
126
|
-
id,
|
|
179
|
+
row.id,
|
|
127
180
|
JSON.stringify(data),
|
|
128
181
|
timestamp,
|
|
129
182
|
timestamp
|
|
@@ -137,7 +190,8 @@ function updateRows(sourceName, mutations) {
|
|
|
137
190
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
138
191
|
db.withTransactionSync(() => {
|
|
139
192
|
for (const mutation of mutations) {
|
|
140
|
-
const
|
|
193
|
+
const data = extractDocumentData(mutation.modified);
|
|
194
|
+
mutation.modified = createDoc(String(mutation.key), data);
|
|
141
195
|
db.runSync(`UPDATE sources
|
|
142
196
|
SET data = ?, updatedAt = ?, version = version + 1
|
|
143
197
|
WHERE source = ? AND id = ?`, [
|
|
@@ -150,13 +204,18 @@ function updateRows(sourceName, mutations) {
|
|
|
150
204
|
});
|
|
151
205
|
return Promise.resolve();
|
|
152
206
|
}
|
|
153
|
-
function deleteRows(sourceName,
|
|
207
|
+
function deleteRows(sourceName, mutations) {
|
|
154
208
|
const db = getDatabase();
|
|
155
209
|
db.withTransactionSync(() => {
|
|
156
|
-
for (const
|
|
210
|
+
for (const mutation of mutations) {
|
|
211
|
+
if (mutation.original) deletedDocs.add(mutation.original);
|
|
212
|
+
deletedDocs.add(mutation.modified);
|
|
213
|
+
db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(mutation.key)]);
|
|
214
|
+
}
|
|
157
215
|
});
|
|
158
216
|
return Promise.resolve();
|
|
159
217
|
}
|
|
160
218
|
|
|
161
219
|
//#endregion
|
|
220
|
+
exports.createID = createID;
|
|
162
221
|
exports.source = source;
|
package/dist/source.d.cts
CHANGED
|
@@ -9,21 +9,28 @@ type InvalidDocumentKeys<T extends object> = { [K in keyof T]-?: Exclude<T[K], u
|
|
|
9
9
|
type DocumentData<T extends object> = InvalidDocumentKeys<T> extends never ? T : never;
|
|
10
10
|
type DefaultValue<T> = Exclude<T, undefined> | (() => Exclude<T, undefined>);
|
|
11
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 SourceRow<T extends object> = {
|
|
13
|
-
id: string;
|
|
14
|
-
} & DocumentData<T>;
|
|
15
12
|
type SourceInput<T extends object> = {
|
|
16
13
|
id: string;
|
|
17
|
-
|
|
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>;
|
|
18
22
|
type SourceCollectionOptions<T extends object> = {
|
|
19
23
|
startSync?: boolean;
|
|
20
24
|
};
|
|
21
25
|
type Source<T extends object> = {
|
|
22
26
|
readonly name: string;
|
|
23
27
|
readonly defaults: SourceDefaults<T>;
|
|
24
|
-
readonly schema: StandardSchemaV1<
|
|
25
|
-
collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<
|
|
28
|
+
readonly schema: StandardSchemaV1<any, SourceRow<T>>;
|
|
29
|
+
collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<any, SourceRow<T>>> & {
|
|
30
|
+
schema: StandardSchemaV1<any, SourceRow<T>>;
|
|
31
|
+
};
|
|
26
32
|
};
|
|
33
|
+
declare function createID(): string;
|
|
27
34
|
declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
|
|
28
35
|
//#endregion
|
|
29
|
-
export { DefaultValue, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, source };
|
|
36
|
+
export { DefaultValue, Doc, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, createID, source };
|
package/dist/source.d.mts
CHANGED
|
@@ -9,21 +9,28 @@ type InvalidDocumentKeys<T extends object> = { [K in keyof T]-?: Exclude<T[K], u
|
|
|
9
9
|
type DocumentData<T extends object> = InvalidDocumentKeys<T> extends never ? T : never;
|
|
10
10
|
type DefaultValue<T> = Exclude<T, undefined> | (() => Exclude<T, undefined>);
|
|
11
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 SourceRow<T extends object> = {
|
|
13
|
-
id: string;
|
|
14
|
-
} & DocumentData<T>;
|
|
15
12
|
type SourceInput<T extends object> = {
|
|
16
13
|
id: string;
|
|
17
|
-
|
|
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>;
|
|
18
22
|
type SourceCollectionOptions<T extends object> = {
|
|
19
23
|
startSync?: boolean;
|
|
20
24
|
};
|
|
21
25
|
type Source<T extends object> = {
|
|
22
26
|
readonly name: string;
|
|
23
27
|
readonly defaults: SourceDefaults<T>;
|
|
24
|
-
readonly schema: StandardSchemaV1<
|
|
25
|
-
collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<
|
|
28
|
+
readonly schema: StandardSchemaV1<any, SourceRow<T>>;
|
|
29
|
+
collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<any, SourceRow<T>>> & {
|
|
30
|
+
schema: StandardSchemaV1<any, SourceRow<T>>;
|
|
31
|
+
};
|
|
26
32
|
};
|
|
33
|
+
declare function createID(): string;
|
|
27
34
|
declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
|
|
28
35
|
//#endregion
|
|
29
|
-
export { DefaultValue, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, source };
|
|
36
|
+
export { DefaultValue, Doc, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, createID, source };
|
package/dist/source.mjs
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import { openDatabaseSync } from "expo-sqlite";
|
|
2
|
+
import { nanoid } from "nanoid";
|
|
2
3
|
|
|
3
4
|
//#region src/source/index.ts
|
|
4
5
|
const sourceNames = /* @__PURE__ */ new Set();
|
|
5
6
|
const sourceNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
|
|
7
|
+
const reservedDocumentFields = new Set([
|
|
8
|
+
"id",
|
|
9
|
+
"data",
|
|
10
|
+
"set"
|
|
11
|
+
]);
|
|
12
|
+
const deletedDocs = /* @__PURE__ */ new WeakSet();
|
|
13
|
+
function createID() {
|
|
14
|
+
return nanoid();
|
|
15
|
+
}
|
|
6
16
|
function source(name, defaults) {
|
|
7
17
|
assertSourceName(name);
|
|
8
18
|
assertFlatDefaults(name, defaults);
|
|
@@ -37,7 +47,7 @@ function source(name, defaults) {
|
|
|
37
47
|
await updateRows(name, transaction.mutations);
|
|
38
48
|
},
|
|
39
49
|
onDelete: async ({ transaction }) => {
|
|
40
|
-
await deleteRows(name, transaction.mutations
|
|
50
|
+
await deleteRows(name, transaction.mutations);
|
|
41
51
|
}
|
|
42
52
|
};
|
|
43
53
|
}
|
|
@@ -47,7 +57,10 @@ function assertSourceName(name) {
|
|
|
47
57
|
if (!sourceNamePattern.test(name)) throw new Error(`Source names must start with a letter and contain only letters, numbers, underscores, or hyphens. Received "${name}".`);
|
|
48
58
|
}
|
|
49
59
|
function assertFlatDefaults(sourceName, defaults) {
|
|
50
|
-
for (const [field, defaultValue] of Object.entries(defaults))
|
|
60
|
+
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
61
|
+
if (reservedDocumentFields.has(field)) throw new Error(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`);
|
|
62
|
+
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.`);
|
|
63
|
+
}
|
|
51
64
|
}
|
|
52
65
|
function createSourceSchema(sourceName, defaults) {
|
|
53
66
|
return { "~standard": {
|
|
@@ -55,21 +68,64 @@ function createSourceSchema(sourceName, defaults) {
|
|
|
55
68
|
vendor: "silo",
|
|
56
69
|
validate(value) {
|
|
57
70
|
if (!isRecord(value)) return failure("Expected a flat source row object.");
|
|
58
|
-
if (typeof value.id !== "string") return failure("Expected source
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
71
|
+
if (typeof value.id !== "string") return failure("Expected source document field \"id\" to be a string.", ["id"]);
|
|
72
|
+
const inputData = isRecord(value.data) ? value.data : extractDocumentData(value);
|
|
73
|
+
const data = {};
|
|
74
|
+
for (const [field, fieldValue] of Object.entries(inputData)) {
|
|
75
|
+
if (reservedDocumentFields.has(field)) return failure(`Source "${sourceName}" field "${field}" is reserved by Silo documents.`, ["data", field]);
|
|
76
|
+
if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, ["data", field]);
|
|
77
|
+
data[field] = fieldValue;
|
|
64
78
|
}
|
|
65
79
|
for (const [field, defaultValue] of Object.entries(defaults)) {
|
|
66
|
-
if (field in
|
|
67
|
-
|
|
80
|
+
if (field in data) continue;
|
|
81
|
+
data[field] = resolveDefault(defaultValue);
|
|
68
82
|
}
|
|
69
|
-
return { value:
|
|
83
|
+
return { value: createDoc(value.id, data) };
|
|
70
84
|
}
|
|
71
85
|
} };
|
|
72
86
|
}
|
|
87
|
+
function createDoc(id, data) {
|
|
88
|
+
const doc = {};
|
|
89
|
+
Object.defineProperty(doc, "id", {
|
|
90
|
+
value: id,
|
|
91
|
+
enumerable: true,
|
|
92
|
+
writable: false,
|
|
93
|
+
configurable: false
|
|
94
|
+
});
|
|
95
|
+
for (const [field, value] of Object.entries(data)) Object.defineProperty(doc, field, {
|
|
96
|
+
value,
|
|
97
|
+
enumerable: true,
|
|
98
|
+
writable: true,
|
|
99
|
+
configurable: true
|
|
100
|
+
});
|
|
101
|
+
Object.defineProperty(doc, "data", {
|
|
102
|
+
value() {
|
|
103
|
+
return deletedDocs.has(this) ? null : extractDocumentData(this);
|
|
104
|
+
},
|
|
105
|
+
enumerable: true,
|
|
106
|
+
writable: false,
|
|
107
|
+
configurable: false
|
|
108
|
+
});
|
|
109
|
+
Object.defineProperty(doc, "set", {
|
|
110
|
+
get() {
|
|
111
|
+
return function setDocumentData(patch) {
|
|
112
|
+
Object.assign(this, patch);
|
|
113
|
+
};
|
|
114
|
+
},
|
|
115
|
+
enumerable: true,
|
|
116
|
+
configurable: false
|
|
117
|
+
});
|
|
118
|
+
return doc;
|
|
119
|
+
}
|
|
120
|
+
function extractDocumentData(value) {
|
|
121
|
+
if (!isRecord(value)) return {};
|
|
122
|
+
const data = {};
|
|
123
|
+
for (const [field, fieldValue] of Object.entries(value)) {
|
|
124
|
+
if (reservedDocumentFields.has(field) || field.startsWith("$") || typeof fieldValue === "function") continue;
|
|
125
|
+
if (isFieldValue(fieldValue)) data[field] = fieldValue;
|
|
126
|
+
}
|
|
127
|
+
return data;
|
|
128
|
+
}
|
|
73
129
|
function resolveDefault(value) {
|
|
74
130
|
const resolved = typeof value === "function" ? value() : value;
|
|
75
131
|
if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
|
|
@@ -108,21 +164,18 @@ function getDatabase() {
|
|
|
108
164
|
return database;
|
|
109
165
|
}
|
|
110
166
|
function loadRows(sourceName) {
|
|
111
|
-
return getDatabase().getAllSync(`SELECT id, data FROM sources WHERE source = ?`, [sourceName]).map((row) => (
|
|
112
|
-
id: row.id,
|
|
113
|
-
...JSON.parse(row.data)
|
|
114
|
-
}));
|
|
167
|
+
return getDatabase().getAllSync(`SELECT id, data FROM sources WHERE source = ?`, [sourceName]).map((row) => createDoc(row.id, JSON.parse(row.data)));
|
|
115
168
|
}
|
|
116
169
|
function insertRows(sourceName, rows) {
|
|
117
170
|
const db = getDatabase();
|
|
118
171
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
119
172
|
db.withTransactionSync(() => {
|
|
120
173
|
for (const row of rows) {
|
|
121
|
-
const
|
|
174
|
+
const data = extractDocumentData(row);
|
|
122
175
|
db.runSync(`INSERT INTO sources (source, id, data, createdAt, updatedAt, version)
|
|
123
176
|
VALUES (?, ?, ?, ?, ?, 1)`, [
|
|
124
177
|
sourceName,
|
|
125
|
-
id,
|
|
178
|
+
row.id,
|
|
126
179
|
JSON.stringify(data),
|
|
127
180
|
timestamp,
|
|
128
181
|
timestamp
|
|
@@ -136,7 +189,8 @@ function updateRows(sourceName, mutations) {
|
|
|
136
189
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
137
190
|
db.withTransactionSync(() => {
|
|
138
191
|
for (const mutation of mutations) {
|
|
139
|
-
const
|
|
192
|
+
const data = extractDocumentData(mutation.modified);
|
|
193
|
+
mutation.modified = createDoc(String(mutation.key), data);
|
|
140
194
|
db.runSync(`UPDATE sources
|
|
141
195
|
SET data = ?, updatedAt = ?, version = version + 1
|
|
142
196
|
WHERE source = ? AND id = ?`, [
|
|
@@ -149,13 +203,17 @@ function updateRows(sourceName, mutations) {
|
|
|
149
203
|
});
|
|
150
204
|
return Promise.resolve();
|
|
151
205
|
}
|
|
152
|
-
function deleteRows(sourceName,
|
|
206
|
+
function deleteRows(sourceName, mutations) {
|
|
153
207
|
const db = getDatabase();
|
|
154
208
|
db.withTransactionSync(() => {
|
|
155
|
-
for (const
|
|
209
|
+
for (const mutation of mutations) {
|
|
210
|
+
if (mutation.original) deletedDocs.add(mutation.original);
|
|
211
|
+
deletedDocs.add(mutation.modified);
|
|
212
|
+
db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(mutation.key)]);
|
|
213
|
+
}
|
|
156
214
|
});
|
|
157
215
|
return Promise.resolve();
|
|
158
216
|
}
|
|
159
217
|
|
|
160
218
|
//#endregion
|
|
161
|
-
export { source };
|
|
219
|
+
export { createID, source };
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "silosdk",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsdown",
|
|
7
7
|
"watch": "tsdown --watch",
|
|
8
8
|
"test": "vitest run",
|
|
9
|
-
"
|
|
9
|
+
"pub": "tsdown && npm publish"
|
|
10
10
|
},
|
|
11
11
|
"packageManager": "yarn@1.22.22",
|
|
12
12
|
"files": [
|