@rootnode-app/react-native 0.1.1 → 0.1.3
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/README.md +4 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +127 -19
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -8,8 +8,10 @@ queueing and sessions, person properties, push registration.
|
|
|
8
8
|
npx expo install @rootnode-app/react-native
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
>
|
|
12
|
-
>
|
|
11
|
+
> One command — the native engines arrive automatically (npm installs peer
|
|
12
|
+
> dependencies since v7). If expo-doctor reports a version mismatch with your
|
|
13
|
+
> Expo SDK, run `npx expo install --fix` once. Includes a native crash
|
|
14
|
+
> engine: ship with a new build (EAS), not an OTA update.
|
|
13
15
|
|
|
14
16
|
```tsx
|
|
15
17
|
import { Rootnode } from '@rootnode-app/react-native';
|
package/dist/index.d.ts
CHANGED
|
@@ -21,8 +21,8 @@ export interface RootnodeConfig {
|
|
|
21
21
|
onError?: (message: string) => void;
|
|
22
22
|
}
|
|
23
23
|
type Loader = (name: string) => any;
|
|
24
|
-
/** Test seam:
|
|
25
|
-
export declare function _setLoader(l: Loader): void;
|
|
24
|
+
/** Test seam: vitest has no RN runtime. null restores the real requires. */
|
|
25
|
+
export declare function _setLoader(l: Loader | null): void;
|
|
26
26
|
declare class RootnodeClient {
|
|
27
27
|
private core;
|
|
28
28
|
private sentry;
|
package/dist/index.js
CHANGED
|
@@ -3,23 +3,131 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.RootnodeCore = exports.Rootnode = void 0;
|
|
4
4
|
exports._setLoader = _setLoader;
|
|
5
5
|
const core_1 = require("./core");
|
|
6
|
-
let
|
|
7
|
-
|
|
8
|
-
return require(name);
|
|
9
|
-
};
|
|
10
|
-
/** Test seam: swap module loading (vitest has no RN runtime). */
|
|
6
|
+
let overrideLoader = null;
|
|
7
|
+
/** Test seam: vitest has no RN runtime. null restores the real requires. */
|
|
11
8
|
function _setLoader(l) {
|
|
12
|
-
|
|
9
|
+
overrideLoader = l;
|
|
13
10
|
}
|
|
14
|
-
|
|
11
|
+
/**
|
|
12
|
+
* Every native module gets its own LITERAL require inside its own try/catch —
|
|
13
|
+
* Metro only resolves static names, and only treats a require as optional
|
|
14
|
+
* when the call sits in a try block. A require call with a variable name ships
|
|
15
|
+
* a bundle where every native feature silently dies (Foneria, 0.1.2).
|
|
16
|
+
*/
|
|
17
|
+
function viaOverride(name) {
|
|
18
|
+
if (!overrideLoader)
|
|
19
|
+
return { hit: false, mod: null };
|
|
15
20
|
try {
|
|
16
|
-
return
|
|
21
|
+
return { hit: true, mod: overrideLoader(name) };
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return { hit: true, mod: null };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function loadSentry() {
|
|
28
|
+
const o = viaOverride('@sentry/react-native');
|
|
29
|
+
if (o.hit)
|
|
30
|
+
return o.mod;
|
|
31
|
+
try {
|
|
32
|
+
return require('@sentry/react-native');
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function loadNotifications() {
|
|
39
|
+
const o = viaOverride('expo-notifications');
|
|
40
|
+
if (o.hit)
|
|
41
|
+
return o.mod;
|
|
42
|
+
try {
|
|
43
|
+
return require('expo-notifications');
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function loadDevice() {
|
|
50
|
+
const o = viaOverride('expo-device');
|
|
51
|
+
if (o.hit)
|
|
52
|
+
return o.mod;
|
|
53
|
+
try {
|
|
54
|
+
return require('expo-device');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function loadReactNative() {
|
|
61
|
+
const o = viaOverride('react-native');
|
|
62
|
+
if (o.hit)
|
|
63
|
+
return o.mod;
|
|
64
|
+
try {
|
|
65
|
+
return require('react-native');
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
function loadAsyncStorage() {
|
|
72
|
+
const o = viaOverride('@react-native-async-storage/async-storage');
|
|
73
|
+
if (o.hit)
|
|
74
|
+
return o.mod;
|
|
75
|
+
try {
|
|
76
|
+
return require('@react-native-async-storage/async-storage');
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function loadFileSystemLegacy() {
|
|
83
|
+
const o = viaOverride('expo-file-system/legacy');
|
|
84
|
+
if (o.hit)
|
|
85
|
+
return o.mod;
|
|
86
|
+
try {
|
|
87
|
+
return require('expo-file-system/legacy');
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function loadFileSystem() {
|
|
94
|
+
const o = viaOverride('expo-file-system');
|
|
95
|
+
if (o.hit)
|
|
96
|
+
return o.mod;
|
|
97
|
+
try {
|
|
98
|
+
return require('expo-file-system');
|
|
17
99
|
}
|
|
18
100
|
catch {
|
|
19
|
-
onError?.(`[rootnode] optional module ${name} unavailable — feature disabled`);
|
|
20
101
|
return null;
|
|
21
102
|
}
|
|
22
103
|
}
|
|
104
|
+
function loadApplication() {
|
|
105
|
+
const o = viaOverride('expo-application');
|
|
106
|
+
if (o.hit)
|
|
107
|
+
return o.mod;
|
|
108
|
+
try {
|
|
109
|
+
return require('expo-application');
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function loadUpdates() {
|
|
116
|
+
const o = viaOverride('expo-updates');
|
|
117
|
+
if (o.hit)
|
|
118
|
+
return o.mod;
|
|
119
|
+
try {
|
|
120
|
+
return require('expo-updates');
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
function warned(mod, name, onError) {
|
|
127
|
+
if (mod == null)
|
|
128
|
+
onError?.(`[rootnode] optional module ${name} unavailable — feature disabled`);
|
|
129
|
+
return mod;
|
|
130
|
+
}
|
|
23
131
|
class RootnodeClient {
|
|
24
132
|
constructor() {
|
|
25
133
|
this.core = null;
|
|
@@ -37,7 +145,7 @@ class RootnodeClient {
|
|
|
37
145
|
? `https://${cfg.dsn.match(/^https?:\/\/[^@]+@([^/]+)\//)[1]}`
|
|
38
146
|
: 'https://rootnode.app');
|
|
39
147
|
if (cfg.dsn) {
|
|
40
|
-
this.sentry =
|
|
148
|
+
this.sentry = warned(loadSentry(), '@sentry/react-native', cfg.onError);
|
|
41
149
|
this.sentry?.init?.({
|
|
42
150
|
dsn: cfg.dsn,
|
|
43
151
|
environment: cfg.environment,
|
|
@@ -118,10 +226,10 @@ class RootnodeClient {
|
|
|
118
226
|
try {
|
|
119
227
|
if (!this.cfg.apiKey)
|
|
120
228
|
return false;
|
|
121
|
-
const Notifications =
|
|
229
|
+
const Notifications = warned(loadNotifications(), 'expo-notifications', this.cfg.onError);
|
|
122
230
|
if (!Notifications)
|
|
123
231
|
return false;
|
|
124
|
-
const Device =
|
|
232
|
+
const Device = loadDevice();
|
|
125
233
|
if (Device && Device.isDevice === false)
|
|
126
234
|
return false; // simulators get nothing
|
|
127
235
|
const perm = await Notifications.getPermissionsAsync();
|
|
@@ -176,10 +284,10 @@ class RootnodeClient {
|
|
|
176
284
|
}
|
|
177
285
|
}
|
|
178
286
|
makeStorage() {
|
|
179
|
-
const AsyncStorage =
|
|
287
|
+
const AsyncStorage = loadAsyncStorage()?.default;
|
|
180
288
|
if (AsyncStorage?.getItem)
|
|
181
289
|
return AsyncStorage;
|
|
182
|
-
const FS =
|
|
290
|
+
const FS = loadFileSystemLegacy() ?? loadFileSystem();
|
|
183
291
|
if (FS?.documentDirectory && FS.readAsStringAsync) {
|
|
184
292
|
const path = (k) => `${FS.documentDirectory}${k.replace(/[^\w.-]/g, '_')}.json`;
|
|
185
293
|
return {
|
|
@@ -199,10 +307,10 @@ class RootnodeClient {
|
|
|
199
307
|
return null; // memory-only queue — still works, just not restart-proof
|
|
200
308
|
}
|
|
201
309
|
makeContext() {
|
|
202
|
-
const RN =
|
|
203
|
-
const Device =
|
|
204
|
-
const App =
|
|
205
|
-
const Updates =
|
|
310
|
+
const RN = loadReactNative();
|
|
311
|
+
const Device = loadDevice();
|
|
312
|
+
const App = loadApplication();
|
|
313
|
+
const Updates = loadUpdates();
|
|
206
314
|
return () => {
|
|
207
315
|
const ctx = {};
|
|
208
316
|
try {
|
|
@@ -226,7 +334,7 @@ class RootnodeClient {
|
|
|
226
334
|
}
|
|
227
335
|
watchAppState() {
|
|
228
336
|
try {
|
|
229
|
-
const RN =
|
|
337
|
+
const RN = warned(loadReactNative(), 'react-native', this.cfg.onError);
|
|
230
338
|
if (!RN?.AppState?.addEventListener)
|
|
231
339
|
return;
|
|
232
340
|
this.appStateSub = RN.AppState.addEventListener('change', (state) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rootnode-app/react-native",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Rootnode for React Native / Expo apps: crash reporting, analytics, sessions and push registration — one install, one init.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Doğan Aktı",
|
|
@@ -19,19 +19,19 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc -p tsconfig.build.json",
|
|
21
21
|
"test": "vitest run",
|
|
22
|
-
"prepublishOnly": "npm run test && npm run build"
|
|
22
|
+
"prepublishOnly": "npm run test && npm run build && npm run verify:bundle",
|
|
23
|
+
"verify:bundle": "esbuild dist/index.js --bundle --platform=browser --outfile=/dev/null --external:react-native --external:@sentry/react-native --external:expo-device --external:expo-notifications --external:expo-file-system --external:expo-file-system/legacy --external:expo-application --external:expo-updates --external:@react-native-async-storage/async-storage --log-level=error"
|
|
23
24
|
},
|
|
24
|
-
"
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"react-native": "*",
|
|
25
27
|
"@sentry/react-native": ">=5.0.0",
|
|
26
28
|
"expo-device": "*",
|
|
27
29
|
"expo-notifications": "*"
|
|
28
30
|
},
|
|
29
|
-
"peerDependencies": {
|
|
30
|
-
"react-native": "*"
|
|
31
|
-
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"@types/node": "^22.20.5",
|
|
34
33
|
"typescript": "^5.9.3",
|
|
35
|
-
"vitest": "^2.1.9"
|
|
34
|
+
"vitest": "^2.1.9",
|
|
35
|
+
"esbuild": "^0.24.0"
|
|
36
36
|
}
|
|
37
37
|
}
|