silosdk 0.0.1 → 0.0.3

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.
Files changed (92) hide show
  1. package/dist/settings.cjs +70 -0
  2. package/dist/settings.d.cts +20 -0
  3. package/dist/settings.d.mts +20 -0
  4. package/dist/settings.mjs +68 -0
  5. package/dist/source.cjs +162 -0
  6. package/dist/source.d.cts +29 -0
  7. package/dist/source.d.mts +29 -0
  8. package/dist/source.mjs +161 -0
  9. package/package.json +23 -20
  10. package/README.md +0 -3
  11. package/dist/cli/d1.cjs +0 -93
  12. package/dist/cli/d1.mjs +0 -92
  13. package/dist/cli/index.cjs +0 -93
  14. package/dist/cli/index.d.cts +0 -1
  15. package/dist/cli/index.d.mts +0 -1
  16. package/dist/cli/index.mjs +0 -94
  17. package/dist/cli/init.cjs +0 -134
  18. package/dist/cli/init.mjs +0 -133
  19. package/dist/cli/kv.cjs +0 -63
  20. package/dist/cli/kv.mjs +0 -60
  21. package/dist/cli/r2.cjs +0 -83
  22. package/dist/cli/r2.mjs +0 -82
  23. package/dist/cli/wrangler.cjs +0 -93
  24. package/dist/cli/wrangler.mjs +0 -89
  25. package/dist/local/adapters/cloudflare.cjs +0 -200
  26. package/dist/local/adapters/cloudflare.d.cts +0 -50
  27. package/dist/local/adapters/cloudflare.d.mts +0 -50
  28. package/dist/local/adapters/cloudflare.mjs +0 -200
  29. package/dist/local/auth-context.cjs +0 -14
  30. package/dist/local/auth-context.d.cts +0 -7
  31. package/dist/local/auth-context.d.mts +0 -7
  32. package/dist/local/auth-context.mjs +0 -12
  33. package/dist/local/auth.cjs +0 -109
  34. package/dist/local/auth.d.cts +0 -26
  35. package/dist/local/auth.d.mts +0 -26
  36. package/dist/local/auth.mjs +0 -99
  37. package/dist/local/commit.cjs +0 -350
  38. package/dist/local/commit.d.cts +0 -59
  39. package/dist/local/commit.d.mts +0 -59
  40. package/dist/local/commit.mjs +0 -349
  41. package/dist/local/config.cjs +0 -17
  42. package/dist/local/config.mjs +0 -15
  43. package/dist/local/index.cjs +0 -16
  44. package/dist/local/index.d.cts +0 -10
  45. package/dist/local/index.d.mts +0 -10
  46. package/dist/local/index.mjs +0 -9
  47. package/dist/local/provider.cjs +0 -204
  48. package/dist/local/provider.d.cts +0 -25
  49. package/dist/local/provider.d.mts +0 -25
  50. package/dist/local/provider.mjs +0 -203
  51. package/dist/local/query-store.cjs +0 -276
  52. package/dist/local/query-store.mjs +0 -274
  53. package/dist/local/storage.cjs +0 -71
  54. package/dist/local/storage.d.cts +0 -7
  55. package/dist/local/storage.d.mts +0 -7
  56. package/dist/local/storage.mjs +0 -68
  57. package/dist/local/sync.cjs +0 -124
  58. package/dist/local/sync.d.cts +0 -36
  59. package/dist/local/sync.d.mts +0 -36
  60. package/dist/local/sync.mjs +0 -122
  61. package/dist/local/view.cjs +0 -257
  62. package/dist/local/view.d.cts +0 -24
  63. package/dist/local/view.d.mts +0 -24
  64. package/dist/local/view.mjs +0 -254
  65. package/dist/package.cjs +0 -11
  66. package/dist/package.mjs +0 -5
  67. package/dist/schema/index.cjs +0 -276
  68. package/dist/schema/index.d.cts +0 -207
  69. package/dist/schema/index.d.mts +0 -207
  70. package/dist/schema/index.mjs +0 -265
  71. package/dist/server/auth.cjs +0 -132
  72. package/dist/server/auth.d.cts +0 -49
  73. package/dist/server/auth.d.mts +0 -49
  74. package/dist/server/auth.mjs +0 -122
  75. package/dist/server/d1.cjs +0 -120
  76. package/dist/server/d1.mjs +0 -116
  77. package/dist/server/do.cjs +0 -132
  78. package/dist/server/do.d.cts +0 -21
  79. package/dist/server/do.d.mts +0 -21
  80. package/dist/server/do.mjs +0 -131
  81. package/dist/server/index.cjs +0 -355
  82. package/dist/server/index.d.cts +0 -65
  83. package/dist/server/index.d.mts +0 -65
  84. package/dist/server/index.mjs +0 -348
  85. package/dist/server/protect.cjs +0 -34
  86. package/dist/server/protect.d.cts +0 -32
  87. package/dist/server/protect.d.mts +0 -32
  88. package/dist/server/protect.mjs +0 -33
  89. package/dist/server/r2.cjs +0 -58
  90. package/dist/server/r2.d.cts +0 -4
  91. package/dist/server/r2.d.mts +0 -4
  92. package/dist/server/r2.mjs +0 -53
@@ -0,0 +1,70 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let __tanstack_react_query = require("@tanstack/react-query");
3
+ let expo_sqlite_kv_store = require("expo-sqlite/kv-store");
4
+ expo_sqlite_kv_store = require_rolldown_runtime.__toESM(expo_sqlite_kv_store);
5
+
6
+ //#region src/settings/index.ts
7
+ const settingNames = /* @__PURE__ */ new Set();
8
+ const settingNamePattern = /^[A-Za-z][A-Za-z0-9_.:-]*$/;
9
+ function setting(name, defaultValue) {
10
+ assertSettingName(name);
11
+ assertSettingValue(resolveDefault(defaultValue));
12
+ if (settingNames.has(name)) throw new Error(`Setting "${name}" is already registered. Define each setting once and import the existing setting instead.`);
13
+ settingNames.add(name);
14
+ const queryKey = [
15
+ "silo",
16
+ "setting",
17
+ name
18
+ ];
19
+ return {
20
+ name,
21
+ defaultValue,
22
+ queryKey,
23
+ queryOptions() {
24
+ return (0, __tanstack_react_query.queryOptions)({
25
+ queryKey,
26
+ queryFn: () => readSetting(name, defaultValue)
27
+ });
28
+ },
29
+ mutationOptions(options = {}) {
30
+ return (0, __tanstack_react_query.mutationOptions)({
31
+ mutationKey: queryKey,
32
+ mutationFn: async (valueOrUpdater) => {
33
+ const current = readSetting(name, defaultValue);
34
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(current) : valueOrUpdater;
35
+ writeSetting(name, next);
36
+ return next;
37
+ },
38
+ onSuccess(value) {
39
+ options.queryClient?.setQueryData(queryKey, value);
40
+ }
41
+ });
42
+ }
43
+ };
44
+ }
45
+ function readSetting(name, defaultValue) {
46
+ const stored = expo_sqlite_kv_store.default.getItemSync(storageKey(name));
47
+ if (stored === null) return resolveDefault(defaultValue);
48
+ const value = JSON.parse(stored);
49
+ assertSettingValue(value);
50
+ return value;
51
+ }
52
+ function writeSetting(name, value) {
53
+ assertSettingValue(value);
54
+ expo_sqlite_kv_store.default.setItemSync(storageKey(name), JSON.stringify(value));
55
+ }
56
+ function storageKey(name) {
57
+ return `silo:setting:${name}`;
58
+ }
59
+ function resolveDefault(defaultValue) {
60
+ return typeof defaultValue === "function" ? defaultValue() : defaultValue;
61
+ }
62
+ function assertSettingName(name) {
63
+ if (!settingNamePattern.test(name)) throw new Error(`Setting names must start with a letter and contain only letters, numbers, underscores, dots, colons, or hyphens. Received "${name}".`);
64
+ }
65
+ function assertSettingValue(value) {
66
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && value !== null) throw new Error("Settings must be strings, numbers, booleans, or null.");
67
+ }
68
+
69
+ //#endregion
70
+ exports.setting = setting;
@@ -0,0 +1,20 @@
1
+ import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
+
3
+ //#region src/settings/index.d.ts
4
+ type SettingValue = string | number | boolean | null;
5
+ type SettingDefault<T extends SettingValue> = T | (() => T);
6
+ type SettingMutation<T extends SettingValue> = T | ((current: T) => T);
7
+ type SettingQueryKey = readonly ['silo', 'setting', string];
8
+ type SettingMutationOptions = {
9
+ queryClient?: QueryClient;
10
+ };
11
+ type Setting<T extends SettingValue> = {
12
+ readonly name: string;
13
+ readonly defaultValue: SettingDefault<T>;
14
+ readonly queryKey: SettingQueryKey;
15
+ queryOptions(): UseQueryOptions<T, Error, T, SettingQueryKey>;
16
+ mutationOptions(options?: SettingMutationOptions): UseMutationOptions<T, Error, SettingMutation<T>>;
17
+ };
18
+ declare function setting<T extends SettingValue>(name: string, defaultValue: SettingDefault<T>): Setting<T>;
19
+ //#endregion
20
+ export { Setting, SettingDefault, SettingMutation, SettingMutationOptions, SettingQueryKey, SettingValue, setting };
@@ -0,0 +1,20 @@
1
+ import { QueryClient, UseMutationOptions, UseQueryOptions } from "@tanstack/react-query";
2
+
3
+ //#region src/settings/index.d.ts
4
+ type SettingValue = string | number | boolean | null;
5
+ type SettingDefault<T extends SettingValue> = T | (() => T);
6
+ type SettingMutation<T extends SettingValue> = T | ((current: T) => T);
7
+ type SettingQueryKey = readonly ['silo', 'setting', string];
8
+ type SettingMutationOptions = {
9
+ queryClient?: QueryClient;
10
+ };
11
+ type Setting<T extends SettingValue> = {
12
+ readonly name: string;
13
+ readonly defaultValue: SettingDefault<T>;
14
+ readonly queryKey: SettingQueryKey;
15
+ queryOptions(): UseQueryOptions<T, Error, T, SettingQueryKey>;
16
+ mutationOptions(options?: SettingMutationOptions): UseMutationOptions<T, Error, SettingMutation<T>>;
17
+ };
18
+ declare function setting<T extends SettingValue>(name: string, defaultValue: SettingDefault<T>): Setting<T>;
19
+ //#endregion
20
+ export { Setting, SettingDefault, SettingMutation, SettingMutationOptions, SettingQueryKey, SettingValue, setting };
@@ -0,0 +1,68 @@
1
+ import { mutationOptions, queryOptions } from "@tanstack/react-query";
2
+ import Storage from "expo-sqlite/kv-store";
3
+
4
+ //#region src/settings/index.ts
5
+ const settingNames = /* @__PURE__ */ new Set();
6
+ const settingNamePattern = /^[A-Za-z][A-Za-z0-9_.:-]*$/;
7
+ function setting(name, defaultValue) {
8
+ assertSettingName(name);
9
+ assertSettingValue(resolveDefault(defaultValue));
10
+ if (settingNames.has(name)) throw new Error(`Setting "${name}" is already registered. Define each setting once and import the existing setting instead.`);
11
+ settingNames.add(name);
12
+ const queryKey = [
13
+ "silo",
14
+ "setting",
15
+ name
16
+ ];
17
+ return {
18
+ name,
19
+ defaultValue,
20
+ queryKey,
21
+ queryOptions() {
22
+ return queryOptions({
23
+ queryKey,
24
+ queryFn: () => readSetting(name, defaultValue)
25
+ });
26
+ },
27
+ mutationOptions(options = {}) {
28
+ return mutationOptions({
29
+ mutationKey: queryKey,
30
+ mutationFn: async (valueOrUpdater) => {
31
+ const current = readSetting(name, defaultValue);
32
+ const next = typeof valueOrUpdater === "function" ? valueOrUpdater(current) : valueOrUpdater;
33
+ writeSetting(name, next);
34
+ return next;
35
+ },
36
+ onSuccess(value) {
37
+ options.queryClient?.setQueryData(queryKey, value);
38
+ }
39
+ });
40
+ }
41
+ };
42
+ }
43
+ function readSetting(name, defaultValue) {
44
+ const stored = Storage.getItemSync(storageKey(name));
45
+ if (stored === null) return resolveDefault(defaultValue);
46
+ const value = JSON.parse(stored);
47
+ assertSettingValue(value);
48
+ return value;
49
+ }
50
+ function writeSetting(name, value) {
51
+ assertSettingValue(value);
52
+ Storage.setItemSync(storageKey(name), JSON.stringify(value));
53
+ }
54
+ function storageKey(name) {
55
+ return `silo:setting:${name}`;
56
+ }
57
+ function resolveDefault(defaultValue) {
58
+ return typeof defaultValue === "function" ? defaultValue() : defaultValue;
59
+ }
60
+ function assertSettingName(name) {
61
+ if (!settingNamePattern.test(name)) throw new Error(`Setting names must start with a letter and contain only letters, numbers, underscores, dots, colons, or hyphens. Received "${name}".`);
62
+ }
63
+ function assertSettingValue(value) {
64
+ if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean" && value !== null) throw new Error("Settings must be strings, numbers, booleans, or null.");
65
+ }
66
+
67
+ //#endregion
68
+ export { setting };
@@ -0,0 +1,162 @@
1
+ const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
2
+ let expo_sqlite = require("expo-sqlite");
3
+
4
+ //#region src/source/index.ts
5
+ const sourceNames = /* @__PURE__ */ new Set();
6
+ const sourceNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
7
+ function source(name, defaults) {
8
+ assertSourceName(name);
9
+ assertFlatDefaults(name, defaults);
10
+ if (sourceNames.has(name)) throw new Error(`Source "${name}" is already registered. Define each source once and import the existing source instead.`);
11
+ sourceNames.add(name);
12
+ const schema = createSourceSchema(name, defaults);
13
+ return {
14
+ name,
15
+ defaults,
16
+ schema,
17
+ collectionOptions(options = {}) {
18
+ return {
19
+ id: name,
20
+ getKey: (item) => item.id,
21
+ schema,
22
+ startSync: options.startSync,
23
+ sync: { sync({ begin, write, commit, markReady, truncate }) {
24
+ const rows = loadRows(name);
25
+ begin();
26
+ truncate();
27
+ for (const row of rows) write({
28
+ type: "insert",
29
+ value: row
30
+ });
31
+ commit();
32
+ markReady();
33
+ } },
34
+ onInsert: async ({ transaction }) => {
35
+ await insertRows(name, transaction.mutations.map((mutation) => mutation.modified));
36
+ },
37
+ onUpdate: async ({ transaction }) => {
38
+ await updateRows(name, transaction.mutations);
39
+ },
40
+ onDelete: async ({ transaction }) => {
41
+ await deleteRows(name, transaction.mutations.map((mutation) => mutation.key));
42
+ }
43
+ };
44
+ }
45
+ };
46
+ }
47
+ function assertSourceName(name) {
48
+ 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
+ }
50
+ 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.`);
52
+ }
53
+ function createSourceSchema(sourceName, defaults) {
54
+ return { "~standard": {
55
+ version: 1,
56
+ vendor: "silo",
57
+ validate(value) {
58
+ 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;
65
+ }
66
+ for (const [field, defaultValue] of Object.entries(defaults)) {
67
+ if (field in row) continue;
68
+ row[field] = resolveDefault(defaultValue);
69
+ }
70
+ return { value: row };
71
+ }
72
+ } };
73
+ }
74
+ function resolveDefault(value) {
75
+ const resolved = typeof value === "function" ? value() : value;
76
+ if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
77
+ return resolved;
78
+ }
79
+ function isRecord(value) {
80
+ return typeof value === "object" && value !== null && !Array.isArray(value);
81
+ }
82
+ function isFieldValue(value) {
83
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
84
+ }
85
+ function failure(message, path) {
86
+ return { issues: [{
87
+ message,
88
+ path
89
+ }] };
90
+ }
91
+ let database;
92
+ function getDatabase() {
93
+ if (!database) {
94
+ database = (0, expo_sqlite.openDatabaseSync)("silo.db");
95
+ database.execSync(`
96
+ PRAGMA journal_mode = WAL;
97
+
98
+ CREATE TABLE IF NOT EXISTS sources (
99
+ source TEXT NOT NULL,
100
+ id TEXT NOT NULL,
101
+ data TEXT NOT NULL,
102
+ createdAt TEXT NOT NULL,
103
+ updatedAt TEXT NOT NULL,
104
+ version INTEGER NOT NULL DEFAULT 1,
105
+ PRIMARY KEY (source, id)
106
+ );
107
+ `);
108
+ }
109
+ return database;
110
+ }
111
+ 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
+ }));
116
+ }
117
+ function insertRows(sourceName, rows) {
118
+ const db = getDatabase();
119
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
120
+ db.withTransactionSync(() => {
121
+ for (const row of rows) {
122
+ const { id, ...data } = row;
123
+ db.runSync(`INSERT INTO sources (source, id, data, createdAt, updatedAt, version)
124
+ VALUES (?, ?, ?, ?, ?, 1)`, [
125
+ sourceName,
126
+ id,
127
+ JSON.stringify(data),
128
+ timestamp,
129
+ timestamp
130
+ ]);
131
+ }
132
+ });
133
+ return Promise.resolve();
134
+ }
135
+ function updateRows(sourceName, mutations) {
136
+ const db = getDatabase();
137
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
138
+ db.withTransactionSync(() => {
139
+ for (const mutation of mutations) {
140
+ const { id, ...data } = mutation.modified;
141
+ db.runSync(`UPDATE sources
142
+ SET data = ?, updatedAt = ?, version = version + 1
143
+ WHERE source = ? AND id = ?`, [
144
+ JSON.stringify(data),
145
+ timestamp,
146
+ sourceName,
147
+ String(mutation.key)
148
+ ]);
149
+ }
150
+ });
151
+ return Promise.resolve();
152
+ }
153
+ function deleteRows(sourceName, ids) {
154
+ const db = getDatabase();
155
+ db.withTransactionSync(() => {
156
+ for (const id of ids) db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(id)]);
157
+ });
158
+ return Promise.resolve();
159
+ }
160
+
161
+ //#endregion
162
+ exports.source = source;
@@ -0,0 +1,29 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { CollectionConfig } from "@tanstack/react-db";
3
+
4
+ //#region src/source/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 SourceRow<T extends object> = {
13
+ id: string;
14
+ } & DocumentData<T>;
15
+ type SourceInput<T extends object> = {
16
+ id: string;
17
+ } & Partial<DocumentData<T>>;
18
+ type SourceCollectionOptions<T extends object> = {
19
+ startSync?: boolean;
20
+ };
21
+ type Source<T extends object> = {
22
+ readonly name: string;
23
+ 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>>>;
26
+ };
27
+ declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
28
+ //#endregion
29
+ export { DefaultValue, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, source };
@@ -0,0 +1,29 @@
1
+ import { StandardSchemaV1 } from "@standard-schema/spec";
2
+ import { CollectionConfig } from "@tanstack/react-db";
3
+
4
+ //#region src/source/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 SourceRow<T extends object> = {
13
+ id: string;
14
+ } & DocumentData<T>;
15
+ type SourceInput<T extends object> = {
16
+ id: string;
17
+ } & Partial<DocumentData<T>>;
18
+ type SourceCollectionOptions<T extends object> = {
19
+ startSync?: boolean;
20
+ };
21
+ type Source<T extends object> = {
22
+ readonly name: string;
23
+ 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>>>;
26
+ };
27
+ declare function source<T extends object>(name: string, defaults: SourceDefaults<T>): Source<T>;
28
+ //#endregion
29
+ export { DefaultValue, DocumentData, FieldValue, Source, SourceCollectionOptions, SourceDefaults, SourceInput, SourceRow, source };
@@ -0,0 +1,161 @@
1
+ import { openDatabaseSync } from "expo-sqlite";
2
+
3
+ //#region src/source/index.ts
4
+ const sourceNames = /* @__PURE__ */ new Set();
5
+ const sourceNamePattern = /^[A-Za-z][A-Za-z0-9_-]*$/;
6
+ function source(name, defaults) {
7
+ assertSourceName(name);
8
+ assertFlatDefaults(name, defaults);
9
+ if (sourceNames.has(name)) throw new Error(`Source "${name}" is already registered. Define each source once and import the existing source instead.`);
10
+ sourceNames.add(name);
11
+ const schema = createSourceSchema(name, defaults);
12
+ return {
13
+ name,
14
+ defaults,
15
+ schema,
16
+ collectionOptions(options = {}) {
17
+ return {
18
+ id: name,
19
+ getKey: (item) => item.id,
20
+ schema,
21
+ startSync: options.startSync,
22
+ sync: { sync({ begin, write, commit, markReady, truncate }) {
23
+ const rows = loadRows(name);
24
+ begin();
25
+ truncate();
26
+ for (const row of rows) write({
27
+ type: "insert",
28
+ value: row
29
+ });
30
+ commit();
31
+ markReady();
32
+ } },
33
+ onInsert: async ({ transaction }) => {
34
+ await insertRows(name, transaction.mutations.map((mutation) => mutation.modified));
35
+ },
36
+ onUpdate: async ({ transaction }) => {
37
+ await updateRows(name, transaction.mutations);
38
+ },
39
+ onDelete: async ({ transaction }) => {
40
+ await deleteRows(name, transaction.mutations.map((mutation) => mutation.key));
41
+ }
42
+ };
43
+ }
44
+ };
45
+ }
46
+ function assertSourceName(name) {
47
+ 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
+ }
49
+ 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.`);
51
+ }
52
+ function createSourceSchema(sourceName, defaults) {
53
+ return { "~standard": {
54
+ version: 1,
55
+ vendor: "silo",
56
+ validate(value) {
57
+ 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;
64
+ }
65
+ for (const [field, defaultValue] of Object.entries(defaults)) {
66
+ if (field in row) continue;
67
+ row[field] = resolveDefault(defaultValue);
68
+ }
69
+ return { value: row };
70
+ }
71
+ } };
72
+ }
73
+ function resolveDefault(value) {
74
+ const resolved = typeof value === "function" ? value() : value;
75
+ if (!isFieldValue(resolved)) throw new Error("Source defaults must resolve to a string, number, boolean, or null.");
76
+ return resolved;
77
+ }
78
+ function isRecord(value) {
79
+ return typeof value === "object" && value !== null && !Array.isArray(value);
80
+ }
81
+ function isFieldValue(value) {
82
+ return typeof value === "string" || typeof value === "number" || typeof value === "boolean" || value === null;
83
+ }
84
+ function failure(message, path) {
85
+ return { issues: [{
86
+ message,
87
+ path
88
+ }] };
89
+ }
90
+ let database;
91
+ function getDatabase() {
92
+ if (!database) {
93
+ database = openDatabaseSync("silo.db");
94
+ database.execSync(`
95
+ PRAGMA journal_mode = WAL;
96
+
97
+ CREATE TABLE IF NOT EXISTS sources (
98
+ source TEXT NOT NULL,
99
+ id TEXT NOT NULL,
100
+ data TEXT NOT NULL,
101
+ createdAt TEXT NOT NULL,
102
+ updatedAt TEXT NOT NULL,
103
+ version INTEGER NOT NULL DEFAULT 1,
104
+ PRIMARY KEY (source, id)
105
+ );
106
+ `);
107
+ }
108
+ return database;
109
+ }
110
+ 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
+ }));
115
+ }
116
+ function insertRows(sourceName, rows) {
117
+ const db = getDatabase();
118
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
119
+ db.withTransactionSync(() => {
120
+ for (const row of rows) {
121
+ const { id, ...data } = row;
122
+ db.runSync(`INSERT INTO sources (source, id, data, createdAt, updatedAt, version)
123
+ VALUES (?, ?, ?, ?, ?, 1)`, [
124
+ sourceName,
125
+ id,
126
+ JSON.stringify(data),
127
+ timestamp,
128
+ timestamp
129
+ ]);
130
+ }
131
+ });
132
+ return Promise.resolve();
133
+ }
134
+ function updateRows(sourceName, mutations) {
135
+ const db = getDatabase();
136
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
137
+ db.withTransactionSync(() => {
138
+ for (const mutation of mutations) {
139
+ const { id, ...data } = mutation.modified;
140
+ db.runSync(`UPDATE sources
141
+ SET data = ?, updatedAt = ?, version = version + 1
142
+ WHERE source = ? AND id = ?`, [
143
+ JSON.stringify(data),
144
+ timestamp,
145
+ sourceName,
146
+ String(mutation.key)
147
+ ]);
148
+ }
149
+ });
150
+ return Promise.resolve();
151
+ }
152
+ function deleteRows(sourceName, ids) {
153
+ const db = getDatabase();
154
+ db.withTransactionSync(() => {
155
+ for (const id of ids) db.runSync(`DELETE FROM sources WHERE source = ? AND id = ?`, [sourceName, String(id)]);
156
+ });
157
+ return Promise.resolve();
158
+ }
159
+
160
+ //#endregion
161
+ export { source };
package/package.json CHANGED
@@ -1,39 +1,39 @@
1
1
  {
2
2
  "name": "silosdk",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "tsdown",
7
7
  "watch": "tsdown --watch",
8
+ "test": "vitest run",
8
9
  "publish": "tsdown && npm publish"
9
10
  },
10
- "packageManager": "yarn@4.9.2",
11
+ "packageManager": "yarn@1.22.22",
11
12
  "files": [
12
13
  "dist/"
13
14
  ],
14
- "bin": {
15
- "silo": "./dist/cli/index.mjs"
16
- },
17
15
  "devDependencies": {
18
- "@cloudflare/workers-types": "^4.20260219.0",
16
+ "@tanstack/react-db": "^0.1.86",
17
+ "@tanstack/react-query": "^5.101.0",
19
18
  "@types/node": "^25.3.0",
20
19
  "@types/react": "^19",
21
- "expo-secure-store": "*",
22
20
  "expo-sqlite": "*",
23
21
  "prettier": "^3.7.4",
22
+ "react-native": "0.85.0",
24
23
  "tsdown": "^0.17.2",
25
- "typescript": "^5.9.3"
24
+ "typescript": "^5.9.3",
25
+ "vitest": "^4.0.18"
26
26
  },
27
27
  "dependencies": {
28
28
  "@standard-schema/spec": "^1.0.0",
29
- "jotai": "^2.15.1",
30
- "jotai-family": "^1.0.1",
29
+ "@tanstack/react-db": "^0.1.86",
30
+ "@tanstack/react-query": "^5.101.0",
31
31
  "nanoid": "5.1.6"
32
32
  },
33
33
  "peerDependencies": {
34
- "expo-secure-store": "*",
35
34
  "expo-sqlite": "*",
36
- "react": "^19"
35
+ "react": "^19",
36
+ "react-native": "0.85.x"
37
37
  },
38
38
  "prettier": {
39
39
  "semi": false,
@@ -42,16 +42,19 @@
42
42
  "printWidth": 80,
43
43
  "tabWidth": 2
44
44
  },
45
+ "main": "./dist/source.cjs",
46
+ "module": "./dist/source.mjs",
47
+ "types": "./dist/source.d.cts",
45
48
  "exports": {
46
- "./schema": {
47
- "types": "./dist/schema/index.d.mts",
48
- "import": "./dist/schema/index.mjs",
49
- "require": "./dist/schema/index.cjs"
49
+ "./source": {
50
+ "types": "./dist/source.d.mts",
51
+ "import": "./dist/source.mjs",
52
+ "require": "./dist/source.cjs"
50
53
  },
51
- "./local": "./dist/local/index.cjs",
52
- "./server": {
53
- "types": "./dist/server/index.d.mts",
54
- "import": "./dist/server/index.mjs"
54
+ "./settings": {
55
+ "types": "./dist/settings.d.mts",
56
+ "import": "./dist/settings.mjs",
57
+ "require": "./dist/settings.cjs"
55
58
  },
56
59
  "./package.json": "./package.json"
57
60
  }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Silo
2
-
3
- Type-safe reactive data for React Native and The Edge.