@voltro/react-native 0.81.0 → 0.83.0

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/dist/index.js CHANGED
@@ -1,71 +1,8 @@
1
1
  import { a as e, i as t, n, r, t as i } from "./devices-BjXAU-8I.js";
2
- import { useCallback as a, useEffect as o, useReducer as s, useRef as c, useState as l } from "react";
3
- import { buildApiRuntime as u, recoveryCoordinatorFor as d, startApiSupervisor as f } from "@voltro/client";
4
- //#region src/deepLink.ts
5
- var p = (e) => {
6
- let t = e.pattern;
7
- if (t.length === 0 || t[0] !== "/") throw Error(`defineDeepLink: pattern must start with "/" (got "${t}")`);
8
- return {
9
- _tag: "VoltroDeepLink",
10
- pattern: t,
11
- handler: e.handler
12
- };
13
- }, m = (e) => {
14
- let t = e, n = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\//.exec(t);
15
- if (n) {
16
- let e = t.slice(n[0].length), r = n[1].toLowerCase();
17
- if (r === "http" || r === "https") {
18
- let n = e.indexOf("/");
19
- t = n === -1 ? "/" : e.slice(n);
20
- } else t = `/${e}`;
21
- }
22
- let r = t.search(/[?#]/);
23
- return r !== -1 && (t = t.slice(0, r)), t.split("/").filter((e) => e.length > 0).map((e) => {
24
- try {
25
- return decodeURIComponent(e);
26
- } catch {
27
- return e;
28
- }
29
- });
30
- }, h = (e, t) => {
31
- let n = m(e), r = m(t);
32
- if (n.length !== r.length) return null;
33
- let i = {};
34
- for (let e = 0; e < n.length; e++) {
35
- let t = n[e], a = r[e];
36
- if (t.startsWith(":")) {
37
- let e = t.slice(1);
38
- if (e.length === 0) return null;
39
- i[e] = a;
40
- } else if (t !== "*" && t !== a) return null;
41
- }
42
- return i;
43
- }, g = (e, t) => {
44
- let n = h(e.pattern, t);
45
- return n === null ? null : (e.handler(n), n);
46
- }, _ = (e, t) => {
47
- let n = e.split("/").map((n) => {
48
- if (!n.startsWith(":")) return n;
49
- let r = n.slice(1), i = t[r];
50
- if (i === void 0) throw Error(`deepLinkHref: pattern "${e}" needs a ":${r}" param`);
51
- return encodeURIComponent(i);
52
- }).join("/");
53
- return n.length === 0 ? "/" : n;
54
- }, v = (e, t) => {
55
- for (let n of e) {
56
- let e = h(n.pattern, t);
57
- if (e !== null) return {
58
- descriptor: n,
59
- params: e
60
- };
61
- }
62
- return null;
63
- }, y = (e, t) => {
64
- let n = v(e, t);
65
- if (n === null) return null;
66
- let r = n.descriptor.handler;
67
- return r(n.params), n.params;
68
- }, b = {
2
+ import { browserOnlineSource as a, browserOnlineSource as o, buildApiRuntime as s, deepLinkHref as c, defineDeepLink as l, definePlatform as u, dispatchDeepLink as d, matchDeepLink as f, matchFirstDeepLink as p, recoveryCoordinatorFor as m, runDeepLink as h, startApiSupervisor as g } from "@voltro/client";
3
+ import { useCallback as _, useEffect as v, useReducer as y, useRef as b, useState as x } from "react";
4
+ //#region src/backgroundSync.ts
5
+ var S = {
69
6
  status: "idle",
70
7
  isForeground: !0,
71
8
  lastSyncAt: void 0,
@@ -73,7 +10,7 @@ var p = (e) => {
73
10
  lastError: void 0,
74
11
  successCount: 0,
75
12
  failureCount: 0
76
- }, x = (e, t) => {
13
+ }, C = (e, t) => {
77
14
  switch (t.type) {
78
15
  case "foreground": return e.isForeground ? e : {
79
16
  ...e,
@@ -104,11 +41,11 @@ var p = (e) => {
104
41
  failureCount: e.failureCount + 1
105
42
  };
106
43
  case "reset": return {
107
- ...b,
44
+ ...S,
108
45
  isForeground: e.isForeground
109
46
  };
110
47
  }
111
- }, S = (e, t, n, r = !1) => !(!n.enabled || e.status === "syncing" || !r && !e.isForeground || !r && n.intervalMs > 0 && e.lastAttemptAt !== void 0 && t - e.lastAttemptAt < n.intervalMs), C = (e, t) => {
48
+ }, w = (e, t, n, r = !1) => !(!n.enabled || e.status === "syncing" || !r && !e.isForeground || !r && n.intervalMs > 0 && e.lastAttemptAt !== void 0 && t - e.lastAttemptAt < n.intervalMs), T = (e, t) => {
112
49
  if (typeof document > "u" || typeof window > "u") return () => {};
113
50
  let n = () => {
114
51
  document.visibilityState === "visible" ? e() : t();
@@ -116,58 +53,58 @@ var p = (e) => {
116
53
  return document.addEventListener("visibilitychange", n), window.addEventListener("focus", e), () => {
117
54
  document.removeEventListener("visibilitychange", n), window.removeEventListener("focus", e);
118
55
  };
119
- }, w = 3e5, T = (e, t = {}) => {
120
- let n = t.intervalMs ?? w, r = t.syncOnForeground ?? !0, i = t.enabled ?? !0, l = t.now ?? Date.now, u = t.subscribeForeground ?? C, [d, f] = s(x, b), p = c(e);
121
- p.current = e;
122
- let m = c(d);
123
- m.current = d;
124
- let h = c(!0), g = a((e) => {
125
- S(m.current, l(), {
56
+ }, E = 3e5, D = (e, t = {}) => {
57
+ let n = t.intervalMs ?? E, r = t.syncOnForeground ?? !0, i = t.enabled ?? !0, a = t.now ?? Date.now, o = t.subscribeForeground ?? T, [s, c] = y(C, S), l = b(e);
58
+ l.current = e;
59
+ let u = b(s);
60
+ u.current = s;
61
+ let d = b(!0), f = _((e) => {
62
+ w(u.current, a(), {
126
63
  intervalMs: n,
127
64
  enabled: i
128
- }, e) && (f({
65
+ }, e) && (c({
129
66
  type: "syncStarted",
130
- at: l()
131
- }), Promise.resolve().then(() => p.current()).then(() => {
132
- h.current && f({
67
+ at: a()
68
+ }), Promise.resolve().then(() => l.current()).then(() => {
69
+ d.current && c({
133
70
  type: "syncSucceeded",
134
- at: l()
71
+ at: a()
135
72
  });
136
73
  }).catch((e) => {
137
- h.current && f({
74
+ d.current && c({
138
75
  type: "syncFailed",
139
- at: l(),
76
+ at: a(),
140
77
  error: e instanceof Error ? e.message : String(e)
141
78
  });
142
79
  }));
143
80
  }, [
144
81
  i,
145
82
  n,
146
- l
83
+ a
147
84
  ]);
148
- o(() => (h.current = !0, () => {
149
- h.current = !1;
150
- }), []), o(() => u(() => {
151
- f({ type: "foreground" }), r && g(!0);
152
- }, () => f({ type: "background" })), [
153
- u,
85
+ v(() => (d.current = !0, () => {
86
+ d.current = !1;
87
+ }), []), v(() => o(() => {
88
+ c({ type: "foreground" }), r && f(!0);
89
+ }, () => c({ type: "background" })), [
90
+ o,
154
91
  r,
155
- g
156
- ]), o(() => {
92
+ f
93
+ ]), v(() => {
157
94
  if (!i || n <= 0) return;
158
- let e = setInterval(() => g(!1), n);
95
+ let e = setInterval(() => f(!1), n);
159
96
  return () => clearInterval(e);
160
97
  }, [
161
98
  i,
162
99
  n,
163
- g
100
+ f
164
101
  ]);
165
- let _ = a(() => g(!0), [g]);
102
+ let p = _(() => f(!0), [f]);
166
103
  return {
167
- ...d,
168
- sync: _
104
+ ...s,
105
+ sync: p
169
106
  };
170
- }, E = {
107
+ }, O = {
171
108
  localFirst: !0,
172
109
  optimistic: !0,
173
110
  syncOnForeground: !0,
@@ -179,16 +116,7 @@ var p = (e) => {
179
116
  15e3,
180
117
  6e4
181
118
  ]
182
- }, D = (e, t) => e ? t > 0 ? "degraded" : "connected" : "offline", O = {
183
- read: () => typeof navigator > "u" || navigator.onLine,
184
- subscribe: (e) => {
185
- if (typeof window > "u") return () => {};
186
- let t = () => e(!0), n = () => e(!1);
187
- return window.addEventListener("online", t), window.addEventListener("offline", n), () => {
188
- window.removeEventListener("online", t), window.removeEventListener("offline", n);
189
- };
190
- }
191
- }, k = (e) => {
119
+ }, k = (e, t) => e ? t > 0 ? "degraded" : "connected" : "offline", A = (e) => {
192
120
  let t = (e) => typeof e.isInternetReachable == "boolean" ? e.isInternetReachable : e.isConnected === !0, n = !0;
193
121
  return {
194
122
  read: () => n,
@@ -201,27 +129,27 @@ var p = (e) => {
201
129
  }).catch(() => {}), i;
202
130
  }
203
131
  };
204
- }, A = (e) => {
205
- let t = e?.onlineSource ?? O, [n, r] = l(() => t.read()), [i, s] = l(0), [u, d] = l(void 0), f = c(!0);
206
- o(() => (f.current = !0, () => {
207
- f.current = !1;
208
- }), []), o(() => t.subscribe((e) => {
209
- r(e), e && s(0);
132
+ }, j = (e) => {
133
+ let t = e?.onlineSource ?? o, [n, r] = x(() => t.read()), [i, a] = x(0), [s, c] = x(void 0), l = b(!0);
134
+ v(() => (l.current = !0, () => {
135
+ l.current = !1;
136
+ }), []), v(() => t.subscribe((e) => {
137
+ r(e), e && a(0);
210
138
  }), [t]);
211
- let p = a(() => {
212
- f.current && (s((e) => e + 1), d(Date.now()));
213
- }, []), m = a(() => {
214
- f.current && s(0);
139
+ let u = _(() => {
140
+ l.current && (a((e) => e + 1), c(Date.now()));
141
+ }, []), d = _(() => {
142
+ l.current && a(0);
215
143
  }, []);
216
144
  return {
217
- status: D(n, i),
145
+ status: k(n, i),
218
146
  online: n,
219
147
  failureCount: i,
220
- lastFailureAt: u,
221
- reportFailure: p,
222
- reportSuccess: m
148
+ lastFailureAt: s,
149
+ reportFailure: u,
150
+ reportSuccess: d
223
151
  };
224
- }, j = (e) => {
152
+ }, M = (e) => {
225
153
  let { storage: t, keys: n } = e, r = e.onError ?? ((e, t) => {
226
154
  console.warn(`[voltro] persisting '${e}' failed`, t);
227
155
  }), i = /* @__PURE__ */ new Map(), a = !1, o = null, s = /* @__PURE__ */ new Map(), c = null, l = async () => {
@@ -280,16 +208,16 @@ var p = (e) => {
280
208
  },
281
209
  keys: () => [...i.keys()]
282
210
  };
283
- }, M = (e, t) => {
211
+ }, N = (e, t) => {
284
212
  let n = globalThis.WebSocket;
285
213
  if (n === void 0) throw Error("startMobileApis: no global WebSocket. React Native provides one; if you are running these bindings somewhere else, pass `webSocketConstructor`.");
286
214
  return t === void 0 ? new n(e) : new n(e, t);
287
- }, N = (e, t, n) => (r, i) => {
215
+ }, P = (e, t, n) => (r, i) => {
288
216
  let a = e(r, i);
289
217
  return a.addEventListener("open", () => t()), a.addEventListener("close", () => n("close")), a.addEventListener("error", () => n("error")), a;
290
- }, P = (e) => {
291
- let t = e.webSocketConstructor ?? M;
292
- return f({
218
+ }, F = (e) => {
219
+ let t = e.webSocketConstructor ?? N;
220
+ return g({
293
221
  apis: e.apis.map((e) => ({
294
222
  name: e.name,
295
223
  group: e.group,
@@ -300,7 +228,7 @@ var p = (e) => {
300
228
  })),
301
229
  ...e.retryDelayMs === void 0 ? {} : { retryDelayMs: e.retryDelayMs },
302
230
  onChange: e.onChange,
303
- buildClient: async (e, n, r, i, a, o) => await u({
231
+ buildClient: async (e, n, r, i, a, o) => await s({
304
232
  name: e.name,
305
233
  generation: n,
306
234
  wsUrl: e.wsUrl,
@@ -308,13 +236,13 @@ var p = (e) => {
308
236
  ...e.headers === void 0 ? {} : { headers: e.headers },
309
237
  ...a === void 0 ? {} : { errorBus: a },
310
238
  ...o === void 0 ? {} : { recovery: o },
311
- webSocketConstructor: N(t, r, i)
239
+ webSocketConstructor: P(t, r, i)
312
240
  })
313
241
  });
314
- }, F = (e, t, n = "/ws") => {
242
+ }, I = (e, t, n = "/ws") => {
315
243
  let r = (e ?? "").split(":")[0];
316
244
  return r === void 0 || r === "" || r === "localhost" || r === "127.0.0.1" ? `ws://localhost:${t}${n}` : `ws://${r}:${t}${n}`;
317
- }, I = (e, t) => {
245
+ }, L = (e, t) => {
318
246
  let n = /* @__PURE__ */ new Map();
319
247
  for (let r of e) {
320
248
  let e = t.get(r.name);
@@ -325,10 +253,164 @@ var p = (e) => {
325
253
  descriptors: r.descriptors,
326
254
  inspectBaseUrl: r.wsUrl.replace(/^ws/, "http").replace(/\/ws$/, ""),
327
255
  errorBus: e.errorBus,
328
- recovery: e.recovery ?? d(e.errorBus, r.name, r.recovery)
256
+ recovery: e.recovery ?? m(e.errorBus, r.name, r.recovery)
329
257
  });
330
258
  }
331
259
  return n;
260
+ }, R = (e) => e === "ios" ? "ios" : e === "android" ? "android" : e === "web" ? "web" : "unknown", z = (e) => e === "active" ? "active" : e === "background" ? "background" : "inactive", B = (e) => e === "granted" ? "granted" : e === "denied" ? "denied" : "prompt", V = (e) => ({
261
+ kind: "keychain",
262
+ get: (t) => e.getItemAsync(t),
263
+ set: (t, n) => e.setItemAsync(t, n),
264
+ delete: (t) => e.deleteItemAsync(t)
265
+ }), H = (e) => ({
266
+ read: () => z(e.currentState),
267
+ subscribe: (t) => {
268
+ let n = e.addEventListener("change", (e) => t(z(e)));
269
+ return () => n.remove();
270
+ }
271
+ }), U = (e) => ({
272
+ initialUrl: () => e.getInitialURL(),
273
+ subscribe: (t) => {
274
+ let n = e.addEventListener("url", ({ url: e }) => t(e));
275
+ return () => n.remove();
276
+ }
277
+ }), W = (e, t) => ({
278
+ permission: async () => B((await e.getPermissionsAsync()).status),
279
+ requestPermission: async () => B((await e.requestPermissionsAsync()).status),
280
+ ...e.getExpoPushTokenAsync === void 0 ? {} : { pushToken: async () => (await e.getExpoPushTokenAsync(t === void 0 ? void 0 : { projectId: t })).data },
281
+ ...e.scheduleNotificationAsync === void 0 ? {} : { show: async (t) => {
282
+ await e.scheduleNotificationAsync({
283
+ content: {
284
+ title: t.title,
285
+ ...t.body === void 0 ? {} : { body: t.body },
286
+ ...t.data === void 0 ? {} : { data: { ...t.data } }
287
+ },
288
+ trigger: null
289
+ });
290
+ } }
291
+ }), G = (e) => ({
292
+ check: async () => {
293
+ let t = await e.checkForUpdateAsync();
294
+ return t.isAvailable ? { version: t.manifest?.id ?? "available" } : null;
295
+ },
296
+ apply: async () => {
297
+ await e.fetchUpdateAsync(), await e.reloadAsync();
298
+ }
299
+ }), K = (e) => ({
300
+ available: async () => await e.hasHardwareAsync() && await e.isEnrolledAsync(),
301
+ authenticate: async (t) => (await e.authenticateAsync({ promptMessage: t })).success
302
+ }), q = async (e) => {
303
+ let t = M({
304
+ storage: e.storage,
305
+ ...e.persistedKeys === void 0 ? {} : { keys: e.persistedKeys }
306
+ });
307
+ return await t.hydrate(), {
308
+ adapter: u({
309
+ platform: {
310
+ kind: R(e.os),
311
+ shell: "expo",
312
+ installed: !0,
313
+ ...e.appVersion === void 0 ? {} : { appVersion: e.appVersion },
314
+ ...e.updates?.updateId ? { buildId: e.updates.updateId } : {}
315
+ },
316
+ webSocket: e.webSocket ?? N,
317
+ storage: t.provider,
318
+ secureStorage: V(e.secureStore),
319
+ online: A(e.netInfo),
320
+ lifecycle: H(e.appState),
321
+ links: U(e.linking),
322
+ ...e.notifications === void 0 ? {} : { notifications: W(e.notifications, e.expoProjectId) },
323
+ ...e.updates === void 0 ? {} : { updates: G(e.updates) },
324
+ ...e.localAuthentication === void 0 ? {} : { biometrics: K(e.localAuthentication) },
325
+ clock: { now: () => Date.now() },
326
+ ...e.extensions === void 0 ? {} : { extensions: e.extensions },
327
+ ...e.capabilities === void 0 ? {} : { capabilities: e.capabilities }
328
+ }),
329
+ persistence: t
330
+ };
331
+ }, J = "\nCREATE TABLE IF NOT EXISTS voltro_crdt (key TEXT PRIMARY KEY NOT NULL, state BLOB NOT NULL);\nCREATE TABLE IF NOT EXISTS voltro_queue (id INTEGER PRIMARY KEY CHECK (id = 1), json TEXT NOT NULL);\nCREATE TABLE IF NOT EXISTS voltro_pins (key TEXT PRIMARY KEY NOT NULL);\nCREATE TABLE IF NOT EXISTS voltro_kv (key TEXT PRIMARY KEY NOT NULL, json TEXT NOT NULL);\n", Y = (e, t) => `${e}\u0001${t}`, X = (e) => {
332
+ if (e instanceof Uint8Array) return new Uint8Array(e);
333
+ if (ArrayBuffer.isView(e)) return new Uint8Array(e.buffer, e.byteOffset, e.byteLength).slice();
334
+ if (e instanceof ArrayBuffer) return new Uint8Array(e.slice(0));
335
+ throw Error("expo-sqlite persistence: CRDT state column is not a BLOB");
336
+ }, Z = (e) => `${e.replace(/[\\%_]/g, (e) => `\\${e}`)}%`, Q = async (e) => {
337
+ let t = async () => {
338
+ await e.execAsync(J);
339
+ };
340
+ return await t(), {
341
+ persistence: {
342
+ kind: "expo-sqlite",
343
+ loadCrdtState: async (t, n) => {
344
+ let r = await e.getFirstAsync("SELECT state FROM voltro_crdt WHERE key = ?", [Y(t, n)]);
345
+ return r === null ? void 0 : X(r.state);
346
+ },
347
+ saveCrdtState: async (t, n, r) => {
348
+ await e.runAsync("INSERT OR REPLACE INTO voltro_crdt (key, state) VALUES (?, ?)", [Y(t, n), new Uint8Array(r)]);
349
+ },
350
+ loadQueue: async () => {
351
+ let t = await e.getFirstAsync("SELECT json FROM voltro_queue WHERE id = 1");
352
+ if (t === null) return [];
353
+ try {
354
+ return JSON.parse(t.json);
355
+ } catch {
356
+ return [];
357
+ }
358
+ },
359
+ saveQueue: async (t) => {
360
+ await e.runAsync("INSERT OR REPLACE INTO voltro_queue (id, json) VALUES (1, ?)", [JSON.stringify(t)]);
361
+ },
362
+ pin: async (t, n) => {
363
+ await e.runAsync("INSERT OR IGNORE INTO voltro_pins (key) VALUES (?)", [Y(t, n)]);
364
+ },
365
+ isPinned: async (t, n) => await e.getFirstAsync("SELECT key FROM voltro_pins WHERE key = ?", [Y(t, n)]) !== null
366
+ },
367
+ kv: {
368
+ kind: "expo-sqlite",
369
+ get: async (t) => {
370
+ let n = await e.getFirstAsync("SELECT json FROM voltro_kv WHERE key = ?", [t]);
371
+ if (n !== null) try {
372
+ return JSON.parse(n.json);
373
+ } catch {
374
+ return;
375
+ }
376
+ },
377
+ put: async (t, n) => {
378
+ await e.runAsync("INSERT OR REPLACE INTO voltro_kv (key, json) VALUES (?, ?)", [t, JSON.stringify(n)]);
379
+ },
380
+ del: async (t) => {
381
+ await e.runAsync("DELETE FROM voltro_kv WHERE key = ?", [t]);
382
+ },
383
+ keysWithPrefix: async (t) => (await e.getAllAsync("SELECT key FROM voltro_kv WHERE key LIKE ? ESCAPE '\\'", [Z(t)])).map((e) => e.key)
384
+ },
385
+ migrate: t
386
+ };
387
+ }, $ = "voltro-background-sync", ee = (e) => {
388
+ let t = e.name ?? "voltro-background-sync", n = e.backgroundFetch.BackgroundFetchResult, r = async () => {
389
+ try {
390
+ let t = await e.run() ?? "no-data";
391
+ return e.onOutcome?.(t), t;
392
+ } catch (t) {
393
+ return e.onOutcome?.("failed", t), "failed";
394
+ }
395
+ }, i = (e) => e === "new-data" ? n.NewData : e === "no-data" ? n.NoData : n.Failed;
396
+ return e.taskManager.defineTask(t, async ({ error: t }) => t ? (e.onOutcome?.("failed", t), n.Failed) : i(await r())), {
397
+ name: t,
398
+ register: async () => {
399
+ e.taskManager.isTaskRegisteredAsync && await e.taskManager.isTaskRegisteredAsync(t) || await e.backgroundFetch.registerTaskAsync(t, {
400
+ minimumInterval: e.minimumIntervalSeconds ?? 900,
401
+ stopOnTerminate: !(e.survivesTermination ?? !0),
402
+ startOnBoot: e.startOnBoot ?? !0
403
+ });
404
+ },
405
+ unregister: () => e.backgroundFetch.unregisterTaskAsync(t),
406
+ availability: async () => {
407
+ let t = e.backgroundFetch;
408
+ if (!t.getStatusAsync || !t.BackgroundFetchStatus) return "unknown";
409
+ let n = await t.getStatusAsync();
410
+ return n === t.BackgroundFetchStatus.Available ? "available" : n === t.BackgroundFetchStatus.Denied ? "denied" : n === t.BackgroundFetchStatus.Restricted ? "restricted" : "unknown";
411
+ },
412
+ runNow: r
413
+ };
332
414
  };
333
415
  //#endregion
334
- export { i as DEVICES_TABLE, n as DEVICE_PLATFORMS, x as backgroundSyncReducer, O as browserOnlineSource, j as createAsyncStoragePersistence, _ as deepLinkHref, M as defaultWebSocketConstructor, p as defineDeepLink, D as deriveConnectionStatus, y as dispatchDeepLink, b as initialBackgroundSyncState, r as isDevicePlatform, h as matchDeepLink, v as matchFirstDeepLink, k as netInfoOnlineSource, E as offlineFirstDefaults, t as registerDevice, F as resolveDevWsUrl, e as resolveDeviceRegistration, g as runDeepLink, S as shouldSync, P as startMobileApis, I as toApiHandles, T as useBackgroundSync, A as useMobileConnectionStatus, N as wireSocket };
416
+ export { $ as DEFAULT_BACKGROUND_SYNC_TASK, i as DEVICES_TABLE, n as DEVICE_PLATFORMS, H as appStateLifecycle, C as backgroundSyncReducer, a as browserOnlineSource, M as createAsyncStoragePersistence, q as createExpoPlatform, Q as createExpoSqliteLocalFirst, c as deepLinkHref, N as defaultWebSocketConstructor, ee as defineBackgroundSyncTask, l as defineDeepLink, k as deriveConnectionStatus, d as dispatchDeepLink, K as expoBiometrics, W as expoNotifications, V as expoSecureTokenStore, G as expoUpdates, S as initialBackgroundSyncState, r as isDevicePlatform, U as linkingDeepLinks, f as matchDeepLink, p as matchFirstDeepLink, A as netInfoOnlineSource, O as offlineFirstDefaults, t as registerDevice, I as resolveDevWsUrl, e as resolveDeviceRegistration, h as runDeepLink, w as shouldSync, F as startMobileApis, L as toApiHandles, D as useBackgroundSync, j as useMobileConnectionStatus, P as wireSocket };
package/dist/schema.d.ts CHANGED
@@ -1,7 +1,14 @@
1
1
  import { ColumnBuilder } from '@voltro/database';
2
+ import { DataStore } from '@voltro/database';
2
3
  import { FieldDefinitions } from '@voltro/database';
3
4
  import { Table } from '@voltro/database';
4
5
 
6
+ /** `localeFor` for `sendNotification`: the most recently seen device's locale. */
7
+ export declare const deviceLocaleFor: (store: DataStore) => (subjectId: string) => Promise<string | null>;
8
+
9
+ /** The push transport a device is reachable on. */
10
+ declare type DevicePlatform = 'ios' | 'android' | 'web';
11
+
5
12
  /** The framework table name devices are stored in — shared with `./schema`. */
6
13
  export declare const DEVICES_TABLE = "_voltro_devices";
7
14
 
@@ -19,4 +26,12 @@ export declare const devicesTable: Table<"_voltro_devices", FieldDefinitions<{
19
26
  readonly createdAt: ColumnBuilder<Date, "timestamp", true>;
20
27
  }>, true, "byDeviceUser" | "byDeviceTenant">;
21
28
 
29
+ /** `tokensFor` for `pushChannel`: every registered token of a subject, newest
30
+ * first. Optionally one platform (`'ios'`, `'android'`). */
31
+ export declare const deviceTokensFor: (store: DataStore, platform?: DevicePlatform) => (subjectId: string) => Promise<ReadonlyArray<string>>;
32
+
33
+ /** `onTokenRejected` for `pushChannel`: delete the device row a provider
34
+ * reported gone, so the next fan-out does not retry it. */
35
+ export declare const pruneDeviceToken: (store: DataStore) => (token: string) => Promise<void>;
36
+
22
37
  export { }
package/dist/schema.js CHANGED
@@ -1,18 +1,21 @@
1
1
  import { t as e } from "./devices-BjXAU-8I.js";
2
- import { id as t, table as n, text as r, timestamp as i } from "@voltro/database";
2
+ import { eq as t, id as n, queryFor as r, table as i, text as a, timestamp as o } from "@voltro/database";
3
3
  //#region src/schema.ts
4
- var a = n(e, {
5
- id: t({ prefix: "device" }),
6
- tenantId: r().nullable(),
7
- userId: r(),
8
- platform: r(),
9
- token: r(),
10
- locale: r().nullable(),
11
- timezone: r().nullable(),
12
- appVersion: r().nullable(),
13
- metadata: r().nullable(),
14
- lastSeenAt: i().default("now"),
15
- createdAt: i().default("now")
16
- }).unique("byDeviceToken", ["platform", "token"]).index("byDeviceUser", ["userId"]).index("byDeviceTenant", ["tenantId"]);
4
+ var s = i(e, {
5
+ id: n({ prefix: "device" }),
6
+ tenantId: a().nullable(),
7
+ userId: a(),
8
+ platform: a(),
9
+ token: a(),
10
+ locale: a().nullable(),
11
+ timezone: a().nullable(),
12
+ appVersion: a().nullable(),
13
+ metadata: a().nullable(),
14
+ lastSeenAt: o().default("now"),
15
+ createdAt: o().default("now")
16
+ }).unique("byDeviceToken", ["platform", "token"]).index("byDeviceUser", ["userId"]).index("byDeviceTenant", ["tenantId"]), c = (e, n) => async (i) => (await e.query(r(s).where(t("userId", i)).descriptor)).filter((e) => n === void 0 || e.platform === n).sort((e, t) => t.lastSeenAt.getTime() - e.lastSeenAt.getTime()).map((e) => e.token), l = (n) => async (i) => {
17
+ let a = await n.query(r(s).where(t("token", i)).descriptor);
18
+ for (let t of a) await n.delete(e, t.id);
19
+ }, u = (e) => async (n) => [...await e.query(r(s).where(t("userId", n)).descriptor)].sort((e, t) => t.lastSeenAt.getTime() - e.lastSeenAt.getTime())[0]?.locale ?? null;
17
20
  //#endregion
18
- export { e as DEVICES_TABLE, a as devicesTable };
21
+ export { e as DEVICES_TABLE, u as deviceLocaleFor, c as deviceTokensFor, s as devicesTable, l as pruneDeviceToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voltro/react-native",
3
- "version": "0.81.0",
3
+ "version": "0.83.0",
4
4
  "description": "React Native bindings for the framework: device-registration primitive, background-sync state machine, offline-first client defaults + connection-status surface, and the deep-link declaration shape — the credential-free mobile plumbing on top of the RN-safe React client.",
5
5
  "keywords": [
6
6
  "voltro",
@@ -38,8 +38,9 @@
38
38
  "node": ">=24.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@voltro/client": "0.81.0",
42
- "@voltro/database": "0.81.0"
41
+ "@voltro/client": "0.83.0",
42
+ "@voltro/database": "0.83.0",
43
+ "@voltro/local-first": "0.83.0"
43
44
  },
44
45
  "peerDependencies": {
45
46
  "react": "^19.0.0"