@voltro/react-native 0.62.3 → 0.63.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/CHANGELOG.md +14 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +42 -38
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -39,6 +39,20 @@ _Changes staged for the next release accumulate here (rolled up from
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
## [0.63.0] — 2026-09-03
|
|
43
|
+
|
|
44
|
+
### ⚠ BREAKING
|
|
45
|
+
|
|
46
|
+
- **@voltro/client, @voltro/runtime, @voltro/cli, @voltro/web, @voltro/react-native** — Short transport gaps now have one bounded recovery contract. Raw diagnostics remain immediate, while `useConnectionStatus` exposes `recovering` during a configurable grace window and `useOnRpcError` emits exactly one visible transport failure only if the gap remains open. Same-subject runtime swaps keep subscription bases, revisions and optimistic patches; subject changes discard them. Interrupted mutations report `outcome: unknown` and replay once with the same key only after the deployed API proves that server idempotency is active. The codemod explains the new status member and the raw versus presentation error channels.
|
|
47
|
+
|
|
48
|
+
**`voltro update` carries you across this** — codemod `0.63.0/01_recovery-presentation`. If you pin versions by hand and never run it, print the notes without changing anything: `voltro update --codemods-only --from <your current version> --dry-run` (this one ships in 0.63.0).
|
|
49
|
+
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- **@voltro/database, @voltro/sql-turso, @voltro/sql-sqlite, @voltro/sql-mssql** — Framework-table evolution now converges under real SQLite and both local Turso transports, and has a live MSSQL fixture. The new Turso runs also fixed internal table protection, boolean/number introspection normalization, and case-insensitive SQLite-family index-name comparison. The MSSQL fixture exposed and fixes a second convergence bug: unbounded text that MSSQL auto-bounds for an index is now rendered and compared with the same indexed-column rule, so a second plan no longer tries to widen an indexed `NVARCHAR(450)` to `NVARCHAR(MAX)`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
42
56
|
## [0.62.3] — 2026-09-03
|
|
43
57
|
|
|
44
58
|
### Added
|
package/dist/index.d.ts
CHANGED
|
@@ -325,6 +325,7 @@ export declare interface MobileApiBinding {
|
|
|
325
325
|
* {@link resolveDevWsUrl}. */
|
|
326
326
|
readonly wsUrl: string;
|
|
327
327
|
readonly headers?: ResolvableHeaders | undefined;
|
|
328
|
+
readonly recovery?: MountedApi['recovery'];
|
|
328
329
|
}
|
|
329
330
|
|
|
330
331
|
export declare interface MobileConnectionControls {
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { a as e, i as t, n, r, t as i } from "./devices-BjXAU-8I.js";
|
|
2
2
|
import { useCallback as a, useEffect as o, useReducer as s, useRef as c, useState as l } from "react";
|
|
3
|
-
import { buildApiRuntime as u,
|
|
3
|
+
import { buildApiRuntime as u, recoveryCoordinatorFor as d, startApiSupervisor as f } from "@voltro/client";
|
|
4
4
|
//#region src/deepLink.ts
|
|
5
|
-
var
|
|
5
|
+
var p = (e) => {
|
|
6
6
|
let t = e.pattern;
|
|
7
7
|
if (t.length === 0 || t[0] !== "/") throw Error(`defineDeepLink: pattern must start with "/" (got "${t}")`);
|
|
8
8
|
return {
|
|
@@ -10,7 +10,7 @@ var f = (e) => {
|
|
|
10
10
|
pattern: t,
|
|
11
11
|
handler: e.handler
|
|
12
12
|
};
|
|
13
|
-
},
|
|
13
|
+
}, m = (e) => {
|
|
14
14
|
let t = e, n = /^([a-zA-Z][a-zA-Z0-9+.-]*):\/\//.exec(t);
|
|
15
15
|
if (n) {
|
|
16
16
|
let e = t.slice(n[0].length), r = n[1].toLowerCase();
|
|
@@ -27,8 +27,8 @@ var f = (e) => {
|
|
|
27
27
|
return e;
|
|
28
28
|
}
|
|
29
29
|
});
|
|
30
|
-
},
|
|
31
|
-
let n =
|
|
30
|
+
}, h = (e, t) => {
|
|
31
|
+
let n = m(e), r = m(t);
|
|
32
32
|
if (n.length !== r.length) return null;
|
|
33
33
|
let i = {};
|
|
34
34
|
for (let e = 0; e < n.length; e++) {
|
|
@@ -40,10 +40,10 @@ var f = (e) => {
|
|
|
40
40
|
} else if (t !== "*" && t !== a) return null;
|
|
41
41
|
}
|
|
42
42
|
return i;
|
|
43
|
-
}, h = (e, t) => {
|
|
44
|
-
let n = m(e.pattern, t);
|
|
45
|
-
return n === null ? null : (e.handler(n), n);
|
|
46
43
|
}, g = (e, t) => {
|
|
44
|
+
let n = h(e.pattern, t);
|
|
45
|
+
return n === null ? null : (e.handler(n), n);
|
|
46
|
+
}, _ = (e, t) => {
|
|
47
47
|
let n = e.split("/").map((n) => {
|
|
48
48
|
if (!n.startsWith(":")) return n;
|
|
49
49
|
let r = n.slice(1), i = t[r];
|
|
@@ -51,21 +51,21 @@ var f = (e) => {
|
|
|
51
51
|
return encodeURIComponent(i);
|
|
52
52
|
}).join("/");
|
|
53
53
|
return n.length === 0 ? "/" : n;
|
|
54
|
-
},
|
|
54
|
+
}, v = (e, t) => {
|
|
55
55
|
for (let n of e) {
|
|
56
|
-
let e =
|
|
56
|
+
let e = h(n.pattern, t);
|
|
57
57
|
if (e !== null) return {
|
|
58
58
|
descriptor: n,
|
|
59
59
|
params: e
|
|
60
60
|
};
|
|
61
61
|
}
|
|
62
62
|
return null;
|
|
63
|
-
},
|
|
64
|
-
let n =
|
|
63
|
+
}, y = (e, t) => {
|
|
64
|
+
let n = v(e, t);
|
|
65
65
|
if (n === null) return null;
|
|
66
66
|
let r = n.descriptor.handler;
|
|
67
67
|
return r(n.params), n.params;
|
|
68
|
-
},
|
|
68
|
+
}, b = {
|
|
69
69
|
status: "idle",
|
|
70
70
|
isForeground: !0,
|
|
71
71
|
lastSyncAt: void 0,
|
|
@@ -73,7 +73,7 @@ var f = (e) => {
|
|
|
73
73
|
lastError: void 0,
|
|
74
74
|
successCount: 0,
|
|
75
75
|
failureCount: 0
|
|
76
|
-
},
|
|
76
|
+
}, x = (e, t) => {
|
|
77
77
|
switch (t.type) {
|
|
78
78
|
case "foreground": return e.isForeground ? e : {
|
|
79
79
|
...e,
|
|
@@ -104,11 +104,11 @@ var f = (e) => {
|
|
|
104
104
|
failureCount: e.failureCount + 1
|
|
105
105
|
};
|
|
106
106
|
case "reset": return {
|
|
107
|
-
...
|
|
107
|
+
...b,
|
|
108
108
|
isForeground: e.isForeground
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
-
},
|
|
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) => {
|
|
112
112
|
if (typeof document > "u" || typeof window > "u") return () => {};
|
|
113
113
|
let n = () => {
|
|
114
114
|
document.visibilityState === "visible" ? e() : t();
|
|
@@ -116,13 +116,13 @@ var f = (e) => {
|
|
|
116
116
|
return document.addEventListener("visibilitychange", n), window.addEventListener("focus", e), () => {
|
|
117
117
|
document.removeEventListener("visibilitychange", n), window.removeEventListener("focus", e);
|
|
118
118
|
};
|
|
119
|
-
},
|
|
120
|
-
let n = t.intervalMs ??
|
|
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
121
|
p.current = e;
|
|
122
122
|
let m = c(d);
|
|
123
123
|
m.current = d;
|
|
124
124
|
let h = c(!0), g = a((e) => {
|
|
125
|
-
|
|
125
|
+
S(m.current, l(), {
|
|
126
126
|
intervalMs: n,
|
|
127
127
|
enabled: i
|
|
128
128
|
}, e) && (f({
|
|
@@ -167,7 +167,7 @@ var f = (e) => {
|
|
|
167
167
|
...d,
|
|
168
168
|
sync: _
|
|
169
169
|
};
|
|
170
|
-
},
|
|
170
|
+
}, E = {
|
|
171
171
|
localFirst: !0,
|
|
172
172
|
optimistic: !0,
|
|
173
173
|
syncOnForeground: !0,
|
|
@@ -179,7 +179,7 @@ var f = (e) => {
|
|
|
179
179
|
15e3,
|
|
180
180
|
6e4
|
|
181
181
|
]
|
|
182
|
-
},
|
|
182
|
+
}, D = (e, t) => e ? t > 0 ? "degraded" : "connected" : "offline", O = {
|
|
183
183
|
read: () => typeof navigator > "u" || navigator.onLine,
|
|
184
184
|
subscribe: (e) => {
|
|
185
185
|
if (typeof window > "u") return () => {};
|
|
@@ -188,7 +188,7 @@ var f = (e) => {
|
|
|
188
188
|
window.removeEventListener("online", t), window.removeEventListener("offline", n);
|
|
189
189
|
};
|
|
190
190
|
}
|
|
191
|
-
},
|
|
191
|
+
}, k = (e) => {
|
|
192
192
|
let t = (e) => typeof e.isInternetReachable == "boolean" ? e.isInternetReachable : e.isConnected === !0, n = !0;
|
|
193
193
|
return {
|
|
194
194
|
read: () => n,
|
|
@@ -201,8 +201,8 @@ var f = (e) => {
|
|
|
201
201
|
}).catch(() => {}), i;
|
|
202
202
|
}
|
|
203
203
|
};
|
|
204
|
-
},
|
|
205
|
-
let t = e?.onlineSource ??
|
|
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
206
|
o(() => (f.current = !0, () => {
|
|
207
207
|
f.current = !1;
|
|
208
208
|
}), []), o(() => t.subscribe((e) => {
|
|
@@ -214,14 +214,14 @@ var f = (e) => {
|
|
|
214
214
|
f.current && s(0);
|
|
215
215
|
}, []);
|
|
216
216
|
return {
|
|
217
|
-
status:
|
|
217
|
+
status: D(n, i),
|
|
218
218
|
online: n,
|
|
219
219
|
failureCount: i,
|
|
220
220
|
lastFailureAt: u,
|
|
221
221
|
reportFailure: p,
|
|
222
222
|
reportSuccess: m
|
|
223
223
|
};
|
|
224
|
-
},
|
|
224
|
+
}, j = (e) => {
|
|
225
225
|
let { storage: t, keys: n } = e, r = e.onError ?? ((e, t) => {
|
|
226
226
|
console.warn(`[voltro] persisting '${e}' failed`, t);
|
|
227
227
|
}), i = /* @__PURE__ */ new Map(), a = !1, o = null, s = /* @__PURE__ */ new Map(), c = null, l = async () => {
|
|
@@ -280,38 +280,41 @@ var f = (e) => {
|
|
|
280
280
|
},
|
|
281
281
|
keys: () => [...i.keys()]
|
|
282
282
|
};
|
|
283
|
-
},
|
|
283
|
+
}, M = (e, t) => {
|
|
284
284
|
let n = globalThis.WebSocket;
|
|
285
285
|
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
286
|
return t === void 0 ? new n(e) : new n(e, t);
|
|
287
|
-
},
|
|
287
|
+
}, N = (e, t, n) => (r, i) => {
|
|
288
288
|
let a = e(r, i);
|
|
289
289
|
return a.addEventListener("open", () => t()), a.addEventListener("close", () => n("close")), a.addEventListener("error", () => n("error")), a;
|
|
290
|
-
},
|
|
291
|
-
let t = e.webSocketConstructor ??
|
|
292
|
-
return
|
|
290
|
+
}, P = (e) => {
|
|
291
|
+
let t = e.webSocketConstructor ?? M;
|
|
292
|
+
return f({
|
|
293
293
|
apis: e.apis.map((e) => ({
|
|
294
294
|
name: e.name,
|
|
295
295
|
group: e.group,
|
|
296
296
|
descriptors: e.descriptors,
|
|
297
297
|
wsUrl: e.wsUrl,
|
|
298
|
-
...e.headers === void 0 ? {} : { headers: e.headers }
|
|
298
|
+
...e.headers === void 0 ? {} : { headers: e.headers },
|
|
299
|
+
...e.recovery === void 0 ? {} : { recovery: e.recovery }
|
|
299
300
|
})),
|
|
300
301
|
...e.retryDelayMs === void 0 ? {} : { retryDelayMs: e.retryDelayMs },
|
|
301
302
|
onChange: e.onChange,
|
|
302
|
-
buildClient: async (e, n, r, i, a) => await u({
|
|
303
|
+
buildClient: async (e, n, r, i, a, o) => await u({
|
|
303
304
|
name: e.name,
|
|
305
|
+
generation: n,
|
|
304
306
|
wsUrl: e.wsUrl,
|
|
305
307
|
group: e.group,
|
|
306
308
|
...e.headers === void 0 ? {} : { headers: e.headers },
|
|
307
309
|
...a === void 0 ? {} : { errorBus: a },
|
|
308
|
-
|
|
310
|
+
...o === void 0 ? {} : { recovery: o },
|
|
311
|
+
webSocketConstructor: N(t, r, i)
|
|
309
312
|
})
|
|
310
313
|
});
|
|
311
|
-
},
|
|
314
|
+
}, F = (e, t, n = "/ws") => {
|
|
312
315
|
let r = (e ?? "").split(":")[0];
|
|
313
316
|
return r === void 0 || r === "" || r === "localhost" || r === "127.0.0.1" ? `ws://localhost:${t}${n}` : `ws://${r}:${t}${n}`;
|
|
314
|
-
},
|
|
317
|
+
}, I = (e, t) => {
|
|
315
318
|
let n = /* @__PURE__ */ new Map();
|
|
316
319
|
for (let r of e) {
|
|
317
320
|
let e = t.get(r.name);
|
|
@@ -321,10 +324,11 @@ var f = (e) => {
|
|
|
321
324
|
client: e.client,
|
|
322
325
|
descriptors: r.descriptors,
|
|
323
326
|
inspectBaseUrl: r.wsUrl.replace(/^ws/, "http").replace(/\/ws$/, ""),
|
|
324
|
-
errorBus: e.errorBus
|
|
327
|
+
errorBus: e.errorBus,
|
|
328
|
+
recovery: e.recovery ?? d(e.errorBus, r.name, r.recovery)
|
|
325
329
|
});
|
|
326
330
|
}
|
|
327
331
|
return n;
|
|
328
332
|
};
|
|
329
333
|
//#endregion
|
|
330
|
-
export { i as DEVICES_TABLE, n as DEVICE_PLATFORMS,
|
|
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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.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,8 @@
|
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@voltro/client": "0.
|
|
42
|
-
"@voltro/database": "0.
|
|
41
|
+
"@voltro/client": "0.63.0",
|
|
42
|
+
"@voltro/database": "0.63.0"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"react": "^19.0.0"
|