silosdk 0.0.0 → 0.0.1

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 (85) hide show
  1. package/README.md +3 -1
  2. package/dist/_virtual/rolldown_runtime.cjs +29 -0
  3. package/dist/cli/d1.cjs +93 -0
  4. package/dist/cli/d1.mjs +92 -0
  5. package/dist/cli/index.cjs +93 -0
  6. package/dist/cli/index.d.cts +1 -0
  7. package/dist/cli/index.d.mts +1 -0
  8. package/dist/cli/index.mjs +94 -0
  9. package/dist/cli/init.cjs +134 -0
  10. package/dist/cli/init.mjs +133 -0
  11. package/dist/cli/kv.cjs +63 -0
  12. package/dist/cli/kv.mjs +60 -0
  13. package/dist/cli/r2.cjs +83 -0
  14. package/dist/cli/r2.mjs +82 -0
  15. package/dist/cli/wrangler.cjs +93 -0
  16. package/dist/cli/wrangler.mjs +89 -0
  17. package/dist/local/adapters/cloudflare.cjs +200 -0
  18. package/dist/local/adapters/cloudflare.d.cts +50 -0
  19. package/dist/local/adapters/cloudflare.d.mts +50 -0
  20. package/dist/local/adapters/cloudflare.mjs +200 -0
  21. package/dist/local/auth-context.cjs +14 -0
  22. package/dist/local/auth-context.d.cts +7 -0
  23. package/dist/local/auth-context.d.mts +7 -0
  24. package/dist/local/auth-context.mjs +12 -0
  25. package/dist/local/auth.cjs +109 -0
  26. package/dist/local/auth.d.cts +26 -0
  27. package/dist/local/auth.d.mts +26 -0
  28. package/dist/local/auth.mjs +99 -0
  29. package/dist/local/commit.cjs +350 -0
  30. package/dist/local/commit.d.cts +59 -0
  31. package/dist/local/commit.d.mts +59 -0
  32. package/dist/local/commit.mjs +349 -0
  33. package/dist/local/config.cjs +17 -0
  34. package/dist/local/config.mjs +15 -0
  35. package/dist/local/index.cjs +16 -0
  36. package/dist/local/index.d.cts +10 -0
  37. package/dist/local/index.d.mts +10 -0
  38. package/dist/local/index.mjs +9 -0
  39. package/dist/local/provider.cjs +204 -0
  40. package/dist/local/provider.d.cts +25 -0
  41. package/dist/local/provider.d.mts +25 -0
  42. package/dist/local/provider.mjs +203 -0
  43. package/dist/local/query-store.cjs +276 -0
  44. package/dist/local/query-store.mjs +274 -0
  45. package/dist/local/storage.cjs +71 -0
  46. package/dist/local/storage.d.cts +7 -0
  47. package/dist/local/storage.d.mts +7 -0
  48. package/dist/local/storage.mjs +68 -0
  49. package/dist/local/sync.cjs +124 -0
  50. package/dist/local/sync.d.cts +36 -0
  51. package/dist/local/sync.d.mts +36 -0
  52. package/dist/local/sync.mjs +122 -0
  53. package/dist/local/view.cjs +257 -0
  54. package/dist/local/view.d.cts +24 -0
  55. package/dist/local/view.d.mts +24 -0
  56. package/dist/local/view.mjs +254 -0
  57. package/dist/package.cjs +11 -0
  58. package/dist/package.mjs +5 -0
  59. package/dist/schema/index.cjs +276 -0
  60. package/dist/schema/index.d.cts +207 -0
  61. package/dist/schema/index.d.mts +207 -0
  62. package/dist/schema/index.mjs +265 -0
  63. package/dist/server/auth.cjs +132 -0
  64. package/dist/server/auth.d.cts +49 -0
  65. package/dist/server/auth.d.mts +49 -0
  66. package/dist/server/auth.mjs +122 -0
  67. package/dist/server/d1.cjs +120 -0
  68. package/dist/server/d1.mjs +116 -0
  69. package/dist/server/do.cjs +132 -0
  70. package/dist/server/do.d.cts +21 -0
  71. package/dist/server/do.d.mts +21 -0
  72. package/dist/server/do.mjs +131 -0
  73. package/dist/server/index.cjs +355 -0
  74. package/dist/server/index.d.cts +65 -0
  75. package/dist/server/index.d.mts +65 -0
  76. package/dist/server/index.mjs +348 -0
  77. package/dist/server/protect.cjs +34 -0
  78. package/dist/server/protect.d.cts +32 -0
  79. package/dist/server/protect.d.mts +32 -0
  80. package/dist/server/protect.mjs +33 -0
  81. package/dist/server/r2.cjs +58 -0
  82. package/dist/server/r2.d.cts +4 -0
  83. package/dist/server/r2.d.mts +4 -0
  84. package/dist/server/r2.mjs +53 -0
  85. package/package.json +55 -2
@@ -0,0 +1,349 @@
1
+ import { useConfig } from "./config.mjs";
2
+ import { getQueryStoreInstance } from "./view.mjs";
3
+ import { isAssetValue, isResolvedAsset, resolvePendingAsset } from "./storage.mjs";
4
+ import { useSQLiteContext } from "expo-sqlite";
5
+ import { nanoid } from "nanoid/non-secure";
6
+
7
+ //#region src/local/commit.ts
8
+ function isCrudOp(op) {
9
+ return op.kind === "add" || op.kind === "update" || op.kind === "remove";
10
+ }
11
+ function isObject(value) {
12
+ return typeof value === "object" && value !== null && !Array.isArray(value);
13
+ }
14
+ function collectResolvedAssetKeys(value, out) {
15
+ if (Array.isArray(value)) {
16
+ for (const item of value) collectResolvedAssetKeys(item, out);
17
+ return;
18
+ }
19
+ if (!isObject(value)) return;
20
+ if (isResolvedAsset(value)) {
21
+ out.add(value.key);
22
+ return;
23
+ }
24
+ for (const nested of Object.values(value)) collectResolvedAssetKeys(nested, out);
25
+ }
26
+ function buildAssetCleanupTasks(oldValue, newValue, scope) {
27
+ const oldKeys = /* @__PURE__ */ new Set();
28
+ const newKeys = /* @__PURE__ */ new Set();
29
+ collectResolvedAssetKeys(oldValue, oldKeys);
30
+ collectResolvedAssetKeys(newValue, newKeys);
31
+ const tasks = [];
32
+ for (const key of oldKeys) if (!newKeys.has(key)) tasks.push({
33
+ key,
34
+ scope
35
+ });
36
+ return tasks;
37
+ }
38
+ async function resolveAssetsDeep(value, scope, url, getToken) {
39
+ if (Array.isArray(value)) {
40
+ const out$1 = [];
41
+ for (const item of value) out$1.push(await resolveAssetsDeep(item, scope, url, getToken));
42
+ return out$1;
43
+ }
44
+ if (!isObject(value)) return value;
45
+ if (isAssetValue(value)) return resolvePendingAsset(value, scope, url, getToken);
46
+ const out = {};
47
+ for (const [k, v] of Object.entries(value)) out[k] = await resolveAssetsDeep(v, scope, url, getToken);
48
+ return out;
49
+ }
50
+ async function resolveAssetsInOps(ops, url, getToken) {
51
+ const resolved = [];
52
+ for (const op of ops) {
53
+ if (!isCrudOp(op)) {
54
+ resolved.push(op);
55
+ continue;
56
+ }
57
+ if (op.kind === "remove") {
58
+ resolved.push(op);
59
+ continue;
60
+ }
61
+ const scope = op.scope ?? "private";
62
+ if (op.kind === "add") {
63
+ const value$1 = await resolveAssetsDeep(op.value, scope, url, getToken);
64
+ resolved.push({
65
+ ...op,
66
+ value: value$1
67
+ });
68
+ continue;
69
+ }
70
+ const value = await resolveAssetsDeep(op.value, scope, url, getToken);
71
+ resolved.push({
72
+ ...op,
73
+ value
74
+ });
75
+ }
76
+ return resolved;
77
+ }
78
+ async function getExistingRowData(db, viewName, id) {
79
+ const row = await db.getFirstAsync(`SELECT data FROM views WHERE id = ? AND view = ?`, [id, viewName]);
80
+ if (!row?.data) return null;
81
+ try {
82
+ return JSON.parse(row.data);
83
+ } catch {
84
+ return null;
85
+ }
86
+ }
87
+ function mergeForUpdate(existing, patch) {
88
+ if (!isObject(existing)) return patch;
89
+ if (!isObject(patch)) return existing;
90
+ return {
91
+ ...existing,
92
+ ...patch
93
+ };
94
+ }
95
+ async function planAssetCleanupFromOps(db, ops) {
96
+ const tasks = [];
97
+ for (const op of ops) {
98
+ if (!isCrudOp(op)) continue;
99
+ const scope = op.scope ?? "private";
100
+ if (op.kind === "remove") {
101
+ const existing = await getExistingRowData(db, op.view.name, op.id);
102
+ tasks.push(...buildAssetCleanupTasks(existing, null, scope));
103
+ continue;
104
+ }
105
+ if (op.kind === "update") {
106
+ const existing = await getExistingRowData(db, op.view.name, op.id);
107
+ const merged = mergeForUpdate(existing, op.value);
108
+ tasks.push(...buildAssetCleanupTasks(existing, merged, scope));
109
+ continue;
110
+ }
111
+ }
112
+ return tasks;
113
+ }
114
+ async function performAssetCleanup(tasks, url, getToken) {
115
+ if (!url || tasks.length === 0) return;
116
+ const unique = /* @__PURE__ */ new Map();
117
+ for (const t of tasks) unique.set(`${t.scope}:${t.key}`, t);
118
+ const headers = {};
119
+ if (getToken) {
120
+ const token = await getToken();
121
+ if (token) headers.Authorization = `Bearer ${token}`;
122
+ }
123
+ await Promise.all(Array.from(unique.values()).map(async ({ key }) => {
124
+ const res = await fetch(`${url}/storage/${encodeURIComponent(key)}`, {
125
+ method: "DELETE",
126
+ headers
127
+ });
128
+ if (![
129
+ 204,
130
+ 404,
131
+ 401,
132
+ 403
133
+ ].includes(res.status)) console.warn(`[silosdk] asset cleanup failed for key ${key}: ${res.status}`);
134
+ }));
135
+ }
136
+ function useCommit() {
137
+ const db = useSQLiteContext();
138
+ const config = useConfig();
139
+ const { subscribers, syncAdapter } = config;
140
+ const ops = [];
141
+ const touchedViews = /* @__PURE__ */ new Set();
142
+ function view(v, idOrOpts, opts) {
143
+ const id = typeof idOrOpts === "string" ? idOrOpts : void 0;
144
+ const scope = (typeof idOrOpts === "object" ? idOrOpts?.scope : opts?.scope) ?? "private";
145
+ if (id) return {
146
+ update: (value) => {
147
+ touchedViews.add(v);
148
+ ops.push({
149
+ kind: "update",
150
+ view: v,
151
+ id,
152
+ value,
153
+ scope
154
+ });
155
+ },
156
+ remove: () => {
157
+ touchedViews.add(v);
158
+ ops.push({
159
+ kind: "remove",
160
+ view: v,
161
+ id,
162
+ scope
163
+ });
164
+ },
165
+ child: (subview) => ({
166
+ add: (cid) => {
167
+ touchedViews.add(v);
168
+ touchedViews.add(subview);
169
+ ops.push({
170
+ kind: "link",
171
+ parent: {
172
+ view: v,
173
+ id
174
+ },
175
+ child: {
176
+ view: subview,
177
+ id: cid
178
+ },
179
+ scope
180
+ });
181
+ },
182
+ remove: (cid) => {
183
+ touchedViews.add(v);
184
+ touchedViews.add(subview);
185
+ ops.push({
186
+ kind: "unlink",
187
+ parent: {
188
+ view: v,
189
+ id
190
+ },
191
+ child: {
192
+ view: subview,
193
+ id: cid
194
+ },
195
+ scope
196
+ });
197
+ }
198
+ })
199
+ };
200
+ return { add: (value) => {
201
+ touchedViews.add(v);
202
+ const id$1 = nanoid();
203
+ ops.push({
204
+ kind: "add",
205
+ view: v,
206
+ id: id$1,
207
+ value,
208
+ scope
209
+ });
210
+ return id$1;
211
+ } };
212
+ }
213
+ return async (handler) => {
214
+ await handler({ view });
215
+ const resolvedOps = await resolveAssetsInOps(ops, config.url, config.getToken);
216
+ const cleanupTasks = await planAssetCleanupFromOps(db, resolvedOps);
217
+ const privateOps = resolvedOps.filter((op) => (op.scope ?? "private") === "private");
218
+ const publicOps = resolvedOps.filter((op) => op.scope === "public");
219
+ const syncOps = privateOps.length > 0 ? await applyOpsToSqlite(db, privateOps) : [];
220
+ for (const sub of subscribers) for (const coll of touchedViews) if (coll.scope !== "public" && sub[0].includes(coll.name)) {
221
+ sub[1].update();
222
+ break;
223
+ }
224
+ if (syncAdapter && syncOps.length > 0) await syncAdapter.send({ ops: syncOps });
225
+ if (publicOps.length > 0) {
226
+ const qs = getQueryStoreInstance();
227
+ const url = config.url;
228
+ const getToken = config.getToken;
229
+ const publicViewNames = new Set(publicOps.map((op) => op.kind === "link" || op.kind === "unlink" ? op.parent.view.name : op.view.name));
230
+ qs.invalidate(publicViewNames);
231
+ if (url) {
232
+ const byView = /* @__PURE__ */ new Map();
233
+ for (const op of publicOps) {
234
+ const viewName = op.kind === "link" || op.kind === "unlink" ? op.parent.view.name : op.view.name;
235
+ const existing = byView.get(viewName) ?? [];
236
+ existing.push(op);
237
+ byView.set(viewName, existing);
238
+ }
239
+ const headers = { "Content-Type": "application/json" };
240
+ if (getToken) headers["Authorization"] = `Bearer ${await getToken()}`;
241
+ await Promise.all(Array.from(byView.entries()).map(async ([viewName, viewOps]) => {
242
+ const res = await fetch(`${url}/public/${viewName}`, {
243
+ method: "POST",
244
+ headers,
245
+ body: JSON.stringify({ ops: viewOps })
246
+ });
247
+ if (!res.ok) {
248
+ qs.invalidate(new Set([viewName]));
249
+ throw new Error(`Public write to ${viewName} failed: ${res.status}`);
250
+ }
251
+ }));
252
+ }
253
+ }
254
+ await performAssetCleanup(cleanupTasks, config.url, config.getToken);
255
+ };
256
+ }
257
+ async function applyOpsToSqlite(db, ops) {
258
+ const syncOps = [];
259
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
260
+ await db.withTransactionAsync(async () => {
261
+ for (const op of ops) {
262
+ if (op.kind === "add" && !!op.value) {
263
+ const version = 1;
264
+ await db.runAsync(`INSERT INTO views (id, view, data, createdAt, updatedAt, version) VALUES (?, ?, ?, ?, ?, ?);`, [
265
+ op.id,
266
+ op.view.name,
267
+ JSON.stringify(op.value),
268
+ timestamp,
269
+ timestamp,
270
+ version
271
+ ]);
272
+ syncOps.push({
273
+ ...op,
274
+ version,
275
+ timestamp
276
+ });
277
+ }
278
+ if (op.kind === "update" && !!op.id && !!op.value) {
279
+ await db.runAsync(`UPDATE views
280
+ SET data = jsonb_patch(data, ?), updatedAt = ?, version = version + 1
281
+ WHERE id = ? AND view = ?;`, [
282
+ JSON.stringify(op.value),
283
+ timestamp,
284
+ op.id,
285
+ op.view.name
286
+ ]);
287
+ const result = await db.getFirstAsync(`SELECT version, data FROM views WHERE id = ? AND view = ?`, [op.id, op.view.name]);
288
+ const version = result?.version ?? 1;
289
+ const fullData = result?.data ? JSON.parse(result.data) : op.value;
290
+ syncOps.push({
291
+ ...op,
292
+ value: fullData,
293
+ version,
294
+ timestamp
295
+ });
296
+ }
297
+ if (op.kind === "remove" && !!op.id) {
298
+ const version = ((await db.getFirstAsync(`SELECT version FROM views WHERE id = ? AND view = ?`, [op.id, op.view.name]))?.version ?? 0) + 1;
299
+ await db.runAsync(`DELETE FROM views WHERE id = ? AND view = ?;`, [op.id, op.view.name]);
300
+ await db.runAsync(`DELETE FROM relations
301
+ WHERE (parent = ? AND pid = ?)
302
+ OR (child = ? AND cid = ?);`, [
303
+ op.view.name,
304
+ op.id,
305
+ op.view.name,
306
+ op.id
307
+ ]);
308
+ syncOps.push({
309
+ ...op,
310
+ version,
311
+ timestamp
312
+ });
313
+ }
314
+ if (op.kind === "link") {
315
+ await db.runAsync(`INSERT INTO relations (parent, pid, child, cid) VALUES (?, ?, ?, ?);`, [
316
+ op.parent.view.name,
317
+ op.parent.id,
318
+ op.child.view.name,
319
+ op.child.id
320
+ ]);
321
+ syncOps.push({
322
+ ...op,
323
+ version: 0,
324
+ timestamp
325
+ });
326
+ }
327
+ if (op.kind === "unlink") {
328
+ await db.runAsync(`DELETE FROM relations WHERE parent = ? AND pid = ? AND child = ? AND cid = ?;`, [
329
+ op.parent.view.name,
330
+ op.parent.id,
331
+ op.child.view.name,
332
+ op.child.id
333
+ ]);
334
+ syncOps.push({
335
+ ...op,
336
+ version: 0,
337
+ timestamp
338
+ });
339
+ }
340
+ }
341
+ }).catch((err) => {
342
+ console.error(err);
343
+ throw err;
344
+ });
345
+ return syncOps;
346
+ }
347
+
348
+ //#endregion
349
+ export { useCommit };
@@ -0,0 +1,17 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let react = require("react");
3
+
4
+ //#region src/local/config.ts
5
+ const Context = (0, react.createContext)({
6
+ subscribers: /* @__PURE__ */ new Map(),
7
+ authReadyPromise: Promise.resolve()
8
+ });
9
+ function useConfig() {
10
+ const ctx = (0, react.useContext)(Context);
11
+ if (!ctx) throw new Error("silosdk <Provider> is missing above this component");
12
+ return ctx;
13
+ }
14
+
15
+ //#endregion
16
+ exports.Context = Context;
17
+ exports.useConfig = useConfig;
@@ -0,0 +1,15 @@
1
+ import { createContext, useContext } from "react";
2
+
3
+ //#region src/local/config.ts
4
+ const Context = createContext({
5
+ subscribers: /* @__PURE__ */ new Map(),
6
+ authReadyPromise: Promise.resolve()
7
+ });
8
+ function useConfig() {
9
+ const ctx = useContext(Context);
10
+ if (!ctx) throw new Error("silosdk <Provider> is missing above this component");
11
+ return ctx;
12
+ }
13
+
14
+ //#endregion
15
+ export { Context, useConfig };
@@ -0,0 +1,16 @@
1
+ const require_schema_index = require('../schema/index.cjs');
2
+ const require_cloudflare = require('./adapters/cloudflare.cjs');
3
+ const require_view = require('./view.cjs');
4
+ const require_auth_context = require('./auth-context.cjs');
5
+ const require_provider = require('./provider.cjs');
6
+ const require_storage = require('./storage.cjs');
7
+ const require_commit = require('./commit.cjs');
8
+
9
+ exports.CloudflareSyncAdapter = require_cloudflare.CloudflareSyncAdapter;
10
+ exports.Provider = require_provider.Provider;
11
+ exports.useAssetUrl = require_storage.useAssetUrl;
12
+ exports.useAuth = require_auth_context.useAuth;
13
+ exports.useCommit = require_commit.useCommit;
14
+ exports.useListView = require_view.useListView;
15
+ exports.useView = require_view.useView;
16
+ exports.view = require_schema_index.view;
@@ -0,0 +1,10 @@
1
+ import { Provider } from "./provider.cjs";
2
+ import { Infer, View, view } from "../schema/index.cjs";
3
+ import { useListView, useView } from "./view.cjs";
4
+ import { MergeFn, SyncAdapter, SyncOp, SyncPayload } from "./sync.cjs";
5
+ import { useCommit } from "./commit.cjs";
6
+ import { AuthState, AuthUser } from "./auth.cjs";
7
+ import { useAuth } from "./auth-context.cjs";
8
+ import { useAssetUrl } from "./storage.cjs";
9
+ import { CloudflareSyncAdapter } from "./adapters/cloudflare.cjs";
10
+ export { type AuthState, type AuthUser, CloudflareSyncAdapter, type Infer, type MergeFn, Provider, type SyncAdapter, type SyncOp, type SyncPayload, type View, useAssetUrl, useAuth, useCommit, useListView, useView, view };
@@ -0,0 +1,10 @@
1
+ import { Provider } from "./provider.mjs";
2
+ import { Infer, View, view } from "../schema/index.mjs";
3
+ import { useListView, useView } from "./view.mjs";
4
+ import { MergeFn, SyncAdapter, SyncOp, SyncPayload } from "./sync.mjs";
5
+ import { useCommit } from "./commit.mjs";
6
+ import { AuthState, AuthUser } from "./auth.mjs";
7
+ import { useAuth } from "./auth-context.mjs";
8
+ import { useAssetUrl } from "./storage.mjs";
9
+ import { CloudflareSyncAdapter } from "./adapters/cloudflare.mjs";
10
+ export { type AuthState, type AuthUser, CloudflareSyncAdapter, type Infer, type MergeFn, Provider, type SyncAdapter, type SyncOp, type SyncPayload, type View, useAssetUrl, useAuth, useCommit, useListView, useView, view };
@@ -0,0 +1,9 @@
1
+ import { view } from "../schema/index.mjs";
2
+ import { CloudflareSyncAdapter } from "./adapters/cloudflare.mjs";
3
+ import { useListView, useView } from "./view.mjs";
4
+ import { useAuth } from "./auth-context.mjs";
5
+ import { Provider } from "./provider.mjs";
6
+ import { useAssetUrl } from "./storage.mjs";
7
+ import { useCommit } from "./commit.mjs";
8
+
9
+ export { CloudflareSyncAdapter, Provider, useAssetUrl, useAuth, useCommit, useListView, useView, view };
@@ -0,0 +1,204 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ const require_sync = require('./sync.cjs');
3
+ const require_cloudflare = require('./adapters/cloudflare.cjs');
4
+ const require_auth = require('./auth.cjs');
5
+ const require_config = require('./config.cjs');
6
+ const require_view = require('./view.cjs');
7
+ const require_auth_context = require('./auth-context.cjs');
8
+ let react = require("react");
9
+ let expo_sqlite = require("expo-sqlite");
10
+ let react_jsx_runtime = require("react/jsx-runtime");
11
+
12
+ //#region src/local/provider.tsx
13
+ function Provider(props) {
14
+ const { config } = props;
15
+ const subscribers = (0, react.useRef)(/* @__PURE__ */ new Map()).current;
16
+ const url = config?.url;
17
+ const prodUrl = config?.prodUrl;
18
+ const [user, setUser] = (0, react.useState)(null);
19
+ const [isLoading, setIsLoading] = (0, react.useState)(true);
20
+ const [authReady, setAuthReady] = (0, react.useState)(false);
21
+ const tokenRef = (0, react.useRef)(null);
22
+ const authReadyPromiseRef = (0, react.useRef)(null);
23
+ const authReadyResolveRef = (0, react.useRef)(null);
24
+ if (!authReadyPromiseRef.current) authReadyPromiseRef.current = new Promise((resolve) => {
25
+ authReadyResolveRef.current = resolve;
26
+ });
27
+ const resolvedGetToken = config?.getToken ? config.getToken : config?.url ? () => require_auth.getStoredSessionToken(config?.url) : void 0;
28
+ const dbName = `${config?.name ?? "app"}.db`;
29
+ (0, react.useEffect)(() => {
30
+ require_auth.setAuthProdUrl(prodUrl ?? null);
31
+ }, [prodUrl]);
32
+ (0, react.useEffect)(() => {
33
+ if (!url) {
34
+ setIsLoading(false);
35
+ setAuthReady(true);
36
+ return;
37
+ }
38
+ (async () => {
39
+ try {
40
+ const token = await require_auth.getStoredSessionToken(url);
41
+ if (token) {
42
+ tokenRef.current = token;
43
+ const current = await require_auth.fetchCurrentUser(url, token);
44
+ if (current.user) setUser(current.user);
45
+ else {
46
+ await require_auth.clearStoredSessionToken(url);
47
+ tokenRef.current = null;
48
+ }
49
+ }
50
+ } catch (err) {
51
+ console.error("[silosdk] Auth init failed:", err);
52
+ } finally {
53
+ setIsLoading(false);
54
+ setAuthReady(true);
55
+ }
56
+ })();
57
+ }, [url]);
58
+ (0, react.useEffect)(() => {
59
+ if (authReady) authReadyResolveRef.current?.();
60
+ }, [authReady]);
61
+ const signIn = (0, react.useCallback)(async (opts) => {
62
+ if (!url) throw new Error("No URL configured");
63
+ await require_auth.requestOtp(url, opts.email);
64
+ }, [url]);
65
+ const verifyOtp = (0, react.useCallback)(async (opts) => {
66
+ if (!url) throw new Error("No URL configured");
67
+ const result = await require_auth.verifyOtpWithServer(url, opts);
68
+ tokenRef.current = result.token;
69
+ setUser(result.user);
70
+ require_auth.authEvents.emit("token-changed");
71
+ }, [url]);
72
+ const signOut = (0, react.useCallback)(async () => {
73
+ if (!url) return;
74
+ const token = tokenRef.current;
75
+ if (token) await require_auth.signOutFromServer(url, token);
76
+ await require_auth.clearStoredSessionToken(url);
77
+ tokenRef.current = null;
78
+ setUser(null);
79
+ require_auth.authEvents.emit("token-changed");
80
+ }, [url]);
81
+ (0, react.useEffect)(() => {
82
+ if (!url) return;
83
+ const handleUnauthorized = () => {
84
+ tokenRef.current = null;
85
+ setUser(null);
86
+ };
87
+ require_auth.authEvents.on("unauthorized", handleUnauthorized);
88
+ return () => require_auth.authEvents.off("unauthorized", handleUnauthorized);
89
+ }, [url]);
90
+ const authState = {
91
+ user,
92
+ isLoading,
93
+ signIn,
94
+ verifyOtp,
95
+ signOut
96
+ };
97
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_config.Context.Provider, {
98
+ value: {
99
+ subscribers,
100
+ url: config?.url,
101
+ getToken: resolvedGetToken,
102
+ authReady,
103
+ authReadyPromise: authReadyPromiseRef.current ?? Promise.resolve()
104
+ },
105
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(require_auth_context.AuthContext.Provider, {
106
+ value: authState,
107
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(expo_sqlite.SQLiteProvider, {
108
+ databaseName: dbName,
109
+ onInit: async (db) => {
110
+ await db.execAsync(`
111
+ PRAGMA journal_mode = WAL;
112
+ CREATE TABLE IF NOT EXISTS views (
113
+ id TEXT PRIMARY KEY,
114
+ view TEXT,
115
+ data BLOB,
116
+ createdAt TEXT,
117
+ updatedAt TEXT,
118
+ version INTEGER DEFAULT 0
119
+ );
120
+ CREATE TABLE IF NOT EXISTS relations (
121
+ parent TEXT,
122
+ pid TEXT,
123
+ child TEXT,
124
+ cid TEXT
125
+ );
126
+ `);
127
+ },
128
+ options: { enableChangeListener: true },
129
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SyncManagerWithConfig, {
130
+ config,
131
+ subscribers
132
+ }), props.children]
133
+ })
134
+ })
135
+ });
136
+ }
137
+ function SyncManagerWithConfig({ config, subscribers }) {
138
+ const db = (0, expo_sqlite.useSQLiteContext)();
139
+ const adapterRef = (0, react.useRef)(void 0);
140
+ const [isAuthenticated, setIsAuthenticated] = (0, react.useState)(null);
141
+ const checkAuth = (0, react.useCallback)(async () => {
142
+ if (!config?.url) {
143
+ setIsAuthenticated(false);
144
+ return;
145
+ }
146
+ const token = await (config.getToken ? config.getToken : () => require_auth.getStoredSessionToken(config.url))();
147
+ if (!token) {
148
+ setIsAuthenticated(false);
149
+ return;
150
+ }
151
+ try {
152
+ const res = await fetch(`${config.url}/auth/me`, { headers: { Authorization: `Bearer ${token}` } });
153
+ if (res.ok) setIsAuthenticated(!(await res.json()).isAnonymous);
154
+ else setIsAuthenticated(false);
155
+ } catch {
156
+ setIsAuthenticated(false);
157
+ }
158
+ }, [config?.url, config?.getToken]);
159
+ (0, react.useEffect)(() => {
160
+ checkAuth();
161
+ }, [checkAuth]);
162
+ (0, react.useEffect)(() => {
163
+ const handleTokenChanged = () => {
164
+ require_view.getQueryStoreInstance().clear();
165
+ checkAuth();
166
+ adapterRef.current?.resetConnection();
167
+ };
168
+ require_auth.authEvents.on("token-changed", handleTokenChanged);
169
+ return () => {
170
+ require_auth.authEvents.off("token-changed", handleTokenChanged);
171
+ };
172
+ }, [checkAuth]);
173
+ (0, react.useEffect)(() => {
174
+ if (!config?.url || isAuthenticated !== true) return;
175
+ const resolvedGetToken = config.getToken ? config.getToken : () => require_auth.getStoredSessionToken(config.url);
176
+ const adapter = new require_cloudflare.CloudflareSyncAdapter({
177
+ url: config.url,
178
+ getToken: resolvedGetToken,
179
+ db,
180
+ subscribers
181
+ });
182
+ adapterRef.current = adapter;
183
+ let mounted = true;
184
+ adapter.onReceive(async (payload) => {
185
+ if (mounted) await require_sync.applyRemoteOps(db, payload.ops, subscribers, adapter.merge);
186
+ });
187
+ adapter.connect();
188
+ return () => {
189
+ mounted = false;
190
+ adapter.disconnect();
191
+ adapterRef.current = void 0;
192
+ };
193
+ }, [
194
+ config?.url,
195
+ config?.getToken,
196
+ db,
197
+ subscribers,
198
+ isAuthenticated
199
+ ]);
200
+ return null;
201
+ }
202
+
203
+ //#endregion
204
+ exports.Provider = Provider;
@@ -0,0 +1,25 @@
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+ import { ReactNode } from "react";
3
+
4
+ //#region src/local/provider.d.ts
5
+ type ProviderConfig = {
6
+ /** SQLite database name. Defaults to 'app' (.db appended internally). */
7
+ name?: string;
8
+ /** CF Worker base URL. Enables DO sync + public D1 queries when present. */
9
+ url?: string;
10
+ /** Optional production base URL used for keying SecureStore. */
11
+ prodUrl?: string;
12
+ /**
13
+ * BYO auth: returns the bearer token for HTTP requests and WS auth.
14
+ * When omitted and `url` is set, the built-in auth session token is read
15
+ * automatically from expo-secure-store (Phase 4 built-in auth mode).
16
+ * Return null to send no Authorization header.
17
+ */
18
+ getToken?: () => Promise<string | null>;
19
+ };
20
+ declare function Provider(props: {
21
+ config?: ProviderConfig;
22
+ children: ReactNode;
23
+ }): react_jsx_runtime0.JSX.Element;
24
+ //#endregion
25
+ export { Provider };
@@ -0,0 +1,25 @@
1
+ import { ReactNode } from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/local/provider.d.ts
5
+ type ProviderConfig = {
6
+ /** SQLite database name. Defaults to 'app' (.db appended internally). */
7
+ name?: string;
8
+ /** CF Worker base URL. Enables DO sync + public D1 queries when present. */
9
+ url?: string;
10
+ /** Optional production base URL used for keying SecureStore. */
11
+ prodUrl?: string;
12
+ /**
13
+ * BYO auth: returns the bearer token for HTTP requests and WS auth.
14
+ * When omitted and `url` is set, the built-in auth session token is read
15
+ * automatically from expo-secure-store (Phase 4 built-in auth mode).
16
+ * Return null to send no Authorization header.
17
+ */
18
+ getToken?: () => Promise<string | null>;
19
+ };
20
+ declare function Provider(props: {
21
+ config?: ProviderConfig;
22
+ children: ReactNode;
23
+ }): react_jsx_runtime0.JSX.Element;
24
+ //#endregion
25
+ export { Provider };