@koderlabs/tasks-sdk-rn 0.1.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/LICENSE +179 -0
- package/README.md +9 -0
- package/dist/index.cjs +584 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +174 -0
- package/dist/index.d.ts +174 -0
- package/dist/index.js +560 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
- package/plugin/dist/index.cjs +126 -0
- package/plugin/dist/index.d.cts +55 -0
- package/plugin/dist/index.d.ts +55 -0
- package/plugin/dist/index.js +99 -0
package/package.json
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@koderlabs/tasks-sdk-rn",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "React Native adapter for the InstantTasks SDK — errors + network + breadcrumbs + navigation + native crash recovery.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"instanttasks",
|
|
7
|
+
"sdk",
|
|
8
|
+
"react-native",
|
|
9
|
+
"mobile"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/jawaidgadiwala/instant-tasks/tree/main/packages/sdk-rn#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jawaidgadiwala/instant-tasks/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/jawaidgadiwala/instant-tasks.git",
|
|
18
|
+
"directory": "packages/sdk-rn"
|
|
19
|
+
},
|
|
20
|
+
"license": "UNLICENSED",
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "Jawaid Gadiwala",
|
|
23
|
+
"email": "jawaidgadiwala@gmail.com",
|
|
24
|
+
"url": "https://koderlabs.com"
|
|
25
|
+
},
|
|
26
|
+
"type": "module",
|
|
27
|
+
"main": "./dist/index.cjs",
|
|
28
|
+
"module": "./dist/index.js",
|
|
29
|
+
"types": "./dist/index.d.ts",
|
|
30
|
+
"exports": {
|
|
31
|
+
".": {
|
|
32
|
+
"types": "./dist/index.d.ts",
|
|
33
|
+
"import": "./dist/index.js",
|
|
34
|
+
"require": "./dist/index.cjs"
|
|
35
|
+
},
|
|
36
|
+
"./app.plugin": {
|
|
37
|
+
"types": "./plugin/dist/index.d.ts",
|
|
38
|
+
"default": "./plugin/dist/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"dist",
|
|
43
|
+
"plugin/dist",
|
|
44
|
+
"LICENSE"
|
|
45
|
+
],
|
|
46
|
+
"sideEffects": false,
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@koderlabs/tasks-sdk": "0.1.0",
|
|
49
|
+
"@koderlabs/tasks-sdk-types": "0.1.0"
|
|
50
|
+
},
|
|
51
|
+
"peerDependencies": {
|
|
52
|
+
"react-native": ">=0.72.0",
|
|
53
|
+
"expo": ">=50.0.0"
|
|
54
|
+
},
|
|
55
|
+
"peerDependenciesMeta": {
|
|
56
|
+
"react-native": {
|
|
57
|
+
"optional": true
|
|
58
|
+
},
|
|
59
|
+
"expo": {
|
|
60
|
+
"optional": true
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"devDependencies": {
|
|
64
|
+
"@expo/config-plugins": "^9.0.0",
|
|
65
|
+
"tsup": "^8.3.0",
|
|
66
|
+
"typescript": "^5.5.0",
|
|
67
|
+
"vitest": "^2.1.0"
|
|
68
|
+
},
|
|
69
|
+
"engines": {
|
|
70
|
+
"node": ">=20"
|
|
71
|
+
},
|
|
72
|
+
"publishConfig": {
|
|
73
|
+
"access": "restricted"
|
|
74
|
+
},
|
|
75
|
+
"app.plugin.js": "./plugin/dist/index.cjs",
|
|
76
|
+
"scripts": {
|
|
77
|
+
"build": "tsup && tsup --config plugin/tsup.config.ts",
|
|
78
|
+
"dev": "tsup --watch",
|
|
79
|
+
"test": "vitest run",
|
|
80
|
+
"test:watch": "vitest"
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// plugin/src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
CRASH_FILE: () => CRASH_FILE,
|
|
24
|
+
default: () => src_default,
|
|
25
|
+
patchAppDelegate: () => patchAppDelegate,
|
|
26
|
+
patchMainApplication: () => patchMainApplication
|
|
27
|
+
});
|
|
28
|
+
module.exports = __toCommonJS(src_exports);
|
|
29
|
+
var import_config_plugins = require("@expo/config-plugins");
|
|
30
|
+
var CRASH_FILE = "tasks-native-crash.json";
|
|
31
|
+
var IOS_HEADER = `// koderlabs/tasks-sdk-rn \u2014 native crash hook installed by config plugin`;
|
|
32
|
+
var IOS_HOOK = `
|
|
33
|
+
// \u2500\u2500\u2500 koderlabs/tasks-sdk-rn native crash hook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
34
|
+
static NSUncaughtExceptionHandler *__tasksPrevExceptionHandler = NULL;
|
|
35
|
+
static void __tasksUncaughtExceptionHandler(NSException *exception) {
|
|
36
|
+
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
37
|
+
NSString *docs = [paths firstObject];
|
|
38
|
+
if (docs) {
|
|
39
|
+
NSDictionary *payload = @{
|
|
40
|
+
@"platform": @"ios",
|
|
41
|
+
@"name": exception.name ?: @"NSException",
|
|
42
|
+
@"reason": exception.reason ?: @"",
|
|
43
|
+
@"callStackSymbols": exception.callStackSymbols ?: @[],
|
|
44
|
+
@"userInfo": exception.userInfo ?: @{},
|
|
45
|
+
@"timestamp": [[NSDate date] description] ?: @"",
|
|
46
|
+
};
|
|
47
|
+
NSError *err = nil;
|
|
48
|
+
NSData *json = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&err];
|
|
49
|
+
if (!err && json) {
|
|
50
|
+
NSString *path = [docs stringByAppendingPathComponent:@"${CRASH_FILE}"];
|
|
51
|
+
[json writeToFile:path atomically:YES];
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
if (__tasksPrevExceptionHandler) __tasksPrevExceptionHandler(exception);
|
|
55
|
+
}
|
|
56
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
57
|
+
`.trim();
|
|
58
|
+
var IOS_INSTALL = ` // koderlabs/tasks-sdk-rn \u2014 chain into any handler set by RN/Expo.
|
|
59
|
+
__tasksPrevExceptionHandler = NSGetUncaughtExceptionHandler();
|
|
60
|
+
NSSetUncaughtExceptionHandler(&__tasksUncaughtExceptionHandler);`;
|
|
61
|
+
var ANDROID_HOOK = `
|
|
62
|
+
// \u2500\u2500\u2500 koderlabs/tasks-sdk-rn native crash hook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
63
|
+
final Thread.UncaughtExceptionHandler __tasksPrevHandler =
|
|
64
|
+
Thread.getDefaultUncaughtExceptionHandler();
|
|
65
|
+
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
|
66
|
+
try {
|
|
67
|
+
java.io.File f = new java.io.File(getFilesDir(), "${CRASH_FILE}");
|
|
68
|
+
org.json.JSONObject payload = new org.json.JSONObject();
|
|
69
|
+
payload.put("platform", "android");
|
|
70
|
+
payload.put("name", throwable.getClass().getName());
|
|
71
|
+
payload.put("message", throwable.getMessage() != null ? throwable.getMessage() : "");
|
|
72
|
+
java.io.StringWriter sw = new java.io.StringWriter();
|
|
73
|
+
throwable.printStackTrace(new java.io.PrintWriter(sw));
|
|
74
|
+
payload.put("stackTrace", sw.toString());
|
|
75
|
+
payload.put("threadName", thread.getName());
|
|
76
|
+
payload.put("timestamp", new java.util.Date().toString());
|
|
77
|
+
java.io.FileWriter fw = new java.io.FileWriter(f, false);
|
|
78
|
+
fw.write(payload.toString());
|
|
79
|
+
fw.close();
|
|
80
|
+
} catch (Throwable ignored) { /* must not raise from inside the handler */ }
|
|
81
|
+
if (__tasksPrevHandler != null) __tasksPrevHandler.uncaughtException(thread, throwable);
|
|
82
|
+
});
|
|
83
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`.trim();
|
|
84
|
+
function patchAppDelegate(contents) {
|
|
85
|
+
if (contents.includes("koderlabs/tasks-sdk-rn native crash hook")) return contents;
|
|
86
|
+
let next = contents.includes("@implementation AppDelegate") ? contents.replace(/@implementation AppDelegate/, `${IOS_HEADER}
|
|
87
|
+
${IOS_HOOK}
|
|
88
|
+
|
|
89
|
+
@implementation AppDelegate`) : `${IOS_HEADER}
|
|
90
|
+
${IOS_HOOK}
|
|
91
|
+
|
|
92
|
+
${contents}`;
|
|
93
|
+
next = next.replace(
|
|
94
|
+
/(- \(BOOL\)application:.*didFinishLaunchingWithOptions:.*\{)/,
|
|
95
|
+
`$1
|
|
96
|
+
${IOS_INSTALL}
|
|
97
|
+
`
|
|
98
|
+
);
|
|
99
|
+
return next;
|
|
100
|
+
}
|
|
101
|
+
function patchMainApplication(contents) {
|
|
102
|
+
if (contents.includes("koderlabs/tasks-sdk-rn native crash hook")) return contents;
|
|
103
|
+
return contents.replace(
|
|
104
|
+
/(super\.onCreate\(\);?)/,
|
|
105
|
+
`$1
|
|
106
|
+
${ANDROID_HOOK}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
var withTasksNativeCrash = (config) => {
|
|
110
|
+
config = (0, import_config_plugins.withAppDelegate)(config, (cfg) => {
|
|
111
|
+
cfg.modResults.contents = patchAppDelegate(cfg.modResults.contents);
|
|
112
|
+
return cfg;
|
|
113
|
+
});
|
|
114
|
+
config = (0, import_config_plugins.withMainApplication)(config, (cfg) => {
|
|
115
|
+
cfg.modResults.contents = patchMainApplication(cfg.modResults.contents);
|
|
116
|
+
return cfg;
|
|
117
|
+
});
|
|
118
|
+
return config;
|
|
119
|
+
};
|
|
120
|
+
var src_default = withTasksNativeCrash;
|
|
121
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
122
|
+
0 && (module.exports = {
|
|
123
|
+
CRASH_FILE,
|
|
124
|
+
patchAppDelegate,
|
|
125
|
+
patchMainApplication
|
|
126
|
+
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expo Config Plugin: native crash capture for @koderlabs/tasks-sdk-rn.
|
|
5
|
+
*
|
|
6
|
+
* Installs platform-specific uncaught-exception handlers at native boot:
|
|
7
|
+
*
|
|
8
|
+
* iOS — wraps `NSSetUncaughtExceptionHandler` in AppDelegate.mm so any
|
|
9
|
+
* ObjC NSException that escapes the runloop is serialised to JSON
|
|
10
|
+
* and written to the app's Documents directory.
|
|
11
|
+
*
|
|
12
|
+
* Android — registers a `Thread.UncaughtExceptionHandler` in MainApplication
|
|
13
|
+
* that catches every uncaught Java/Kotlin throwable, serialises
|
|
14
|
+
* the stack trace, and writes JSON to filesDir.
|
|
15
|
+
*
|
|
16
|
+
* Both handlers wrap (don't replace) any handler installed by React Native or
|
|
17
|
+
* Expo so the existing redbox / crash-reporter behaviour is preserved.
|
|
18
|
+
*
|
|
19
|
+
* On the next cold start, `@koderlabs/tasks-sdk-rn` reads the file (if any),
|
|
20
|
+
* ships it as an event with `mechanism: 'native_crash'`, then deletes it.
|
|
21
|
+
*
|
|
22
|
+
* **Limitations** (acceptable for v1):
|
|
23
|
+
* - C-level signals (SIGSEGV, SIGBUS, SIGABRT) are NOT caught. Capturing
|
|
24
|
+
* those needs PLCrashReporter (iOS) and Breakpad/Crashpad (Android).
|
|
25
|
+
* Add `@koderlabs/tasks-sdk-rn-native` as a follow-up package if needed.
|
|
26
|
+
* - Android ANRs (main-thread blocked >5s) are not detected — requires a
|
|
27
|
+
* watchdog thread; deferred.
|
|
28
|
+
* - The handler runs on the thread that crashed; only synchronous file
|
|
29
|
+
* write is safe, so the JSON payload is intentionally small.
|
|
30
|
+
*
|
|
31
|
+
* Usage in `app.json`:
|
|
32
|
+
* "plugins": [
|
|
33
|
+
* "@koderlabs/tasks-sdk-rn"
|
|
34
|
+
* ]
|
|
35
|
+
*
|
|
36
|
+
* The plugin auto-resolves at prebuild via Expo's discovery.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/** Filename written by both platforms; SDK reads this on next launch. */
|
|
40
|
+
declare const CRASH_FILE = "tasks-native-crash.json";
|
|
41
|
+
/**
|
|
42
|
+
* Inject the iOS uncaught-exception hook into the project's AppDelegate.mm
|
|
43
|
+
* (Expo's Obj-C(++) bridge entry point). Idempotent — if the marker is
|
|
44
|
+
* already present we leave the file alone.
|
|
45
|
+
*/
|
|
46
|
+
declare function patchAppDelegate(contents: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Inject the Android uncaught-exception hook into MainApplication.
|
|
49
|
+
* Expo SDK ≥ 50 uses Kotlin (`MainApplication.kt`); we target the
|
|
50
|
+
* compatible Java syntax which Kotlin's interop accepts.
|
|
51
|
+
*/
|
|
52
|
+
declare function patchMainApplication(contents: string): string;
|
|
53
|
+
declare const withTasksNativeCrash: ConfigPlugin;
|
|
54
|
+
|
|
55
|
+
export { CRASH_FILE, withTasksNativeCrash as default, patchAppDelegate, patchMainApplication };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { ConfigPlugin } from '@expo/config-plugins';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Expo Config Plugin: native crash capture for @koderlabs/tasks-sdk-rn.
|
|
5
|
+
*
|
|
6
|
+
* Installs platform-specific uncaught-exception handlers at native boot:
|
|
7
|
+
*
|
|
8
|
+
* iOS — wraps `NSSetUncaughtExceptionHandler` in AppDelegate.mm so any
|
|
9
|
+
* ObjC NSException that escapes the runloop is serialised to JSON
|
|
10
|
+
* and written to the app's Documents directory.
|
|
11
|
+
*
|
|
12
|
+
* Android — registers a `Thread.UncaughtExceptionHandler` in MainApplication
|
|
13
|
+
* that catches every uncaught Java/Kotlin throwable, serialises
|
|
14
|
+
* the stack trace, and writes JSON to filesDir.
|
|
15
|
+
*
|
|
16
|
+
* Both handlers wrap (don't replace) any handler installed by React Native or
|
|
17
|
+
* Expo so the existing redbox / crash-reporter behaviour is preserved.
|
|
18
|
+
*
|
|
19
|
+
* On the next cold start, `@koderlabs/tasks-sdk-rn` reads the file (if any),
|
|
20
|
+
* ships it as an event with `mechanism: 'native_crash'`, then deletes it.
|
|
21
|
+
*
|
|
22
|
+
* **Limitations** (acceptable for v1):
|
|
23
|
+
* - C-level signals (SIGSEGV, SIGBUS, SIGABRT) are NOT caught. Capturing
|
|
24
|
+
* those needs PLCrashReporter (iOS) and Breakpad/Crashpad (Android).
|
|
25
|
+
* Add `@koderlabs/tasks-sdk-rn-native` as a follow-up package if needed.
|
|
26
|
+
* - Android ANRs (main-thread blocked >5s) are not detected — requires a
|
|
27
|
+
* watchdog thread; deferred.
|
|
28
|
+
* - The handler runs on the thread that crashed; only synchronous file
|
|
29
|
+
* write is safe, so the JSON payload is intentionally small.
|
|
30
|
+
*
|
|
31
|
+
* Usage in `app.json`:
|
|
32
|
+
* "plugins": [
|
|
33
|
+
* "@koderlabs/tasks-sdk-rn"
|
|
34
|
+
* ]
|
|
35
|
+
*
|
|
36
|
+
* The plugin auto-resolves at prebuild via Expo's discovery.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/** Filename written by both platforms; SDK reads this on next launch. */
|
|
40
|
+
declare const CRASH_FILE = "tasks-native-crash.json";
|
|
41
|
+
/**
|
|
42
|
+
* Inject the iOS uncaught-exception hook into the project's AppDelegate.mm
|
|
43
|
+
* (Expo's Obj-C(++) bridge entry point). Idempotent — if the marker is
|
|
44
|
+
* already present we leave the file alone.
|
|
45
|
+
*/
|
|
46
|
+
declare function patchAppDelegate(contents: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Inject the Android uncaught-exception hook into MainApplication.
|
|
49
|
+
* Expo SDK ≥ 50 uses Kotlin (`MainApplication.kt`); we target the
|
|
50
|
+
* compatible Java syntax which Kotlin's interop accepts.
|
|
51
|
+
*/
|
|
52
|
+
declare function patchMainApplication(contents: string): string;
|
|
53
|
+
declare const withTasksNativeCrash: ConfigPlugin;
|
|
54
|
+
|
|
55
|
+
export { CRASH_FILE, withTasksNativeCrash as default, patchAppDelegate, patchMainApplication };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// plugin/src/index.ts
|
|
2
|
+
import { withAppDelegate, withMainApplication } from "@expo/config-plugins";
|
|
3
|
+
var CRASH_FILE = "tasks-native-crash.json";
|
|
4
|
+
var IOS_HEADER = `// koderlabs/tasks-sdk-rn \u2014 native crash hook installed by config plugin`;
|
|
5
|
+
var IOS_HOOK = `
|
|
6
|
+
// \u2500\u2500\u2500 koderlabs/tasks-sdk-rn native crash hook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
7
|
+
static NSUncaughtExceptionHandler *__tasksPrevExceptionHandler = NULL;
|
|
8
|
+
static void __tasksUncaughtExceptionHandler(NSException *exception) {
|
|
9
|
+
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
|
|
10
|
+
NSString *docs = [paths firstObject];
|
|
11
|
+
if (docs) {
|
|
12
|
+
NSDictionary *payload = @{
|
|
13
|
+
@"platform": @"ios",
|
|
14
|
+
@"name": exception.name ?: @"NSException",
|
|
15
|
+
@"reason": exception.reason ?: @"",
|
|
16
|
+
@"callStackSymbols": exception.callStackSymbols ?: @[],
|
|
17
|
+
@"userInfo": exception.userInfo ?: @{},
|
|
18
|
+
@"timestamp": [[NSDate date] description] ?: @"",
|
|
19
|
+
};
|
|
20
|
+
NSError *err = nil;
|
|
21
|
+
NSData *json = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&err];
|
|
22
|
+
if (!err && json) {
|
|
23
|
+
NSString *path = [docs stringByAppendingPathComponent:@"${CRASH_FILE}"];
|
|
24
|
+
[json writeToFile:path atomically:YES];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (__tasksPrevExceptionHandler) __tasksPrevExceptionHandler(exception);
|
|
28
|
+
}
|
|
29
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
30
|
+
`.trim();
|
|
31
|
+
var IOS_INSTALL = ` // koderlabs/tasks-sdk-rn \u2014 chain into any handler set by RN/Expo.
|
|
32
|
+
__tasksPrevExceptionHandler = NSGetUncaughtExceptionHandler();
|
|
33
|
+
NSSetUncaughtExceptionHandler(&__tasksUncaughtExceptionHandler);`;
|
|
34
|
+
var ANDROID_HOOK = `
|
|
35
|
+
// \u2500\u2500\u2500 koderlabs/tasks-sdk-rn native crash hook \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
|
|
36
|
+
final Thread.UncaughtExceptionHandler __tasksPrevHandler =
|
|
37
|
+
Thread.getDefaultUncaughtExceptionHandler();
|
|
38
|
+
Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
|
|
39
|
+
try {
|
|
40
|
+
java.io.File f = new java.io.File(getFilesDir(), "${CRASH_FILE}");
|
|
41
|
+
org.json.JSONObject payload = new org.json.JSONObject();
|
|
42
|
+
payload.put("platform", "android");
|
|
43
|
+
payload.put("name", throwable.getClass().getName());
|
|
44
|
+
payload.put("message", throwable.getMessage() != null ? throwable.getMessage() : "");
|
|
45
|
+
java.io.StringWriter sw = new java.io.StringWriter();
|
|
46
|
+
throwable.printStackTrace(new java.io.PrintWriter(sw));
|
|
47
|
+
payload.put("stackTrace", sw.toString());
|
|
48
|
+
payload.put("threadName", thread.getName());
|
|
49
|
+
payload.put("timestamp", new java.util.Date().toString());
|
|
50
|
+
java.io.FileWriter fw = new java.io.FileWriter(f, false);
|
|
51
|
+
fw.write(payload.toString());
|
|
52
|
+
fw.close();
|
|
53
|
+
} catch (Throwable ignored) { /* must not raise from inside the handler */ }
|
|
54
|
+
if (__tasksPrevHandler != null) __tasksPrevHandler.uncaughtException(thread, throwable);
|
|
55
|
+
});
|
|
56
|
+
// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`.trim();
|
|
57
|
+
function patchAppDelegate(contents) {
|
|
58
|
+
if (contents.includes("koderlabs/tasks-sdk-rn native crash hook")) return contents;
|
|
59
|
+
let next = contents.includes("@implementation AppDelegate") ? contents.replace(/@implementation AppDelegate/, `${IOS_HEADER}
|
|
60
|
+
${IOS_HOOK}
|
|
61
|
+
|
|
62
|
+
@implementation AppDelegate`) : `${IOS_HEADER}
|
|
63
|
+
${IOS_HOOK}
|
|
64
|
+
|
|
65
|
+
${contents}`;
|
|
66
|
+
next = next.replace(
|
|
67
|
+
/(- \(BOOL\)application:.*didFinishLaunchingWithOptions:.*\{)/,
|
|
68
|
+
`$1
|
|
69
|
+
${IOS_INSTALL}
|
|
70
|
+
`
|
|
71
|
+
);
|
|
72
|
+
return next;
|
|
73
|
+
}
|
|
74
|
+
function patchMainApplication(contents) {
|
|
75
|
+
if (contents.includes("koderlabs/tasks-sdk-rn native crash hook")) return contents;
|
|
76
|
+
return contents.replace(
|
|
77
|
+
/(super\.onCreate\(\);?)/,
|
|
78
|
+
`$1
|
|
79
|
+
${ANDROID_HOOK}`
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
var withTasksNativeCrash = (config) => {
|
|
83
|
+
config = withAppDelegate(config, (cfg) => {
|
|
84
|
+
cfg.modResults.contents = patchAppDelegate(cfg.modResults.contents);
|
|
85
|
+
return cfg;
|
|
86
|
+
});
|
|
87
|
+
config = withMainApplication(config, (cfg) => {
|
|
88
|
+
cfg.modResults.contents = patchMainApplication(cfg.modResults.contents);
|
|
89
|
+
return cfg;
|
|
90
|
+
});
|
|
91
|
+
return config;
|
|
92
|
+
};
|
|
93
|
+
var src_default = withTasksNativeCrash;
|
|
94
|
+
export {
|
|
95
|
+
CRASH_FILE,
|
|
96
|
+
src_default as default,
|
|
97
|
+
patchAppDelegate,
|
|
98
|
+
patchMainApplication
|
|
99
|
+
};
|