@scalebun/react-native 1.4.0 → 1.6.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/README.md +33 -0
- package/android/src/main/java/com/scalebun/rn/ota/BsPatch.kt +182 -0
- package/android/src/main/java/com/scalebun/rn/ota/ScaleBunOtaModule.kt +88 -13
- package/android/src/oldarch/java/com/scalebun/rn/crash/ScaleBunCrashSpec.kt +16 -0
- package/bin/lib/androidCodemod.js +370 -0
- package/bin/scalebun.js +305 -13
- package/dist/scalebun.full.js +129 -97
- package/dist/scalebun.full.js.map +1 -1
- package/dist/scalebun.slim.js +129 -97
- package/dist/scalebun.slim.js.map +1 -1
- package/ios/Ota/BsPatch.swift +180 -0
- package/ios/Ota/OtaSlotManager.swift +59 -2
- package/ios/Ota/ScaleBunOtaModule.swift +89 -10
- package/lib/commonjs/analytics/EventTracker.js +21 -1
- package/lib/commonjs/analytics/EventTracker.js.map +1 -1
- package/lib/commonjs/core/constants/version.js +1 -1
- package/lib/commonjs/core/id/installationId.js +55 -0
- package/lib/commonjs/core/id/installationId.js.map +1 -0
- package/lib/commonjs/debug/bootstrap.js +12 -0
- package/lib/commonjs/debug/bootstrap.js.map +1 -1
- package/lib/commonjs/features/ota/OtaOrchestrator.js +2 -30
- package/lib/commonjs/features/ota/OtaOrchestrator.js.map +1 -1
- package/lib/commonjs/features/session/BackendSessionAdapter.js +8 -0
- package/lib/commonjs/features/session/BackendSessionAdapter.js.map +1 -1
- package/lib/module/analytics/EventTracker.js +21 -1
- package/lib/module/analytics/EventTracker.js.map +1 -1
- package/lib/module/core/constants/version.js +1 -1
- package/lib/module/core/id/installationId.js +50 -0
- package/lib/module/core/id/installationId.js.map +1 -0
- package/lib/module/debug/bootstrap.js +12 -0
- package/lib/module/debug/bootstrap.js.map +1 -1
- package/lib/module/features/ota/OtaOrchestrator.js +1 -29
- package/lib/module/features/ota/OtaOrchestrator.js.map +1 -1
- package/lib/module/features/session/BackendSessionAdapter.js +8 -0
- package/lib/module/features/session/BackendSessionAdapter.js.map +1 -1
- package/lib/typescript/analytics/EventTracker.d.ts.map +1 -1
- package/lib/typescript/core/constants/version.d.ts +1 -1
- package/lib/typescript/core/id/installationId.d.ts +20 -0
- package/lib/typescript/core/id/installationId.d.ts.map +1 -0
- package/lib/typescript/debug/bootstrap.d.ts +18 -0
- package/lib/typescript/debug/bootstrap.d.ts.map +1 -1
- package/lib/typescript/features/ota/OtaOrchestrator.d.ts.map +1 -1
- package/lib/typescript/features/session/BackendSessionAdapter.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/analytics/EventTracker.ts +21 -1
- package/src/core/constants/version.ts +1 -1
- package/src/core/id/installationId.ts +52 -0
- package/src/debug/bootstrap.ts +36 -0
- package/src/features/ota/OtaOrchestrator.ts +1 -30
- package/src/features/session/BackendSessionAdapter.ts +8 -0
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* Android MainApplication codemod for `npx scalebun init android`.
|
|
4
|
+
*
|
|
5
|
+
* Pure, dependency-free string transforms (no fs / process / RN imports) so they
|
|
6
|
+
* are unit-testable with plain fixtures and safe to run anywhere. The CLI
|
|
7
|
+
* (bin/scalebun.js) handles disk IO, flags, and reporting; this module only
|
|
8
|
+
* decides WHAT the patched source should be.
|
|
9
|
+
*
|
|
10
|
+
* It wires OTA bundle resolution into the host app's MainApplication so a
|
|
11
|
+
* downloaded update actually LOADS instead of the app silently running the
|
|
12
|
+
* bundle baked into the APK. The required call is the static
|
|
13
|
+
* `ScaleBunOtaModule.getJSBundleFile(context)`, wired in one of two shapes
|
|
14
|
+
* depending on the React Native version:
|
|
15
|
+
*
|
|
16
|
+
* - RN < 0.82 (ReactNativeHost still exists): add
|
|
17
|
+
* override fun getJSBundleFile(): String? =
|
|
18
|
+
* ScaleBunOtaModule.getJSBundleFile(this@MainApplication) ?: super.getJSBundleFile()
|
|
19
|
+
* inside the `object : *ReactNativeHost { … }` block.
|
|
20
|
+
*
|
|
21
|
+
* - RN >= 0.82 (ReactNativeHost removed): pass the path to getDefaultReactHost:
|
|
22
|
+
* getDefaultReactHost(
|
|
23
|
+
* …,
|
|
24
|
+
* jsBundleFilePath = ScaleBunOtaModule.getJSBundleFile(applicationContext),
|
|
25
|
+
* )
|
|
26
|
+
*
|
|
27
|
+
* The version decides the shape — mirroring the check in
|
|
28
|
+
* @scalebun/cli src/commands/doctor.ts. Every transform is idempotent (running
|
|
29
|
+
* twice is a no-op). Java MainApplication and unrecognized structures are NOT
|
|
30
|
+
* auto-patched — they return an exact manual snippet instead of a risky edit.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
const OTA_IMPORT = 'import com.scalebun.rn.ota.ScaleBunOtaModule';
|
|
34
|
+
const BUNDLE_CALL = 'ScaleBunOtaModule.getJSBundleFile';
|
|
35
|
+
const MANAGED_TAG = 'ScaleBun OTA bundle resolution';
|
|
36
|
+
|
|
37
|
+
// ── MainApplication discovery ────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Find the host MainApplication under <androidDir>. Returns { path, kind } where
|
|
41
|
+
* kind is 'kotlin' (.kt) or 'java' (.java), or null when none is found. Kotlin
|
|
42
|
+
* is preferred. `fsLike`/`pathLike` are injected for testability.
|
|
43
|
+
*/
|
|
44
|
+
function findMainApplication(androidDir, fsLike, pathLike) {
|
|
45
|
+
const fs = fsLike;
|
|
46
|
+
const path = pathLike;
|
|
47
|
+
const root = path.join(androidDir, 'app', 'src', 'main');
|
|
48
|
+
|
|
49
|
+
// Walk src/main/{java,kotlin}/** for MainApplication.kt/.java.
|
|
50
|
+
const found = { kotlin: null, java: null };
|
|
51
|
+
const walk = (dir) => {
|
|
52
|
+
let entries = [];
|
|
53
|
+
try {
|
|
54
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
55
|
+
} catch {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const p = path.join(dir, entry.name);
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
walk(p);
|
|
62
|
+
} else if (entry.name === 'MainApplication.kt' && !found.kotlin) {
|
|
63
|
+
found.kotlin = p;
|
|
64
|
+
} else if (entry.name === 'MainApplication.java' && !found.java) {
|
|
65
|
+
found.java = p;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
walk(root);
|
|
70
|
+
|
|
71
|
+
if (found.kotlin) return { path: found.kotlin, kind: 'kotlin' };
|
|
72
|
+
if (found.java) return { path: found.java, kind: 'java' };
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── version helper ────────────────────────────────────────────────────────────
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* True when RN >= 0.82 (ReactNativeHost removed → must pass jsBundleFilePath to
|
|
80
|
+
* getDefaultReactHost). `minor` is [major, minor] or null when unknown; when
|
|
81
|
+
* unknown we assume the modern shape.
|
|
82
|
+
*/
|
|
83
|
+
function isReactHostEra(minor) {
|
|
84
|
+
if (!minor) return true;
|
|
85
|
+
return minor[0] > 0 || minor[1] >= 82;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ── analysis ────────────────────────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
/** Report which OTA wiring pieces are already present in a Kotlin MainApplication. */
|
|
91
|
+
function analyzeKotlin(source) {
|
|
92
|
+
const s = String(source);
|
|
93
|
+
return {
|
|
94
|
+
hasImport: s.includes(OTA_IMPORT),
|
|
95
|
+
hasBundleWiring: s.includes(BUNDLE_CALL),
|
|
96
|
+
hasReactNativeHostObject: /object\s*:\s*\w*ReactNativeHost\s*\(/.test(s),
|
|
97
|
+
hasGetDefaultReactHost: s.includes('getDefaultReactHost('),
|
|
98
|
+
hasJsBundleFilePathArg: s.includes('jsBundleFilePath'),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* True when OTA bundle resolution is fully wired for this RN version (for
|
|
104
|
+
* --check). On RN >= 0.82 the wiring must go through the jsBundleFilePath arg;
|
|
105
|
+
* before that any getJSBundleFile call is enough.
|
|
106
|
+
*/
|
|
107
|
+
function isFullyWiredKotlin(source, minor) {
|
|
108
|
+
const a = analyzeKotlin(source);
|
|
109
|
+
if (!a.hasImport || !a.hasBundleWiring) return false;
|
|
110
|
+
if (isReactHostEra(minor)) return a.hasJsBundleFilePathArg;
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// ── helpers ─────────────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
/** Insert `lineToAdd` immediately after the last top-of-file Kotlin import. */
|
|
117
|
+
function insertAfterLastImport(source, lineToAdd) {
|
|
118
|
+
const lines = source.split('\n');
|
|
119
|
+
let lastImport = -1;
|
|
120
|
+
for (let i = 0; i < lines.length; i++) {
|
|
121
|
+
if (/^\s*import\b/.test(lines[i])) lastImport = i;
|
|
122
|
+
// Stop scanning once the class/object declaration begins.
|
|
123
|
+
if (/^\s*(class|object)\b/.test(lines[i])) break;
|
|
124
|
+
}
|
|
125
|
+
if (lastImport === -1) {
|
|
126
|
+
// No imports — put it after the package line, else prepend.
|
|
127
|
+
for (let i = 0; i < lines.length; i++) {
|
|
128
|
+
if (/^\s*package\b/.test(lines[i])) {
|
|
129
|
+
lines.splice(i + 1, 0, '', lineToAdd);
|
|
130
|
+
return lines.join('\n');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return lineToAdd + '\n' + source;
|
|
134
|
+
}
|
|
135
|
+
lines.splice(lastImport + 1, 0, lineToAdd);
|
|
136
|
+
return lines.join('\n');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Leading whitespace of the first non-empty line at/after `fromIdx`. */
|
|
140
|
+
function bodyIndentAfter(source, fromIdx) {
|
|
141
|
+
const rest = source.slice(fromIdx).split('\n');
|
|
142
|
+
for (const line of rest) {
|
|
143
|
+
if (line.trim() === '') continue;
|
|
144
|
+
return (line.match(/^\s*/) || [''])[0];
|
|
145
|
+
}
|
|
146
|
+
return ' ';
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Insert the getJSBundleFile() override into the `object : *ReactNativeHost(…) {`
|
|
151
|
+
* block (RN < 0.82). Returns { source, inserted, reason }.
|
|
152
|
+
*/
|
|
153
|
+
function insertIntoReactNativeHostObject(source, methodText) {
|
|
154
|
+
const objRe = /object\s*:\s*\w*ReactNativeHost\s*\([^)]*\)\s*\{/;
|
|
155
|
+
const m = objRe.exec(source);
|
|
156
|
+
if (!m) return { source, inserted: false, reason: 'no-object' };
|
|
157
|
+
const insertAt = m.index + m[0].length; // right after the opening `{`
|
|
158
|
+
const indent = bodyIndentAfter(source, insertAt);
|
|
159
|
+
const block = '\n' + methodText(indent);
|
|
160
|
+
return {
|
|
161
|
+
source: source.slice(0, insertAt) + block + source.slice(insertAt),
|
|
162
|
+
inserted: true,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Insert a named argument into a call, respecting single-line vs multi-line
|
|
168
|
+
* Kotlin argument style. Returns { source, inserted, reason }.
|
|
169
|
+
*/
|
|
170
|
+
function insertArgIntoCall(source, callName, argText) {
|
|
171
|
+
const callIdx = source.indexOf(callName + '(');
|
|
172
|
+
if (callIdx === -1) return { source, inserted: false, reason: 'no-call' };
|
|
173
|
+
const openIdx = source.indexOf('(', callIdx);
|
|
174
|
+
|
|
175
|
+
// Find the matching close paren (calls contain nested parens like
|
|
176
|
+
// PackageList(this).packages, so a naive indexOf(')') is wrong).
|
|
177
|
+
let depth = 0;
|
|
178
|
+
let closeIdx = -1;
|
|
179
|
+
for (let i = openIdx; i < source.length; i++) {
|
|
180
|
+
const c = source[i];
|
|
181
|
+
if (c === '(') depth++;
|
|
182
|
+
else if (c === ')') {
|
|
183
|
+
depth--;
|
|
184
|
+
if (depth === 0) {
|
|
185
|
+
closeIdx = i;
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (closeIdx === -1) return { source, inserted: false, reason: 'no-close' };
|
|
191
|
+
|
|
192
|
+
const inner = source.slice(openIdx + 1, closeIdx);
|
|
193
|
+
const isMultiline = inner.includes('\n');
|
|
194
|
+
|
|
195
|
+
// Last meaningful (non-whitespace) char before the close paren.
|
|
196
|
+
let j = closeIdx - 1;
|
|
197
|
+
while (j > openIdx && /\s/.test(source[j])) j--;
|
|
198
|
+
const lastChar = source[j];
|
|
199
|
+
|
|
200
|
+
let insertText;
|
|
201
|
+
if (inner.trim() === '') {
|
|
202
|
+
insertText = argText;
|
|
203
|
+
} else if (isMultiline) {
|
|
204
|
+
// Align the new argument with the last existing argument line (the line
|
|
205
|
+
// where `j` sits) so the call reads consistently, not with the closing
|
|
206
|
+
// paren's indentation.
|
|
207
|
+
const argLineStart = source.lastIndexOf('\n', j) + 1;
|
|
208
|
+
const argIndent = (source.slice(argLineStart, j).match(/^\s*/) || [''])[0];
|
|
209
|
+
insertText =
|
|
210
|
+
lastChar === ','
|
|
211
|
+
? `\n${argIndent}${argText},`
|
|
212
|
+
: `,\n${argIndent}${argText}`;
|
|
213
|
+
} else {
|
|
214
|
+
insertText = lastChar === ',' ? ` ${argText},` : `, ${argText}`;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
source: source.slice(0, j + 1) + insertText + source.slice(j + 1),
|
|
219
|
+
inserted: true,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// ── the patch ─────────────────────────────────────────────────────────────────
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Patch a Kotlin MainApplication for OTA bundle resolution. `opts.minor` is the
|
|
227
|
+
* [major, minor] React Native version (or null). Returns:
|
|
228
|
+
* { changed, source, summary[], manual[] }
|
|
229
|
+
*/
|
|
230
|
+
function patchKotlin(source, opts = {}) {
|
|
231
|
+
let out = String(source);
|
|
232
|
+
const summary = [];
|
|
233
|
+
const manual = [];
|
|
234
|
+
const a = analyzeKotlin(out);
|
|
235
|
+
const reactHostEra = isReactHostEra(opts.minor);
|
|
236
|
+
|
|
237
|
+
// Already fully wired for this era → no-op.
|
|
238
|
+
if (isFullyWiredKotlin(out, opts.minor)) {
|
|
239
|
+
return { changed: false, source: out, summary, manual };
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
if (reactHostEra) {
|
|
243
|
+
// RN >= 0.82: pass jsBundleFilePath to getDefaultReactHost(...).
|
|
244
|
+
if (!a.hasGetDefaultReactHost) {
|
|
245
|
+
manual.push(reactHostManual());
|
|
246
|
+
return { changed: false, source: out, summary, manual };
|
|
247
|
+
}
|
|
248
|
+
if (!a.hasImport) {
|
|
249
|
+
out = insertAfterLastImport(out, OTA_IMPORT);
|
|
250
|
+
summary.push(`Added import: ${OTA_IMPORT}`);
|
|
251
|
+
}
|
|
252
|
+
if (!a.hasJsBundleFilePathArg) {
|
|
253
|
+
const r = insertArgIntoCall(
|
|
254
|
+
out,
|
|
255
|
+
'getDefaultReactHost',
|
|
256
|
+
'jsBundleFilePath = ScaleBunOtaModule.getJSBundleFile(applicationContext)',
|
|
257
|
+
);
|
|
258
|
+
if (r.inserted) {
|
|
259
|
+
out = r.source;
|
|
260
|
+
summary.push(
|
|
261
|
+
'Patched getDefaultReactHost(...) → added jsBundleFilePath = ScaleBunOtaModule.getJSBundleFile(applicationContext)',
|
|
262
|
+
);
|
|
263
|
+
} else {
|
|
264
|
+
manual.push(reactHostManual());
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
} else {
|
|
268
|
+
// RN < 0.82: add getJSBundleFile() override inside the ReactNativeHost object.
|
|
269
|
+
if (!a.hasReactNativeHostObject) {
|
|
270
|
+
manual.push(legacyManual());
|
|
271
|
+
return { changed: false, source: out, summary, manual };
|
|
272
|
+
}
|
|
273
|
+
if (!a.hasImport) {
|
|
274
|
+
out = insertAfterLastImport(out, OTA_IMPORT);
|
|
275
|
+
summary.push(`Added import: ${OTA_IMPORT}`);
|
|
276
|
+
}
|
|
277
|
+
if (!a.hasBundleWiring) {
|
|
278
|
+
const r = insertIntoReactNativeHostObject(
|
|
279
|
+
out,
|
|
280
|
+
(indent) =>
|
|
281
|
+
`${indent}// ${MANAGED_TAG} (added by \`npx scalebun init android\`)\n` +
|
|
282
|
+
`${indent}override fun getJSBundleFile(): String? =\n` +
|
|
283
|
+
`${indent} ScaleBunOtaModule.getJSBundleFile(this@MainApplication) ?: super.getJSBundleFile()\n`,
|
|
284
|
+
);
|
|
285
|
+
if (r.inserted) {
|
|
286
|
+
out = r.source;
|
|
287
|
+
summary.push(
|
|
288
|
+
'Added getJSBundleFile() override → ScaleBunOtaModule.getJSBundleFile(this@MainApplication)',
|
|
289
|
+
);
|
|
290
|
+
} else {
|
|
291
|
+
manual.push(legacyManual());
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return { changed: out !== String(source), source: out, summary, manual };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ── manual instructions ─────────────────────────────────────────────────────
|
|
300
|
+
|
|
301
|
+
/** Manual snippet for RN >= 0.82 when the getDefaultReactHost call can't be found. */
|
|
302
|
+
function reactHostManual() {
|
|
303
|
+
return [
|
|
304
|
+
'Could not locate getDefaultReactHost(...) in MainApplication. Wire OTA bundle',
|
|
305
|
+
'resolution manually (RN >= 0.82):',
|
|
306
|
+
'',
|
|
307
|
+
` ${OTA_IMPORT}`,
|
|
308
|
+
'',
|
|
309
|
+
' override val reactHost: ReactHost by lazy {',
|
|
310
|
+
' getDefaultReactHost(',
|
|
311
|
+
' context = applicationContext,',
|
|
312
|
+
' packageList = PackageList(this).packages,',
|
|
313
|
+
' jsBundleFilePath = ScaleBunOtaModule.getJSBundleFile(applicationContext),',
|
|
314
|
+
' )',
|
|
315
|
+
' }',
|
|
316
|
+
].join('\n');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/** Manual snippet for RN < 0.82 when the ReactNativeHost object can't be found. */
|
|
320
|
+
function legacyManual() {
|
|
321
|
+
return [
|
|
322
|
+
'Could not locate the ReactNativeHost object in MainApplication. Add the OTA',
|
|
323
|
+
'bundle-resolution override manually (RN < 0.82), inside your ReactNativeHost:',
|
|
324
|
+
'',
|
|
325
|
+
` ${OTA_IMPORT}`,
|
|
326
|
+
'',
|
|
327
|
+
' override fun getJSBundleFile(): String? =',
|
|
328
|
+
' ScaleBunOtaModule.getJSBundleFile(this@MainApplication) ?: super.getJSBundleFile()',
|
|
329
|
+
].join('\n');
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/** Manual instructions for a Java MainApplication (auto-patch not supported). */
|
|
333
|
+
function javaInstructions(minor) {
|
|
334
|
+
if (isReactHostEra(minor)) {
|
|
335
|
+
return [
|
|
336
|
+
'A Java MainApplication was detected. Automatic patching of Java is not',
|
|
337
|
+
'supported — wire OTA bundle resolution manually (RN >= 0.82):',
|
|
338
|
+
'',
|
|
339
|
+
' import com.scalebun.rn.ota.ScaleBunOtaModule;',
|
|
340
|
+
'',
|
|
341
|
+
' // where you build the ReactHost via DefaultReactHost.getDefaultReactHost(...),',
|
|
342
|
+
' // pass the OTA bundle path:',
|
|
343
|
+
' ScaleBunOtaModule.getJSBundleFile(getApplicationContext())',
|
|
344
|
+
].join('\n');
|
|
345
|
+
}
|
|
346
|
+
return [
|
|
347
|
+
'A Java MainApplication was detected. Automatic patching of Java is not',
|
|
348
|
+
'supported — add this override inside your ReactNativeHost (RN < 0.82):',
|
|
349
|
+
'',
|
|
350
|
+
' import com.scalebun.rn.ota.ScaleBunOtaModule;',
|
|
351
|
+
'',
|
|
352
|
+
' @Override',
|
|
353
|
+
' protected String getJSBundleFile() {',
|
|
354
|
+
' String ota = ScaleBunOtaModule.getJSBundleFile(getApplicationContext());',
|
|
355
|
+
' return ota != null ? ota : super.getJSBundleFile();',
|
|
356
|
+
' }',
|
|
357
|
+
].join('\n');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
module.exports = {
|
|
361
|
+
OTA_IMPORT,
|
|
362
|
+
BUNDLE_CALL,
|
|
363
|
+
MANAGED_TAG,
|
|
364
|
+
findMainApplication,
|
|
365
|
+
isReactHostEra,
|
|
366
|
+
analyzeKotlin,
|
|
367
|
+
isFullyWiredKotlin,
|
|
368
|
+
patchKotlin,
|
|
369
|
+
javaInstructions,
|
|
370
|
+
};
|