rn-update-cli 1.0.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 +21 -0
- package/README.md +167 -0
- package/README.zh-CN.md +167 -0
- package/cli.json +525 -0
- package/lib/api.d.ts +56 -0
- package/lib/api.js +671 -0
- package/lib/app.d.ts +107 -0
- package/lib/app.js +329 -0
- package/lib/auto-update.d.ts +35 -0
- package/lib/auto-update.js +507 -0
- package/lib/bin.d.ts +2 -0
- package/lib/bin.js +133 -0
- package/lib/bundle-pack.d.ts +6 -0
- package/lib/bundle-pack.js +91 -0
- package/lib/bundle-runner.d.ts +168 -0
- package/lib/bundle-runner.js +1305 -0
- package/lib/bundle.d.ts +45 -0
- package/lib/bundle.js +226 -0
- package/lib/cache.d.ts +10 -0
- package/lib/cache.js +25 -0
- package/lib/commands.d.ts +11 -0
- package/lib/commands.js +72 -0
- package/lib/diff.d.ts +18 -0
- package/lib/diff.js +690 -0
- package/lib/exports.d.ts +11 -0
- package/lib/exports.js +20 -0
- package/lib/install.d.ts +9 -0
- package/lib/install.js +51 -0
- package/lib/locales/en.d.ts +187 -0
- package/lib/locales/en.js +194 -0
- package/lib/locales/zh.d.ts +187 -0
- package/lib/locales/zh.js +194 -0
- package/lib/native-package.d.ts +21 -0
- package/lib/native-package.js +298 -0
- package/lib/package.d.ts +68 -0
- package/lib/package.js +487 -0
- package/lib/provider.d.ts +24 -0
- package/lib/provider.js +244 -0
- package/lib/standalone-publish.d.ts +17 -0
- package/lib/standalone-publish.js +433 -0
- package/lib/symbolicate.d.ts +35 -0
- package/lib/symbolicate.js +130 -0
- package/lib/types.d.ts +152 -0
- package/lib/types.js +2 -0
- package/lib/user.d.ts +8 -0
- package/lib/user.js +50 -0
- package/lib/utils/add-gitignore.d.ts +6 -0
- package/lib/utils/add-gitignore.js +44 -0
- package/lib/utils/app-info-parser/aab.d.ts +27 -0
- package/lib/utils/app-info-parser/aab.js +256 -0
- package/lib/utils/app-info-parser/apk.d.ts +19 -0
- package/lib/utils/app-info-parser/apk.js +95 -0
- package/lib/utils/app-info-parser/app.d.ts +9 -0
- package/lib/utils/app-info-parser/app.js +15 -0
- package/lib/utils/app-info-parser/index.d.ts +21 -0
- package/lib/utils/app-info-parser/index.js +51 -0
- package/lib/utils/app-info-parser/ipa.d.ts +19 -0
- package/lib/utils/app-info-parser/ipa.js +101 -0
- package/lib/utils/app-info-parser/resource-finder.d.ts +49 -0
- package/lib/utils/app-info-parser/resource-finder.js +429 -0
- package/lib/utils/app-info-parser/utils.d.ts +36 -0
- package/lib/utils/app-info-parser/utils.js +140 -0
- package/lib/utils/app-info-parser/xml-parser/binary.d.ts +61 -0
- package/lib/utils/app-info-parser/xml-parser/binary.js +577 -0
- package/lib/utils/app-info-parser/xml-parser/manifest.d.ts +15 -0
- package/lib/utils/app-info-parser/xml-parser/manifest.js +207 -0
- package/lib/utils/app-info-parser/zip.d.ts +29 -0
- package/lib/utils/app-info-parser/zip.js +97 -0
- package/lib/utils/check-lockfile.d.ts +6 -0
- package/lib/utils/check-lockfile.js +93 -0
- package/lib/utils/check-plugin.d.ts +12 -0
- package/lib/utils/check-plugin.js +36 -0
- package/lib/utils/command-result.d.ts +8 -0
- package/lib/utils/command-result.js +27 -0
- package/lib/utils/constants.d.ts +10 -0
- package/lib/utils/constants.js +39 -0
- package/lib/utils/dep-versions.d.ts +20 -0
- package/lib/utils/dep-versions.js +129 -0
- package/lib/utils/git.d.ts +14 -0
- package/lib/utils/git.js +64 -0
- package/lib/utils/hbcTransform.d.ts +111 -0
- package/lib/utils/hbcTransform.js +561 -0
- package/lib/utils/hermes-base.d.ts +227 -0
- package/lib/utils/hermes-base.js +1287 -0
- package/lib/utils/hermes-literals.d.ts +64 -0
- package/lib/utils/hermes-literals.js +260 -0
- package/lib/utils/http-helper.d.ts +10 -0
- package/lib/utils/http-helper.js +150 -0
- package/lib/utils/i18n.d.ts +22 -0
- package/lib/utils/i18n.js +46 -0
- package/lib/utils/index.d.ts +67 -0
- package/lib/utils/index.js +545 -0
- package/lib/utils/latest-version/index.d.ts +154 -0
- package/lib/utils/latest-version/index.js +331 -0
- package/lib/utils/options.d.ts +10 -0
- package/lib/utils/options.js +68 -0
- package/lib/utils/plugin-config.d.ts +14 -0
- package/lib/utils/plugin-config.js +66 -0
- package/lib/utils/runtime.d.ts +54 -0
- package/lib/utils/runtime.js +263 -0
- package/lib/utils/slim-sourcemap.d.ts +28 -0
- package/lib/utils/slim-sourcemap.js +106 -0
- package/lib/utils/zip-entries.d.ts +10 -0
- package/lib/utils/zip-entries.js +211 -0
- package/lib/utils/zip-options.d.ts +17 -0
- package/lib/utils/zip-options.js +190 -0
- package/lib/utils/zip-range.d.ts +148 -0
- package/lib/utils/zip-range.js +581 -0
- package/lib/utils/zip-raw-writer.d.ts +52 -0
- package/lib/utils/zip-raw-writer.js +209 -0
- package/lib/version-operations.d.ts +21 -0
- package/lib/version-operations.js +320 -0
- package/lib/versions.d.ts +66 -0
- package/lib/versions.js +402 -0
- package/package.json +113 -0
- package/proto/Configuration.proto +183 -0
- package/proto/Resources.proto +569 -0
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.checkPlugins = exports.IpaBundleFileName = exports.ApkBundleFileName = void 0;
|
|
7
|
+
exports.loadTtyTable = loadTtyTable;
|
|
8
|
+
exports.isNonInteractive = isNonInteractive;
|
|
9
|
+
exports.question = question;
|
|
10
|
+
exports.translateOptions = translateOptions;
|
|
11
|
+
exports.getApkInfo = getApkInfo;
|
|
12
|
+
exports.getAppInfo = getAppInfo;
|
|
13
|
+
exports.getIpaInfo = getIpaInfo;
|
|
14
|
+
exports.getAabInfo = getAabInfo;
|
|
15
|
+
exports.printVersionCommand = printVersionCommand;
|
|
16
|
+
/**
|
|
17
|
+
* [INPUT]: 依赖 ZIP/APK/AAB/IPA/Harmony 解析器和摘要计算
|
|
18
|
+
* [OUTPUT]: 对外提供原生包版本、channel、bundleHash 与 opaque buildTime 读取器
|
|
19
|
+
* [POS]: CLI 构建身份解析层,永不把原生构建标识转换成数字
|
|
20
|
+
*/
|
|
21
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
22
|
+
const compare_versions_1 = require("compare-versions");
|
|
23
|
+
const crypto_1 = require("crypto");
|
|
24
|
+
const path_1 = __importDefault(require("path"));
|
|
25
|
+
const read_1 = require("read");
|
|
26
|
+
const yauzl_1 = require("yauzl");
|
|
27
|
+
const package_json_1 = __importDefault(require("../../package.json"));
|
|
28
|
+
const check_plugin_1 = require("./check-plugin");
|
|
29
|
+
Object.defineProperty(exports, "checkPlugins", { enumerable: true, get: function () { return check_plugin_1.checkPlugins; } });
|
|
30
|
+
const dep_versions_1 = require("./dep-versions");
|
|
31
|
+
const http_helper_1 = require("./http-helper");
|
|
32
|
+
const i18n_1 = require("./i18n");
|
|
33
|
+
// app-info-parser (and its zip/plist/protobuf stack) is only needed by the
|
|
34
|
+
// package commands; load it on first use instead of on every CLI start.
|
|
35
|
+
function createAppInfoParser(fn) {
|
|
36
|
+
const { default: AppInfoParser } = require('./app-info-parser');
|
|
37
|
+
return new AppInfoParser(fn);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* tty-table (~25 ms to load) only when a table is rendered. Bun's require of
|
|
41
|
+
* a CJS module can hand back a namespace object, so unwrap `default`.
|
|
42
|
+
*/
|
|
43
|
+
function loadTtyTable() {
|
|
44
|
+
const mod = require('tty-table');
|
|
45
|
+
return (mod.default ?? mod);
|
|
46
|
+
}
|
|
47
|
+
/** True when prompts cannot or must not be shown. */
|
|
48
|
+
function isNonInteractive(env = process.env, stdinIsTTY = process.stdin.isTTY, globalFlag = global.NO_INTERACTIVE) {
|
|
49
|
+
const envValue = env.NO_INTERACTIVE?.toLowerCase();
|
|
50
|
+
return (globalFlag === true ||
|
|
51
|
+
envValue === 'true' ||
|
|
52
|
+
envValue === '1' ||
|
|
53
|
+
stdinIsTTY !== true);
|
|
54
|
+
}
|
|
55
|
+
async function question(query, password) {
|
|
56
|
+
if (isNonInteractive()) {
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
return (0, read_1.read)({
|
|
60
|
+
prompt: query,
|
|
61
|
+
silent: password,
|
|
62
|
+
replace: password ? '*' : undefined,
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
function translateOptions(options, map) {
|
|
66
|
+
if (!map) {
|
|
67
|
+
// Existing logic for template replacement if no map is provided
|
|
68
|
+
const ret = {};
|
|
69
|
+
for (const key in options) {
|
|
70
|
+
const value = options[key];
|
|
71
|
+
if (typeof value === 'string') {
|
|
72
|
+
ret[key] = value.replace(/\$\{(\w+)\}/g, (placeholder, name) => {
|
|
73
|
+
const replacement = options[name] ?? process.env[name];
|
|
74
|
+
if (typeof replacement === 'string' ||
|
|
75
|
+
typeof replacement === 'number' ||
|
|
76
|
+
typeof replacement === 'boolean') {
|
|
77
|
+
return String(replacement);
|
|
78
|
+
}
|
|
79
|
+
return placeholder;
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
ret[key] = value;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return ret;
|
|
87
|
+
}
|
|
88
|
+
const result = { ...options };
|
|
89
|
+
for (const [key, value] of Object.entries(map)) {
|
|
90
|
+
if (result[key] !== undefined) {
|
|
91
|
+
result[value] = result[key];
|
|
92
|
+
delete result[key];
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return result;
|
|
96
|
+
}
|
|
97
|
+
// bundleHash: sha256 of the raw bundle bytes extracted from the package. Must
|
|
98
|
+
// stay byte-identical with what the client hashes at runtime (its embedded
|
|
99
|
+
// bundle, which is also the pdiff source) — the server compares the two to
|
|
100
|
+
// decide pdiff applicability. Raw bytes, no normalization of any kind.
|
|
101
|
+
// getEntries is typed Buffer | Blob (app-info-parser's browser/node duality);
|
|
102
|
+
// under Node it is always a Buffer, but handle both.
|
|
103
|
+
async function sha256(data) {
|
|
104
|
+
const buffer = Buffer.isBuffer(data)
|
|
105
|
+
? data
|
|
106
|
+
: Buffer.from(await data.arrayBuffer());
|
|
107
|
+
return (0, crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
108
|
+
}
|
|
109
|
+
// Anchored exact paths: unanchored/unescaped patterns also matched entries
|
|
110
|
+
// like index.android.bundle.map or .backup, and a later match would silently
|
|
111
|
+
// overwrite the real bundle, registering a wrong bundleHash.
|
|
112
|
+
exports.ApkBundleFileName = /^assets\/index\.android\.bundle$/;
|
|
113
|
+
const ApkUpdateJsonName = /^res\/raw\/update\.json$/;
|
|
114
|
+
async function getApkInfo(fn) {
|
|
115
|
+
const appInfoParser = createAppInfoParser(fn);
|
|
116
|
+
// read both entries in a single scan over the archive
|
|
117
|
+
const entries = await appInfoParser.parser.getEntries([
|
|
118
|
+
exports.ApkBundleFileName,
|
|
119
|
+
ApkUpdateJsonName,
|
|
120
|
+
]);
|
|
121
|
+
const bundleFile = entries[String(exports.ApkBundleFileName)];
|
|
122
|
+
if (!bundleFile) {
|
|
123
|
+
throw new Error((0, i18n_1.t)('bundleNotFound', {
|
|
124
|
+
packageType: 'apk',
|
|
125
|
+
entryFile: 'index.android.bundle',
|
|
126
|
+
}));
|
|
127
|
+
}
|
|
128
|
+
const updateJsonFile = entries[String(ApkUpdateJsonName)];
|
|
129
|
+
let appCredential = {};
|
|
130
|
+
if (updateJsonFile) {
|
|
131
|
+
appCredential = JSON.parse(updateJsonFile.toString()).android;
|
|
132
|
+
}
|
|
133
|
+
const { versionName, application } = await appInfoParser.parse();
|
|
134
|
+
let buildTime = '';
|
|
135
|
+
let channel = 'default';
|
|
136
|
+
if (Array.isArray(application.metaData)) {
|
|
137
|
+
for (const meta of application.metaData) {
|
|
138
|
+
if (meta.name === 'pakta_build_time') {
|
|
139
|
+
if (Array.isArray(meta.value)) {
|
|
140
|
+
const firstValue = meta.value[0];
|
|
141
|
+
buildTime = String(firstValue);
|
|
142
|
+
}
|
|
143
|
+
else if (meta.value !== undefined) {
|
|
144
|
+
buildTime = String(meta.value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
if (meta.name === 'channel' && meta.value !== undefined) {
|
|
148
|
+
const value = Array.isArray(meta.value) ? meta.value[0] : meta.value;
|
|
149
|
+
channel = String(value).trim() || 'default';
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
if (!buildTime || buildTime === '0') {
|
|
154
|
+
throw new Error((0, i18n_1.t)('buildTimeNotFound'));
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
versionName,
|
|
158
|
+
buildTime,
|
|
159
|
+
bundleHash: await sha256(bundleFile),
|
|
160
|
+
bundleFile,
|
|
161
|
+
...appCredential,
|
|
162
|
+
channel,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
async function getAppInfo(fn) {
|
|
166
|
+
const appInfoParser = createAppInfoParser(fn);
|
|
167
|
+
// single scan (and single nested .hap extraction) for all three entries
|
|
168
|
+
const [bundleFile, updateJsonFile, metaJsonFile] = await appInfoParser.parser.getEntriesFromHarmonyApp([
|
|
169
|
+
/^resources\/rawfile\/bundle\.harmony\.js$/,
|
|
170
|
+
/^resources\/rawfile\/update\.json$/,
|
|
171
|
+
/^resources\/rawfile\/meta\.json$/,
|
|
172
|
+
]);
|
|
173
|
+
if (!bundleFile) {
|
|
174
|
+
throw new Error((0, i18n_1.t)('bundleNotFound', {
|
|
175
|
+
packageType: 'app',
|
|
176
|
+
entryFile: 'bundle.harmony.js',
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
let appCredential = {};
|
|
180
|
+
if (updateJsonFile) {
|
|
181
|
+
appCredential = JSON.parse(updateJsonFile.toString()).harmony;
|
|
182
|
+
}
|
|
183
|
+
let metaData = {};
|
|
184
|
+
if (metaJsonFile) {
|
|
185
|
+
metaData = JSON.parse(metaJsonFile.toString());
|
|
186
|
+
}
|
|
187
|
+
const { versionName, pakta_build_time, channel: parsedChannel } = metaData;
|
|
188
|
+
const buildTime = pakta_build_time === undefined ? '' : String(pakta_build_time);
|
|
189
|
+
const channel = typeof parsedChannel === 'string' && parsedChannel.trim() ? parsedChannel.trim() : 'default';
|
|
190
|
+
if (!buildTime || buildTime === '0') {
|
|
191
|
+
throw new Error((0, i18n_1.t)('buildTimeNotFound'));
|
|
192
|
+
}
|
|
193
|
+
return {
|
|
194
|
+
versionName,
|
|
195
|
+
buildTime,
|
|
196
|
+
bundleHash: await sha256(bundleFile),
|
|
197
|
+
bundleFile,
|
|
198
|
+
...appCredential,
|
|
199
|
+
channel,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
// lowercase because the zip reader also matches against the lowercased entry
|
|
203
|
+
// name (real IPAs use "Payload/")
|
|
204
|
+
exports.IpaBundleFileName = /^payload\/[^/]+\.app\/main\.jsbundle$/;
|
|
205
|
+
const IpaUpdateJsonName = /^payload\/[^/]+\.app\/assets\/update\.json$/;
|
|
206
|
+
const IpaBuildTimeName = /^payload\/[^/]+\.app\/pakta_build_time\.txt$/;
|
|
207
|
+
// Not in root bundle when use `use_frameworks`
|
|
208
|
+
const IpaBuildTimeFrameworkName = /^payload\/[^/]+\.app\/frameworks\/react_native_update\.framework\/pakta_build_time\.txt$/;
|
|
209
|
+
async function getIpaInfo(fn) {
|
|
210
|
+
const appInfoParser = createAppInfoParser(fn);
|
|
211
|
+
// read all four entries in a single scan over the archive
|
|
212
|
+
const entries = await appInfoParser.parser.getEntries([
|
|
213
|
+
exports.IpaBundleFileName,
|
|
214
|
+
IpaUpdateJsonName,
|
|
215
|
+
IpaBuildTimeName,
|
|
216
|
+
IpaBuildTimeFrameworkName,
|
|
217
|
+
]);
|
|
218
|
+
const bundleFile = entries[String(exports.IpaBundleFileName)];
|
|
219
|
+
if (!bundleFile) {
|
|
220
|
+
throw new Error((0, i18n_1.t)('bundleNotFound', {
|
|
221
|
+
packageType: 'ipa',
|
|
222
|
+
entryFile: 'main.jsbundle',
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
const updateJsonFile = entries[String(IpaUpdateJsonName)];
|
|
226
|
+
let appCredential = {};
|
|
227
|
+
if (updateJsonFile) {
|
|
228
|
+
appCredential = JSON.parse(updateJsonFile.toString()).ios;
|
|
229
|
+
}
|
|
230
|
+
const { CFBundleShortVersionString: versionName, channel: parsedChannel } = await appInfoParser.parse();
|
|
231
|
+
const buildTimeTxtBuffer = entries[String(IpaBuildTimeName)] ??
|
|
232
|
+
entries[String(IpaBuildTimeFrameworkName)];
|
|
233
|
+
if (!buildTimeTxtBuffer) {
|
|
234
|
+
throw new Error((0, i18n_1.t)('buildTimeNotFound'));
|
|
235
|
+
}
|
|
236
|
+
const buildTime = buildTimeTxtBuffer.toString().trim();
|
|
237
|
+
if (!buildTime || buildTime === '0') {
|
|
238
|
+
throw new Error((0, i18n_1.t)('buildTimeNotFound'));
|
|
239
|
+
}
|
|
240
|
+
return {
|
|
241
|
+
versionName,
|
|
242
|
+
buildTime,
|
|
243
|
+
bundleHash: await sha256(bundleFile),
|
|
244
|
+
bundleFile,
|
|
245
|
+
...appCredential,
|
|
246
|
+
channel: typeof parsedChannel === 'string' && parsedChannel.trim() ? parsedChannel.trim() : 'default',
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
async function getAabInfo(fn) {
|
|
250
|
+
const protobuf = require('protobufjs');
|
|
251
|
+
const root = await protobuf.load(path_1.default.join(__dirname, '../../proto/Resources.proto'));
|
|
252
|
+
const XmlNode = root.lookupType('aapt.pb.XmlNode');
|
|
253
|
+
const buffer = await readZipEntry(fn, 'base/manifest/AndroidManifest.xml');
|
|
254
|
+
const message = XmlNode.decode(buffer);
|
|
255
|
+
const object = XmlNode.toObject(message, {
|
|
256
|
+
enums: String,
|
|
257
|
+
longs: String,
|
|
258
|
+
bytes: String,
|
|
259
|
+
defaults: true,
|
|
260
|
+
arrays: true,
|
|
261
|
+
});
|
|
262
|
+
const manifestElement = object.element;
|
|
263
|
+
if (manifestElement.name !== 'manifest') {
|
|
264
|
+
throw new Error((0, i18n_1.t)('invalidManifest'));
|
|
265
|
+
}
|
|
266
|
+
let versionName = '';
|
|
267
|
+
for (const attr of manifestElement.attribute) {
|
|
268
|
+
if (attr.name === 'versionName') {
|
|
269
|
+
versionName = attr.value;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
let buildTime = '';
|
|
273
|
+
let channel = 'default';
|
|
274
|
+
const appCredential = {};
|
|
275
|
+
// Find application node
|
|
276
|
+
const applicationNode = manifestElement.child.find((child) => child.element?.name === 'application');
|
|
277
|
+
const applicationElement = applicationNode?.element;
|
|
278
|
+
if (applicationElement) {
|
|
279
|
+
const metaDataNodes = applicationElement.child.filter((child) => child.element?.name === 'meta-data');
|
|
280
|
+
for (const meta of metaDataNodes) {
|
|
281
|
+
let name = '';
|
|
282
|
+
let value = '';
|
|
283
|
+
let resourceId = 0;
|
|
284
|
+
const metaElement = meta.element;
|
|
285
|
+
if (!metaElement) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
for (const attr of metaElement.attribute) {
|
|
289
|
+
if (attr.name === 'name') {
|
|
290
|
+
name = attr.value;
|
|
291
|
+
}
|
|
292
|
+
if (attr.name === 'value') {
|
|
293
|
+
value = attr.value;
|
|
294
|
+
if (attr.compiledItem?.ref?.id) {
|
|
295
|
+
resourceId = attr.compiledItem.ref.id;
|
|
296
|
+
}
|
|
297
|
+
else if (attr.compiledItem?.prim?.intDecimalValue) {
|
|
298
|
+
value = attr.compiledItem.prim.intDecimalValue.toString();
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (name === 'pakta_build_time') {
|
|
303
|
+
if (resourceId > 0) {
|
|
304
|
+
const resolvedValue = await resolveResource(fn, resourceId, root);
|
|
305
|
+
if (resolvedValue) {
|
|
306
|
+
value = resolvedValue;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
buildTime = String(value);
|
|
310
|
+
}
|
|
311
|
+
if (name === 'channel') {
|
|
312
|
+
channel = value.trim() || 'default';
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (!buildTime || buildTime === '0') {
|
|
317
|
+
throw new Error((0, i18n_1.t)('buildTimeNotFound'));
|
|
318
|
+
}
|
|
319
|
+
// The JS bundle lives in the base module; these are the same bytes the
|
|
320
|
+
// extracted APK carries (extractApk copies entries verbatim), so the hash
|
|
321
|
+
// matches what uploadAab ends up registering via getApkInfo. Lenient on
|
|
322
|
+
// absence — getAabInfo is display-only (parseAab); the upload path goes
|
|
323
|
+
// through extractApk → getApkInfo which enforces the bundle's presence.
|
|
324
|
+
let bundleHash;
|
|
325
|
+
try {
|
|
326
|
+
bundleHash = await sha256(await readZipEntry(fn, 'base/assets/index.android.bundle'));
|
|
327
|
+
}
|
|
328
|
+
catch {
|
|
329
|
+
// no embedded bundle (e.g. debug AAB); omit the field
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
versionName,
|
|
333
|
+
buildTime,
|
|
334
|
+
...(bundleHash ? { bundleHash } : {}),
|
|
335
|
+
...appCredential,
|
|
336
|
+
channel,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async function readZipEntry(fn, entryName) {
|
|
340
|
+
return new Promise((resolve, reject) => {
|
|
341
|
+
(0, yauzl_1.open)(fn, { lazyEntries: true }, (err, zipfile) => {
|
|
342
|
+
if (err || !zipfile) {
|
|
343
|
+
reject(err ?? new Error('Failed to open zip file'));
|
|
344
|
+
return;
|
|
345
|
+
}
|
|
346
|
+
let found = false;
|
|
347
|
+
zipfile.readEntry();
|
|
348
|
+
zipfile.on('entry', (entry) => {
|
|
349
|
+
if (entry.fileName === entryName) {
|
|
350
|
+
found = true;
|
|
351
|
+
zipfile.openReadStream(entry, (streamError, readStream) => {
|
|
352
|
+
if (streamError || !readStream) {
|
|
353
|
+
zipfile.close();
|
|
354
|
+
reject(streamError ?? new Error('Failed to read zip entry'));
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
const chunks = [];
|
|
358
|
+
readStream.on('data', (chunk) => chunks.push(chunk));
|
|
359
|
+
readStream.on('end', () => {
|
|
360
|
+
zipfile.close();
|
|
361
|
+
resolve(Buffer.concat(chunks));
|
|
362
|
+
});
|
|
363
|
+
readStream.on('error', (error) => {
|
|
364
|
+
zipfile.close();
|
|
365
|
+
reject(error);
|
|
366
|
+
});
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
zipfile.readEntry();
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
zipfile.on('end', () => {
|
|
374
|
+
if (!found)
|
|
375
|
+
reject(new Error(`${entryName} not found in AAB`));
|
|
376
|
+
});
|
|
377
|
+
zipfile.on('error', reject);
|
|
378
|
+
});
|
|
379
|
+
});
|
|
380
|
+
}
|
|
381
|
+
async function resolveResource(fn, resourceId, root) {
|
|
382
|
+
const pkgId = (resourceId >> 24) & 0xff;
|
|
383
|
+
const typeId = (resourceId >> 16) & 0xff;
|
|
384
|
+
const entryId = resourceId & 0xffff;
|
|
385
|
+
try {
|
|
386
|
+
const buffer = await readZipEntry(fn, 'base/resources.pb');
|
|
387
|
+
const ResourceTable = root.lookupType('aapt.pb.ResourceTable');
|
|
388
|
+
const message = ResourceTable.decode(buffer);
|
|
389
|
+
const object = ResourceTable.toObject(message, {
|
|
390
|
+
enums: String,
|
|
391
|
+
longs: String,
|
|
392
|
+
bytes: String,
|
|
393
|
+
defaults: true,
|
|
394
|
+
arrays: true,
|
|
395
|
+
});
|
|
396
|
+
// Find package
|
|
397
|
+
const pkg = object.package.find((pkgItem) => pkgItem.packageId === pkgId);
|
|
398
|
+
if (!pkg)
|
|
399
|
+
return null;
|
|
400
|
+
// Find type
|
|
401
|
+
const type = pkg.type.find((typeItem) => typeItem.typeId === typeId);
|
|
402
|
+
if (!type)
|
|
403
|
+
return null;
|
|
404
|
+
// Find entry
|
|
405
|
+
const entry = type.entry.find((entryItem) => entryItem.entryId === entryId);
|
|
406
|
+
if (!entry)
|
|
407
|
+
return null;
|
|
408
|
+
// Get value from configValue
|
|
409
|
+
if (entry.configValue && entry.configValue.length > 0) {
|
|
410
|
+
const val = entry.configValue[0]?.value;
|
|
411
|
+
const stringValue = val?.item?.str?.value;
|
|
412
|
+
if (typeof stringValue === 'string') {
|
|
413
|
+
return stringValue;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
catch (e) {
|
|
418
|
+
console.warn((0, i18n_1.t)('failedToResolveResource', { error: e }));
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
}
|
|
422
|
+
const VERSION_CHECK_PACKAGES = ['rn-update-cli', 'rn-update'];
|
|
423
|
+
/**
|
|
424
|
+
* Latest registry versions of the CLI and the client library, in that order
|
|
425
|
+
* (`undefined` where unknown). Never rejects.
|
|
426
|
+
*
|
|
427
|
+
* `background: true` answers from a 1-day cache when possible and otherwise
|
|
428
|
+
* refreshes it with an unref'd request, so the process is free to exit before
|
|
429
|
+
* the registry answers; `background: false` always asks the registry and
|
|
430
|
+
* waits (refreshing the cache on the way).
|
|
431
|
+
*/
|
|
432
|
+
async function getLatestVersions(background) {
|
|
433
|
+
// the registry client pulls in global-dirs/registry-auth-token/semver: only
|
|
434
|
+
// pay for them when actually checking
|
|
435
|
+
const { default: latestVersion } = require('./latest-version');
|
|
436
|
+
return latestVersion(VERSION_CHECK_PACKAGES, {
|
|
437
|
+
useCache: true,
|
|
438
|
+
...(background ? { unref: true } : { cacheMaxAge: 0 }),
|
|
439
|
+
requestOptions: {
|
|
440
|
+
timeout: 2000,
|
|
441
|
+
},
|
|
442
|
+
})
|
|
443
|
+
.then((pkgs) => pkgs.map((pkg) => pkg.latest))
|
|
444
|
+
.catch(() => VERSION_CHECK_PACKAGES.map(() => undefined));
|
|
445
|
+
}
|
|
446
|
+
function isNewer(latest, current) {
|
|
447
|
+
if (!latest || !current) {
|
|
448
|
+
return false;
|
|
449
|
+
}
|
|
450
|
+
try {
|
|
451
|
+
return (0, compare_versions_1.compare)(latest, current, '>');
|
|
452
|
+
}
|
|
453
|
+
catch {
|
|
454
|
+
return false;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
function latestTag(version) {
|
|
458
|
+
return version
|
|
459
|
+
? ` ${(0, i18n_1.t)('latestVersionTag', { version: chalk_1.default.green(version) })}`
|
|
460
|
+
: '';
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Print the installed CLI / rn-update versions (and refuse or warn
|
|
464
|
+
* about unsupported client versions) immediately, without touching the
|
|
465
|
+
* network.
|
|
466
|
+
*
|
|
467
|
+
* With `wait: true` (the `-v`/`version` command) the registry is queried
|
|
468
|
+
* first and the latest versions are printed inline. Otherwise the registry
|
|
469
|
+
* check runs in the background and the returned `printHints` shows what is
|
|
470
|
+
* newer once the command is done — the command itself is never delayed by it.
|
|
471
|
+
*/
|
|
472
|
+
async function printVersionCommand({ wait = true, } = {}) {
|
|
473
|
+
// one dependency, not the whole map: this runs before every command
|
|
474
|
+
const rnuVersion = (0, dep_versions_1.getDepVersion)('rn-update');
|
|
475
|
+
const standalone = (0, http_helper_1.isStandaloneService)();
|
|
476
|
+
let latest;
|
|
477
|
+
const check = (standalone ? Promise.resolve([]) : getLatestVersions(!wait)).then((versions) => {
|
|
478
|
+
latest = versions;
|
|
479
|
+
});
|
|
480
|
+
if (wait) {
|
|
481
|
+
await check;
|
|
482
|
+
}
|
|
483
|
+
const [latestCliVersion, latestRnuVersion] = latest ?? [];
|
|
484
|
+
console.log(`rn-update-cli: ${package_json_1.default.version}${latestTag(latestCliVersion)}`);
|
|
485
|
+
try {
|
|
486
|
+
const { consumeAutoUpdateNotice } = require('../auto-update');
|
|
487
|
+
const notice = standalone ? undefined : consumeAutoUpdateNotice(package_json_1.default.version);
|
|
488
|
+
if (notice?.kind === 'updated') {
|
|
489
|
+
console.log((0, i18n_1.t)('autoUpdateSuccess', {
|
|
490
|
+
from: notice.currentVersion,
|
|
491
|
+
to: notice.targetVersion,
|
|
492
|
+
}));
|
|
493
|
+
}
|
|
494
|
+
else if (notice?.kind === 'permission') {
|
|
495
|
+
console.warn((0, i18n_1.t)('autoUpdatePermission', {
|
|
496
|
+
command: notice.command,
|
|
497
|
+
}));
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
// A corrupt/unwritable updater cache cannot affect normal commands.
|
|
502
|
+
}
|
|
503
|
+
if (rnuVersion) {
|
|
504
|
+
console.log(`rn-update: ${rnuVersion}${latestTag(latestRnuVersion)}`);
|
|
505
|
+
}
|
|
506
|
+
else {
|
|
507
|
+
console.log((0, i18n_1.t)('rnuVersionNotFound'));
|
|
508
|
+
}
|
|
509
|
+
let hinted = wait; // inline tags already shown
|
|
510
|
+
const printHints = () => {
|
|
511
|
+
if (hinted || !latest) {
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
hinted = true;
|
|
515
|
+
const [cliLatest, rnuLatest] = latest;
|
|
516
|
+
if (isNewer(cliLatest, package_json_1.default.version)) {
|
|
517
|
+
console.log(`rn-update-cli: ${package_json_1.default.version}${latestTag(cliLatest)}`);
|
|
518
|
+
}
|
|
519
|
+
if (rnuVersion && isNewer(rnuLatest, rnuVersion)) {
|
|
520
|
+
console.log(`rn-update: ${rnuVersion}${latestTag(rnuLatest)}`);
|
|
521
|
+
}
|
|
522
|
+
};
|
|
523
|
+
const settle = (graceMs) => {
|
|
524
|
+
let timer;
|
|
525
|
+
const grace = new Promise((resolve) => {
|
|
526
|
+
timer = setTimeout(resolve, graceMs);
|
|
527
|
+
});
|
|
528
|
+
return Promise.race([check, grace]).then(() => clearTimeout(timer));
|
|
529
|
+
};
|
|
530
|
+
let autoUpdateStarted = false;
|
|
531
|
+
const startAutoUpdate = () => {
|
|
532
|
+
if (standalone || autoUpdateStarted || !isNewer(latest?.[0], package_json_1.default.version)) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
autoUpdateStarted = true;
|
|
536
|
+
try {
|
|
537
|
+
const { launchAutoUpdate } = require('../auto-update');
|
|
538
|
+
launchAutoUpdate(package_json_1.default.version, latest?.[0]);
|
|
539
|
+
}
|
|
540
|
+
catch {
|
|
541
|
+
// Self-update is strictly opportunistic and never affects the command.
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
return { done: check, settle, startAutoUpdate, printHints };
|
|
545
|
+
}
|