@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/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
+ };