silosdk 0.0.4 → 0.0.6

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
@@ -11,6 +11,7 @@ Silo owns:
11
11
  - defaults-first source shape
12
12
  - settings storage
13
13
  - media file ownership and resolution
14
+ - ID generation through `expo-crypto`
14
15
 
15
16
  TanStack owns:
16
17
 
@@ -21,7 +22,7 @@ TanStack owns:
21
22
  ## Install
22
23
 
23
24
  ```sh
24
- yarn add silosdk @tanstack/react-db @tanstack/react-query expo-sqlite expo-file-system
25
+ yarn add silosdk @tanstack/react-db @tanstack/react-query expo-crypto expo-sqlite expo-file-system
25
26
  ```
26
27
 
27
28
  ## Sources
@@ -31,7 +32,7 @@ collections.
31
32
 
32
33
  ```ts
33
34
  import { createCollection } from '@tanstack/react-db'
34
- import { source } from 'silosdk/source'
35
+ import { createID, source } from 'silosdk/source'
35
36
 
36
37
  type Post = {
37
38
  title: string
@@ -55,19 +56,37 @@ Query and mutate with TanStack DB directly:
55
56
  ```tsx
56
57
  import { eq, useLiveQuery } from '@tanstack/react-db'
57
58
 
58
- const { data = [] } = useLiveQuery((q) =>
59
+ const { data: docs = [] } = useLiveQuery((q) =>
59
60
  q.from({ posts }).where(({ posts }) => eq(posts.published, true)),
60
61
  )
61
62
 
63
+ const doc = docs[0]
64
+ const post = doc?.data()
65
+
66
+ doc?.id
67
+ post?.title
68
+ ```
69
+
70
+ ```ts
71
+ const id = createID()
72
+
62
73
  const tx = posts.insert({
63
- id: crypto.randomUUID(),
64
- title: 'Hello',
65
- body: '',
66
- published: false,
67
- coverImage: null,
74
+ id,
75
+ data: {
76
+ title: 'Hello',
77
+ body: '',
78
+ published: false,
79
+ coverImage: null,
80
+ },
68
81
  })
69
82
 
70
83
  await tx.isPersisted.promise
84
+
85
+ await posts.update(id, (doc) => {
86
+ doc.set({ published: true })
87
+ }).isPersisted.promise
88
+
89
+ await posts.delete(id).isPersisted.promise
71
90
  ```
72
91
 
73
92
  Source rows are intentionally flat. Field values must be:
@@ -134,7 +153,7 @@ Store the returned `media://...` ref in source rows:
134
153
 
135
154
  ```ts
136
155
  posts.update(postId, (draft) => {
137
- draft.coverImage = coverImage
156
+ draft.set({ coverImage })
138
157
  })
139
158
  ```
140
159
 
package/dist/media.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let expo_crypto = require("expo-crypto");
2
3
  let expo_sqlite = require("expo-sqlite");
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 = {
@@ -39,7 +39,7 @@ function mediaQueryKey(ref) {
39
39
  }
40
40
  async function importMedia(input) {
41
41
  assertMediaKind(input.kind);
42
- const id = (0, nanoid.nanoid)();
42
+ const id = (0, expo_crypto.randomUUID)();
43
43
  const ref = createMediaRef(input.kind, id);
44
44
  const source = new expo_file_system.File(input.uri);
45
45
  const name = input.name ?? source.name ?? `${id}${source.extension}`;
package/dist/media.mjs CHANGED
@@ -1,7 +1,7 @@
1
+ import { randomUUID } from "expo-crypto";
1
2
  import { openDatabaseSync } from "expo-sqlite";
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 = {
@@ -38,7 +38,7 @@ function mediaQueryKey(ref) {
38
38
  }
39
39
  async function importMedia(input) {
40
40
  assertMediaKind(input.kind);
41
- const id = nanoid();
41
+ const id = randomUUID();
42
42
  const ref = createMediaRef(input.kind, id);
43
43
  const source = new File(input.uri);
44
44
  const name = input.name ?? source.name ?? `${id}${source.extension}`;
package/dist/source.cjs CHANGED
@@ -1,9 +1,19 @@
1
1
  const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let expo_crypto = require("expo-crypto");
2
3
  let expo_sqlite = require("expo-sqlite");
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, expo_crypto.randomUUID)();
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.map((mutation) => mutation.key));
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)) 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.`);
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 row field \"id\" to be a string.", ["id"]);
60
- const row = { id: value.id };
61
- for (const [field, fieldValue] of Object.entries(value)) {
62
- if (field === "id") continue;
63
- if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, [field]);
64
- row[field] = fieldValue;
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 row) continue;
68
- row[field] = resolveDefault(defaultValue);
81
+ if (field in data) continue;
82
+ data[field] = resolveDefault(defaultValue);
69
83
  }
70
- return { value: row };
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 { id, ...data } = row;
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 { id, ...data } = mutation.modified;
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, ids) {
207
+ function deleteRows(sourceName, mutations) {
154
208
  const db = getDatabase();
155
209
  db.withTransactionSync(() => {
156
- for (const id of ids) db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(id)]);
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
- } & Partial<DocumentData<T>>;
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<SourceInput<T>, SourceRow<T>>;
25
- collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<SourceInput<T>, SourceRow<T>>>;
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
- } & Partial<DocumentData<T>>;
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<SourceInput<T>, SourceRow<T>>;
25
- collectionOptions(options?: SourceCollectionOptions<T>): CollectionConfig<SourceRow<T>, string, StandardSchemaV1<SourceInput<T>, SourceRow<T>>>;
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
+ import { randomUUID } from "expo-crypto";
1
2
  import { openDatabaseSync } from "expo-sqlite";
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 randomUUID();
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.map((mutation) => mutation.key));
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)) 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.`);
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 row field \"id\" to be a string.", ["id"]);
59
- const row = { id: value.id };
60
- for (const [field, fieldValue] of Object.entries(value)) {
61
- if (field === "id") continue;
62
- if (!isFieldValue(fieldValue)) return failure(`Source "${sourceName}" field "${field}" must be a string, number, boolean, or null.`, [field]);
63
- row[field] = fieldValue;
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 row) continue;
67
- row[field] = resolveDefault(defaultValue);
80
+ if (field in data) continue;
81
+ data[field] = resolveDefault(defaultValue);
68
82
  }
69
- return { value: row };
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 { id, ...data } = row;
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 { id, ...data } = mutation.modified;
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, ids) {
206
+ function deleteRows(sourceName, mutations) {
153
207
  const db = getDatabase();
154
208
  db.withTransactionSync(() => {
155
- for (const id of ids) db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(id)]);
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.4",
3
+ "version": "0.0.6",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "tsdown",
7
7
  "watch": "tsdown --watch",
8
8
  "test": "vitest run",
9
- "publish": "tsdown && npm publish"
9
+ "pub": "tsdown && npm publish"
10
10
  },
11
11
  "packageManager": "yarn@1.22.22",
12
12
  "files": [
@@ -17,6 +17,7 @@
17
17
  "@tanstack/react-query": "^5.101.0",
18
18
  "@types/node": "^25.3.0",
19
19
  "@types/react": "^19",
20
+ "expo-crypto": "^56.0.4",
20
21
  "expo-file-system": "^56.0.8",
21
22
  "expo-sqlite": "*",
22
23
  "prettier": "^3.7.4",
@@ -26,12 +27,12 @@
26
27
  "vitest": "^4.0.18"
27
28
  },
28
29
  "dependencies": {
29
- "@standard-schema/spec": "^1.0.0",
30
- "nanoid": "5.1.6"
30
+ "@standard-schema/spec": "^1.0.0"
31
31
  },
32
32
  "peerDependencies": {
33
33
  "@tanstack/react-db": "^0.1.86",
34
34
  "@tanstack/react-query": "^5.101.0",
35
+ "expo-crypto": "^56.0.4",
35
36
  "expo-file-system": "^56.0.8",
36
37
  "expo-sqlite": "*",
37
38
  "react": "^19",