@evolu/react-native 14.3.0 → 15.0.0-next.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.
Files changed (82) hide show
  1. package/README.md +3 -3
  2. package/dist/src/Polyfills.d.ts +3 -0
  3. package/dist/src/Polyfills.d.ts.map +1 -0
  4. package/dist/src/Polyfills.js +209 -0
  5. package/dist/src/Task.d.ts +31 -0
  6. package/dist/src/Task.d.ts.map +1 -0
  7. package/dist/src/Task.js +46 -0
  8. package/dist/src/Worker.d.ts +30 -0
  9. package/dist/src/Worker.d.ts.map +1 -0
  10. package/dist/src/Worker.js +29 -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 +87 -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 +51 -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 +37 -0
  38. package/dist/src/web.d.ts.map +1 -0
  39. package/package.json +73 -51
  40. package/src/ErrorUtils.d.ts +19 -0
  41. package/src/Polyfills.ts +305 -0
  42. package/src/Task.ts +66 -0
  43. package/src/Worker.ts +54 -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 +113 -52
  51. package/src/sqlite-drivers/createExpoSqliteDriver.ts +59 -51
  52. package/src/sqlite-drivers/createOpSqliteDriver.ts +51 -61
  53. package/LICENSE +0 -21
  54. package/dist/components/EvoluIdenticon.d.ts +0 -9
  55. package/dist/components/EvoluIdenticon.d.ts.map +0 -1
  56. package/dist/createExpoDeps.d.ts +0 -7
  57. package/dist/createExpoDeps.d.ts.map +0 -1
  58. package/dist/createExpoDeps.js +0 -123
  59. package/dist/exports/bare-op-sqlite.d.ts +0 -10
  60. package/dist/exports/bare-op-sqlite.d.ts.map +0 -1
  61. package/dist/exports/bare-op-sqlite.js +0 -24
  62. package/dist/exports/expo-op-sqlite.d.ts +0 -9
  63. package/dist/exports/expo-op-sqlite.d.ts.map +0 -1
  64. package/dist/exports/expo-op-sqlite.js +0 -12
  65. package/dist/exports/expo-sqlite.d.ts +0 -8
  66. package/dist/exports/expo-sqlite.d.ts.map +0 -1
  67. package/dist/exports/expo-sqlite.js +0 -11
  68. package/dist/index.d.ts +0 -2
  69. package/dist/index.d.ts.map +0 -1
  70. package/dist/index.js +0 -1
  71. package/dist/shared.d.ts +0 -5
  72. package/dist/shared.d.ts.map +0 -1
  73. package/dist/shared.js +0 -39
  74. package/dist/sqlite-drivers/createExpoSqliteDriver.d.ts +0 -3
  75. package/dist/sqlite-drivers/createExpoSqliteDriver.d.ts.map +0 -1
  76. package/dist/sqlite-drivers/createExpoSqliteDriver.js +0 -46
  77. package/dist/sqlite-drivers/createOpSqliteDriver.d.ts +0 -3
  78. package/dist/sqlite-drivers/createOpSqliteDriver.d.ts.map +0 -1
  79. package/dist/sqlite-drivers/createOpSqliteDriver.js +0 -54
  80. package/dist/web.d.ts.map +0 -1
  81. /package/dist/{web.d.ts → src/web.d.ts} +0 -0
  82. /package/dist/{web.js → src/web.js} +0 -0
@@ -0,0 +1,305 @@
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 AbortSignalWithReason extends AbortSignal {
80
+ readonly reason: unknown;
81
+ }
82
+
83
+ interface AnyControllersRegistry {
84
+ readonly add: (controller: AbortController) => void;
85
+ readonly remove: (controller: AbortController) => void;
86
+ }
87
+
88
+ interface WeakRefLike<T extends object> {
89
+ deref(): T | undefined;
90
+ }
91
+
92
+ type ControllerRef = WeakRefLike<AbortController>;
93
+
94
+ const abortReasonBySignal = new WeakMap<AbortSignal, unknown>();
95
+ const anyControllersBySignal = new WeakMap<
96
+ AbortSignal,
97
+ AnyControllersRegistry
98
+ >();
99
+
100
+ const installAbortControllerPolyfills = (): void => {
101
+ installAbortReasonPolyfill(
102
+ globalThis.AbortController as AbortControllerConstructor,
103
+ globalThis.AbortSignal as AbortSignalConstructor,
104
+ );
105
+ installAbortSignalStaticMethods(
106
+ globalThis.AbortController as AbortControllerConstructor,
107
+ globalThis.AbortSignal as AbortSignalConstructor,
108
+ );
109
+ };
110
+
111
+ const installAbortReasonPolyfill = (
112
+ abortController: AbortControllerConstructor,
113
+ abortSignal: AbortSignalConstructor,
114
+ ): void => {
115
+ if (!("reason" in abortSignal.prototype)) {
116
+ Object.defineProperty(abortSignal.prototype, "reason", {
117
+ configurable: true,
118
+ enumerable: false,
119
+ get(this: AbortSignal): unknown {
120
+ return abortReasonBySignal.get(this);
121
+ },
122
+ });
123
+ }
124
+
125
+ const prototype = abortController.prototype as AbortControllerPrototype;
126
+ if (prototype.__evoluAbortReasonPatched) return;
127
+
128
+ const nativeAbort = prototype.abort;
129
+ prototype.abort = function (this: AbortController, reason?: unknown): void {
130
+ const normalizedReason = reason === undefined ? createAbortError() : reason;
131
+ abortReasonBySignal.set(this.signal, normalizedReason);
132
+ nativeAbort.call(this, normalizedReason);
133
+ };
134
+
135
+ Object.defineProperty(prototype, "__evoluAbortReasonPatched", {
136
+ value: true,
137
+ configurable: true,
138
+ enumerable: false,
139
+ writable: false,
140
+ });
141
+ };
142
+
143
+ const installAbortSignalStaticMethods = (
144
+ abortController: AbortControllerConstructor,
145
+ abortSignal: AbortSignalConstructor,
146
+ ): void => {
147
+ if (typeof abortSignal.abort !== "function") {
148
+ Object.defineProperty(abortSignal, "abort", {
149
+ configurable: true,
150
+ enumerable: false,
151
+ writable: true,
152
+ value: (reason?: unknown): AbortSignal => {
153
+ const controller = new abortController();
154
+ controller.abort(reason);
155
+ return controller.signal;
156
+ },
157
+ });
158
+ }
159
+
160
+ if (typeof abortSignal.timeout !== "function") {
161
+ Object.defineProperty(abortSignal, "timeout", {
162
+ configurable: true,
163
+ enumerable: false,
164
+ writable: true,
165
+ value: (milliseconds: number): AbortSignal => {
166
+ const controller = new abortController();
167
+ const timeoutId = globalThis.setTimeout(() => {
168
+ controller.abort(createTimeoutError(milliseconds));
169
+ }, milliseconds);
170
+
171
+ controller.signal.addEventListener(
172
+ "abort",
173
+ () => {
174
+ globalThis.clearTimeout(timeoutId);
175
+ },
176
+ { once: true },
177
+ );
178
+
179
+ return controller.signal;
180
+ },
181
+ });
182
+ }
183
+
184
+ if (typeof abortSignal.any !== "function") {
185
+ Object.defineProperty(abortSignal, "any", {
186
+ configurable: true,
187
+ enumerable: false,
188
+ writable: true,
189
+ value: (signals: ReadonlyArray<AbortSignal>): AbortSignal =>
190
+ createAbortSignalAny(abortController, signals),
191
+ });
192
+ }
193
+ };
194
+
195
+ const createAbortSignalAny = (
196
+ abortController: AbortControllerConstructor,
197
+ signals: ReadonlyArray<AbortSignal>,
198
+ ): AbortSignal => {
199
+ const controller = new abortController();
200
+ if (signals.length === 0) return controller.signal;
201
+
202
+ for (const signal of signals) {
203
+ if (signal.aborted) {
204
+ controller.abort(readSignalReason(signal));
205
+ return controller.signal;
206
+ }
207
+ }
208
+
209
+ const sources = Array.from(new Set(signals));
210
+
211
+ for (const signal of sources) {
212
+ const registry =
213
+ anyControllersBySignal.get(signal) ??
214
+ createAnyControllersRegistry(signal);
215
+ anyControllersBySignal.set(signal, registry);
216
+ registry.add(controller);
217
+ }
218
+
219
+ controller.signal.addEventListener(
220
+ "abort",
221
+ () => {
222
+ for (const signal of sources) {
223
+ anyControllersBySignal.get(signal)?.remove(controller);
224
+ }
225
+ },
226
+ { once: true },
227
+ );
228
+
229
+ return controller.signal;
230
+ };
231
+
232
+ const createAnyControllersRegistry = (
233
+ sourceSignal: AbortSignal,
234
+ ): AnyControllersRegistry => {
235
+ let refs: Array<ControllerRef> = [];
236
+
237
+ const cleanup = (): void => {
238
+ refs = refs.filter((ref) => {
239
+ const controller = ref.deref();
240
+ return controller != null && !controller.signal.aborted;
241
+ });
242
+
243
+ if (refs.length === 0) {
244
+ sourceSignal.removeEventListener("abort", onAbort);
245
+ anyControllersBySignal.delete(sourceSignal);
246
+ }
247
+ };
248
+
249
+ const onAbort = (): void => {
250
+ const reason = readSignalReason(sourceSignal);
251
+
252
+ for (const ref of refs) {
253
+ const controller = ref.deref();
254
+ if (!controller || controller.signal.aborted) continue;
255
+ controller.abort(reason);
256
+ }
257
+
258
+ refs = [];
259
+ sourceSignal.removeEventListener("abort", onAbort);
260
+ anyControllersBySignal.delete(sourceSignal);
261
+ };
262
+
263
+ sourceSignal.addEventListener("abort", onAbort, { once: true });
264
+
265
+ return {
266
+ add: (controller) => {
267
+ refs.push(new globalThis.WeakRef(controller));
268
+ cleanup();
269
+ },
270
+ remove: (controller) => {
271
+ refs = refs.filter((ref) => {
272
+ const candidate = ref.deref();
273
+ return candidate != null && candidate !== controller;
274
+ });
275
+ cleanup();
276
+ },
277
+ };
278
+ };
279
+
280
+ const readSignalReason = (signal: AbortSignal): unknown => {
281
+ if ("reason" in signal) {
282
+ return (signal as AbortSignalWithReason).reason;
283
+ }
284
+ return createAbortError();
285
+ };
286
+
287
+ const createTimeoutError = (milliseconds: number): Error =>
288
+ createNamedError("TimeoutError", `signal timed out after ${milliseconds} ms`);
289
+
290
+ const createAbortError = (): Error =>
291
+ createNamedError("AbortError", "This operation was aborted");
292
+
293
+ const createNamedError = (name: string, message: string): Error => {
294
+ if (typeof globalThis.DOMException === "function") {
295
+ try {
296
+ return new globalThis.DOMException(message, name);
297
+ } catch {
298
+ // Some runtimes expose DOMException but cannot construct it reliably.
299
+ }
300
+ }
301
+
302
+ const error = new Error(message) as Error & { name: string };
303
+ error.name = name;
304
+ return error;
305
+ };
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 stack = new AsyncDisposableStack();
32
+ *
33
+ * stack.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
+ };
package/src/Worker.ts ADDED
@@ -0,0 +1,54 @@
1
+ import {
2
+ createMessageChannel as createCommonMessageChannel,
3
+ createMessagePort as createCommonMessagePort,
4
+ createSharedWorker as createCommonSharedWorker,
5
+ createWorker as createCommonWorker,
6
+ type MessageChannel,
7
+ type MessagePort,
8
+ type NativeMessagePort,
9
+ type SharedWorker,
10
+ type SharedWorkerSelf,
11
+ type Worker,
12
+ type WorkerSelf,
13
+ } from "@evolu/common";
14
+
15
+ /**
16
+ * Creates an in-memory {@link Worker} for React Native.
17
+ *
18
+ * Since React Native doesn't support Web Workers yet, this runs in the main
19
+ * thread. Messages are queued until `onMessage` is assigned.
20
+ */
21
+ export const createWorker = <Input, Output>(
22
+ initWorker: (self: WorkerSelf<Input, Output>) => void,
23
+ ): Worker<Input, Output> => createCommonWorker(initWorker);
24
+
25
+ /**
26
+ * Creates an in-memory {@link SharedWorker} for React Native.
27
+ *
28
+ * Since React Native doesn't support SharedWorkers, this runs in the main
29
+ * thread. Messages are queued until `onMessage` is assigned.
30
+ */
31
+ export const createSharedWorker = <Input, Output>(
32
+ initWorker: (self: SharedWorkerSelf<Input, Output>) => void,
33
+ ): SharedWorker<Input, Output> => createCommonSharedWorker(initWorker);
34
+
35
+ /**
36
+ * Creates an in-memory {@link MessageChannel} for React Native.
37
+ *
38
+ * Since React Native doesn't support MessageChannel yet, this creates two
39
+ * connected ports running in-process. Messages are queued until `onMessage` is
40
+ * assigned.
41
+ */
42
+ export const createMessageChannel = <Input, Output = never>(): MessageChannel<
43
+ Input,
44
+ Output
45
+ > => createCommonMessageChannel<Input, Output>();
46
+
47
+ /**
48
+ * Creates an Evolu {@link MessagePort} from a "native" port.
49
+ *
50
+ * Uses the shared in-memory native-port registry from `@evolu/common`.
51
+ */
52
+ export const createMessagePort = <Input, Output = never>(
53
+ nativePort: NativeMessagePort<Input, Output>,
54
+ ): MessagePort<Input, Output> => createCommonMessagePort(nativePort);
@@ -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