@evolu/react-native 14.3.0 → 15.0.0-next.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 (81) hide show
  1. package/README.md +3 -3
  2. package/dist/src/LockManager.d.ts +13 -0
  3. package/dist/src/LockManager.d.ts.map +1 -0
  4. package/dist/src/LockManager.js +219 -0
  5. package/dist/src/Polyfills.d.ts +3 -0
  6. package/dist/src/Polyfills.d.ts.map +1 -0
  7. package/dist/src/Polyfills.js +209 -0
  8. package/dist/src/Task.d.ts +31 -0
  9. package/dist/src/Task.d.ts.map +1 -0
  10. package/dist/src/Task.js +46 -0
  11. package/dist/src/components/EvoluIdenticon.d.ts +9 -0
  12. package/dist/src/components/EvoluIdenticon.d.ts.map +1 -0
  13. package/dist/{components → src/components}/EvoluIdenticon.js +1 -1
  14. package/dist/src/createExpoDeps.d.ts +1 -0
  15. package/dist/src/createExpoDeps.d.ts.map +1 -0
  16. package/dist/src/createExpoDeps.js +163 -0
  17. package/dist/src/exports/bare-op-sqlite.d.ts +1 -0
  18. package/dist/src/exports/bare-op-sqlite.d.ts.map +1 -0
  19. package/dist/src/exports/bare-op-sqlite.js +31 -0
  20. package/dist/src/exports/expo-op-sqlite.d.ts +1 -0
  21. package/dist/src/exports/expo-op-sqlite.d.ts.map +1 -0
  22. package/dist/src/exports/expo-op-sqlite.js +16 -0
  23. package/dist/src/exports/expo-sqlite.d.ts +11 -0
  24. package/dist/src/exports/expo-sqlite.d.ts.map +1 -0
  25. package/dist/src/exports/expo-sqlite.js +24 -0
  26. package/dist/src/index.d.ts +4 -0
  27. package/dist/src/index.d.ts.map +1 -0
  28. package/dist/src/index.js +3 -0
  29. package/dist/src/shared.d.ts +5 -0
  30. package/dist/src/shared.d.ts.map +1 -0
  31. package/dist/src/shared.js +90 -0
  32. package/dist/src/sqlite-drivers/createExpoSqliteDriver.d.ts +3 -0
  33. package/dist/src/sqlite-drivers/createExpoSqliteDriver.d.ts.map +1 -0
  34. package/dist/src/sqlite-drivers/createExpoSqliteDriver.js +134 -0
  35. package/dist/src/sqlite-drivers/createOpSqliteDriver.d.ts +3 -0
  36. package/dist/src/sqlite-drivers/createOpSqliteDriver.d.ts.map +1 -0
  37. package/dist/src/sqlite-drivers/createOpSqliteDriver.js +119 -0
  38. package/dist/src/web.d.ts.map +1 -0
  39. package/package.json +72 -48
  40. package/src/ErrorUtils.d.ts +19 -0
  41. package/src/LockManager.ts +325 -0
  42. package/src/Polyfills.ts +301 -0
  43. package/src/Task.ts +66 -0
  44. package/src/components/EvoluIdenticon.tsx +2 -2
  45. package/src/createExpoDeps.ts +162 -160
  46. package/src/exports/bare-op-sqlite.ts +30 -28
  47. package/src/exports/expo-op-sqlite.ts +15 -14
  48. package/src/exports/expo-sqlite.ts +21 -4
  49. package/src/index.ts +2 -0
  50. package/src/shared.ts +115 -49
  51. package/src/sqlite-drivers/createExpoSqliteDriver.ts +74 -51
  52. package/src/sqlite-drivers/createOpSqliteDriver.ts +62 -60
  53. package/dist/components/EvoluIdenticon.d.ts +0 -9
  54. package/dist/components/EvoluIdenticon.d.ts.map +0 -1
  55. package/dist/createExpoDeps.d.ts +0 -7
  56. package/dist/createExpoDeps.d.ts.map +0 -1
  57. package/dist/createExpoDeps.js +0 -123
  58. package/dist/exports/bare-op-sqlite.d.ts +0 -10
  59. package/dist/exports/bare-op-sqlite.d.ts.map +0 -1
  60. package/dist/exports/bare-op-sqlite.js +0 -24
  61. package/dist/exports/expo-op-sqlite.d.ts +0 -9
  62. package/dist/exports/expo-op-sqlite.d.ts.map +0 -1
  63. package/dist/exports/expo-op-sqlite.js +0 -12
  64. package/dist/exports/expo-sqlite.d.ts +0 -8
  65. package/dist/exports/expo-sqlite.d.ts.map +0 -1
  66. package/dist/exports/expo-sqlite.js +0 -11
  67. package/dist/index.d.ts +0 -2
  68. package/dist/index.d.ts.map +0 -1
  69. package/dist/index.js +0 -1
  70. package/dist/shared.d.ts +0 -5
  71. package/dist/shared.d.ts.map +0 -1
  72. package/dist/shared.js +0 -39
  73. package/dist/sqlite-drivers/createExpoSqliteDriver.d.ts +0 -3
  74. package/dist/sqlite-drivers/createExpoSqliteDriver.d.ts.map +0 -1
  75. package/dist/sqlite-drivers/createExpoSqliteDriver.js +0 -46
  76. package/dist/sqlite-drivers/createOpSqliteDriver.d.ts +0 -3
  77. package/dist/sqlite-drivers/createOpSqliteDriver.d.ts.map +0 -1
  78. package/dist/sqlite-drivers/createOpSqliteDriver.js +0 -54
  79. package/dist/web.d.ts.map +0 -1
  80. /package/dist/{web.d.ts → src/web.d.ts} +0 -0
  81. /package/dist/{web.js → src/web.js} +0 -0
@@ -0,0 +1,301 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
2
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
3
+
4
+ import { installPolyfills as installCommonPolyfills } from "@evolu/common/polyfills";
5
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
6
+ import fromAsync from "array-from-async";
7
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
8
+ import withResolvers from "promise.withresolvers";
9
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
10
+ import promiseTry from "promise.try";
11
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
12
+ import toSorted from "array.prototype.tosorted";
13
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
14
+ import difference from "set.prototype.difference";
15
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
16
+ import intersection from "set.prototype.intersection";
17
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
18
+ import isDisjointFrom from "set.prototype.isdisjointfrom";
19
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
20
+ import isSubsetOf from "set.prototype.issubsetof";
21
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
22
+ import isSupersetOf from "set.prototype.issupersetof";
23
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
24
+ import symmetricDifference from "set.prototype.symmetricdifference";
25
+ // @ts-expect-error Runtime polyfill package has no TypeScript declarations.
26
+ import union from "set.prototype.union";
27
+
28
+ difference.shim();
29
+ intersection.shim();
30
+ isDisjointFrom.shim();
31
+ isSubsetOf.shim();
32
+ isSupersetOf.shim();
33
+ symmetricDifference.shim();
34
+ union.shim();
35
+
36
+ /** Installs polyfills required by Evolu in React Native runtimes. */
37
+ export const installPolyfills = (): void => {
38
+ installCommonPolyfills();
39
+ installArrayPolyfills();
40
+ installPromisePolyfills();
41
+ installAbortControllerPolyfills();
42
+ };
43
+
44
+ const installArrayPolyfills = (): void => {
45
+ if (typeof Array.fromAsync !== "function") {
46
+ Object.defineProperty(Array, "fromAsync", {
47
+ configurable: true,
48
+ enumerable: false,
49
+ writable: true,
50
+ value: fromAsync as typeof Array.fromAsync,
51
+ });
52
+ }
53
+
54
+ toSorted.shim();
55
+ };
56
+
57
+ const installPromisePolyfills = () => {
58
+ withResolvers.shim();
59
+ promiseTry.shim();
60
+ };
61
+
62
+ interface AbortControllerConstructor {
63
+ new (): AbortController;
64
+ readonly prototype: AbortController;
65
+ }
66
+
67
+ interface AbortSignalConstructor {
68
+ readonly prototype: AbortSignal;
69
+ abort?: (reason?: unknown) => AbortSignal;
70
+ timeout?: (milliseconds: number) => AbortSignal;
71
+ any?: (signals: Array<AbortSignal>) => AbortSignal;
72
+ }
73
+
74
+ interface AbortControllerPrototype extends AbortController {
75
+ abort: (reason?: unknown) => void;
76
+ readonly __evoluAbortReasonPatched?: true;
77
+ }
78
+
79
+ interface AnyControllersRegistry {
80
+ readonly add: (controller: AbortController) => void;
81
+ readonly remove: (controller: AbortController) => void;
82
+ }
83
+
84
+ interface WeakRefLike<T extends object> {
85
+ deref(): T | undefined;
86
+ }
87
+
88
+ type ControllerRef = WeakRefLike<AbortController>;
89
+
90
+ const abortReasonBySignal = new WeakMap<AbortSignal, unknown>();
91
+ const anyControllersBySignal = new WeakMap<
92
+ AbortSignal,
93
+ AnyControllersRegistry
94
+ >();
95
+
96
+ const installAbortControllerPolyfills = (): void => {
97
+ installAbortReasonPolyfill(
98
+ globalThis.AbortController,
99
+ globalThis.AbortSignal,
100
+ );
101
+ installAbortSignalStaticMethods(
102
+ globalThis.AbortController,
103
+ globalThis.AbortSignal,
104
+ );
105
+ };
106
+
107
+ const installAbortReasonPolyfill = (
108
+ abortController: AbortControllerConstructor,
109
+ abortSignal: AbortSignalConstructor,
110
+ ): void => {
111
+ if (!("reason" in abortSignal.prototype)) {
112
+ Object.defineProperty(abortSignal.prototype, "reason", {
113
+ configurable: true,
114
+ enumerable: false,
115
+ get(this: AbortSignal): unknown {
116
+ return abortReasonBySignal.get(this);
117
+ },
118
+ });
119
+ }
120
+
121
+ const prototype = abortController.prototype as AbortControllerPrototype;
122
+ if (prototype.__evoluAbortReasonPatched) return;
123
+
124
+ const nativeAbort = prototype.abort;
125
+ prototype.abort = function (this: AbortController, reason?: unknown): void {
126
+ const normalizedReason = reason === undefined ? createAbortError() : reason;
127
+ abortReasonBySignal.set(this.signal, normalizedReason);
128
+ nativeAbort.call(this, normalizedReason);
129
+ };
130
+
131
+ Object.defineProperty(prototype, "__evoluAbortReasonPatched", {
132
+ value: true,
133
+ configurable: true,
134
+ enumerable: false,
135
+ writable: false,
136
+ });
137
+ };
138
+
139
+ const installAbortSignalStaticMethods = (
140
+ abortController: AbortControllerConstructor,
141
+ abortSignal: AbortSignalConstructor,
142
+ ): void => {
143
+ if (typeof abortSignal.abort !== "function") {
144
+ Object.defineProperty(abortSignal, "abort", {
145
+ configurable: true,
146
+ enumerable: false,
147
+ writable: true,
148
+ value: (reason?: unknown): AbortSignal => {
149
+ const controller = new abortController();
150
+ controller.abort(reason);
151
+ return controller.signal;
152
+ },
153
+ });
154
+ }
155
+
156
+ if (typeof abortSignal.timeout !== "function") {
157
+ Object.defineProperty(abortSignal, "timeout", {
158
+ configurable: true,
159
+ enumerable: false,
160
+ writable: true,
161
+ value: (milliseconds: number): AbortSignal => {
162
+ const controller = new abortController();
163
+ const timeoutId = globalThis.setTimeout(() => {
164
+ controller.abort(createTimeoutError(milliseconds));
165
+ }, milliseconds);
166
+
167
+ controller.signal.addEventListener(
168
+ "abort",
169
+ () => {
170
+ globalThis.clearTimeout(timeoutId);
171
+ },
172
+ { once: true },
173
+ );
174
+
175
+ return controller.signal;
176
+ },
177
+ });
178
+ }
179
+
180
+ if (typeof abortSignal.any !== "function") {
181
+ Object.defineProperty(abortSignal, "any", {
182
+ configurable: true,
183
+ enumerable: false,
184
+ writable: true,
185
+ value: (signals: ReadonlyArray<AbortSignal>): AbortSignal =>
186
+ createAbortSignalAny(abortController, signals),
187
+ });
188
+ }
189
+ };
190
+
191
+ const createAbortSignalAny = (
192
+ abortController: AbortControllerConstructor,
193
+ signals: ReadonlyArray<AbortSignal>,
194
+ ): AbortSignal => {
195
+ const controller = new abortController();
196
+ if (signals.length === 0) return controller.signal;
197
+
198
+ for (const signal of signals) {
199
+ if (signal.aborted) {
200
+ controller.abort(readSignalReason(signal));
201
+ return controller.signal;
202
+ }
203
+ }
204
+
205
+ const sources = Array.from(new Set(signals));
206
+
207
+ for (const signal of sources) {
208
+ const registry =
209
+ anyControllersBySignal.get(signal) ??
210
+ createAnyControllersRegistry(signal);
211
+ anyControllersBySignal.set(signal, registry);
212
+ registry.add(controller);
213
+ }
214
+
215
+ controller.signal.addEventListener(
216
+ "abort",
217
+ () => {
218
+ for (const signal of sources) {
219
+ anyControllersBySignal.get(signal)?.remove(controller);
220
+ }
221
+ },
222
+ { once: true },
223
+ );
224
+
225
+ return controller.signal;
226
+ };
227
+
228
+ const createAnyControllersRegistry = (
229
+ sourceSignal: AbortSignal,
230
+ ): AnyControllersRegistry => {
231
+ let refs: Array<ControllerRef> = [];
232
+
233
+ const cleanup = (): void => {
234
+ refs = refs.filter((ref) => {
235
+ const controller = ref.deref();
236
+ return controller != null && !controller.signal.aborted;
237
+ });
238
+
239
+ if (refs.length === 0) {
240
+ sourceSignal.removeEventListener("abort", onAbort);
241
+ anyControllersBySignal.delete(sourceSignal);
242
+ }
243
+ };
244
+
245
+ const onAbort = (): void => {
246
+ const reason = readSignalReason(sourceSignal);
247
+
248
+ for (const ref of refs) {
249
+ const controller = ref.deref();
250
+ if (!controller || controller.signal.aborted) continue;
251
+ controller.abort(reason);
252
+ }
253
+
254
+ refs = [];
255
+ sourceSignal.removeEventListener("abort", onAbort);
256
+ anyControllersBySignal.delete(sourceSignal);
257
+ };
258
+
259
+ sourceSignal.addEventListener("abort", onAbort, { once: true });
260
+
261
+ return {
262
+ add: (controller) => {
263
+ refs.push(new globalThis.WeakRef(controller));
264
+ cleanup();
265
+ },
266
+ remove: (controller) => {
267
+ refs = refs.filter((ref) => {
268
+ const candidate = ref.deref();
269
+ return candidate != null && candidate !== controller;
270
+ });
271
+ cleanup();
272
+ },
273
+ };
274
+ };
275
+
276
+ const readSignalReason = (signal: AbortSignal): unknown => {
277
+ if ("reason" in signal) {
278
+ return signal.reason;
279
+ }
280
+ return createAbortError();
281
+ };
282
+
283
+ const createTimeoutError = (milliseconds: number): Error =>
284
+ createNamedError("TimeoutError", `signal timed out after ${milliseconds} ms`);
285
+
286
+ const createAbortError = (): Error =>
287
+ createNamedError("AbortError", "This operation was aborted");
288
+
289
+ const createNamedError = (name: string, message: string): Error => {
290
+ if (typeof globalThis.DOMException === "function") {
291
+ try {
292
+ return new globalThis.DOMException(message, name);
293
+ } catch {
294
+ // Some runtimes expose DOMException but cannot construct it reliably.
295
+ }
296
+ }
297
+
298
+ const error = new Error(message) as Error & { name: string };
299
+ error.name = name;
300
+ return error;
301
+ };
package/src/Task.ts ADDED
@@ -0,0 +1,66 @@
1
+ /**
2
+ * React Native-specific Task utilities.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ import {
8
+ createRun as createCommonRun,
9
+ createUnknownError,
10
+ type CreateRun,
11
+ type Run,
12
+ type RunDeps,
13
+ } from "@evolu/common";
14
+
15
+ /**
16
+ * Creates {@link Run} for React Native with global error handling.
17
+ *
18
+ * Registers `ErrorUtils.setGlobalHandler` for uncaught JavaScript errors. The
19
+ * handler is restored to the previous one when the Run is disposed.
20
+ *
21
+ * ### Example
22
+ *
23
+ * ```ts
24
+ * const console = createConsole({
25
+ * formatter: createConsoleFormatter()({
26
+ * timestampFormat: "relative",
27
+ * }),
28
+ * });
29
+ *
30
+ * await using run = createRun({ console });
31
+ * await using disposer = new AsyncDisposableStack();
32
+ *
33
+ * disposer.use(await run.orThrow(startApp()));
34
+ * ```
35
+ *
36
+ * @group React Native Run
37
+ */
38
+ export const createRun: CreateRun<RunDeps> = <D>(
39
+ deps?: D,
40
+ ): Run<RunDeps & D> => {
41
+ const run = createCommonRun(deps);
42
+
43
+ const console = run.deps.console.child("global");
44
+
45
+ const previousHandler = globalThis.ErrorUtils?.getGlobalHandler();
46
+
47
+ const handleError = (error: unknown, isFatal?: boolean) => {
48
+ console.error(
49
+ isFatal ? "fatalError" : "uncaughtError",
50
+ createUnknownError(error),
51
+ );
52
+
53
+ // Call the previous handler if it exists
54
+ previousHandler?.(error, isFatal);
55
+ };
56
+
57
+ globalThis.ErrorUtils?.setGlobalHandler(handleError);
58
+
59
+ run.onAbort(() => {
60
+ if (previousHandler) {
61
+ globalThis.ErrorUtils?.setGlobalHandler(previousHandler);
62
+ }
63
+ });
64
+
65
+ return run;
66
+ };
@@ -1,5 +1,5 @@
1
- import { createIdenticon, IdenticonStyle, OwnerId } from "@evolu/common";
2
- import { FC, useMemo } from "react";
1
+ import { createIdenticon, type IdenticonStyle, OwnerId } from "@evolu/common";
2
+ import { type FC, useMemo } from "react";
3
3
  import { View } from "react-native";
4
4
  import { SvgXml } from "react-native-svg";
5
5