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.
Files changed (117) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -0
  3. package/README.zh-CN.md +167 -0
  4. package/cli.json +525 -0
  5. package/lib/api.d.ts +56 -0
  6. package/lib/api.js +671 -0
  7. package/lib/app.d.ts +107 -0
  8. package/lib/app.js +329 -0
  9. package/lib/auto-update.d.ts +35 -0
  10. package/lib/auto-update.js +507 -0
  11. package/lib/bin.d.ts +2 -0
  12. package/lib/bin.js +133 -0
  13. package/lib/bundle-pack.d.ts +6 -0
  14. package/lib/bundle-pack.js +91 -0
  15. package/lib/bundle-runner.d.ts +168 -0
  16. package/lib/bundle-runner.js +1305 -0
  17. package/lib/bundle.d.ts +45 -0
  18. package/lib/bundle.js +226 -0
  19. package/lib/cache.d.ts +10 -0
  20. package/lib/cache.js +25 -0
  21. package/lib/commands.d.ts +11 -0
  22. package/lib/commands.js +72 -0
  23. package/lib/diff.d.ts +18 -0
  24. package/lib/diff.js +690 -0
  25. package/lib/exports.d.ts +11 -0
  26. package/lib/exports.js +20 -0
  27. package/lib/install.d.ts +9 -0
  28. package/lib/install.js +51 -0
  29. package/lib/locales/en.d.ts +187 -0
  30. package/lib/locales/en.js +194 -0
  31. package/lib/locales/zh.d.ts +187 -0
  32. package/lib/locales/zh.js +194 -0
  33. package/lib/native-package.d.ts +21 -0
  34. package/lib/native-package.js +298 -0
  35. package/lib/package.d.ts +68 -0
  36. package/lib/package.js +487 -0
  37. package/lib/provider.d.ts +24 -0
  38. package/lib/provider.js +244 -0
  39. package/lib/standalone-publish.d.ts +17 -0
  40. package/lib/standalone-publish.js +433 -0
  41. package/lib/symbolicate.d.ts +35 -0
  42. package/lib/symbolicate.js +130 -0
  43. package/lib/types.d.ts +152 -0
  44. package/lib/types.js +2 -0
  45. package/lib/user.d.ts +8 -0
  46. package/lib/user.js +50 -0
  47. package/lib/utils/add-gitignore.d.ts +6 -0
  48. package/lib/utils/add-gitignore.js +44 -0
  49. package/lib/utils/app-info-parser/aab.d.ts +27 -0
  50. package/lib/utils/app-info-parser/aab.js +256 -0
  51. package/lib/utils/app-info-parser/apk.d.ts +19 -0
  52. package/lib/utils/app-info-parser/apk.js +95 -0
  53. package/lib/utils/app-info-parser/app.d.ts +9 -0
  54. package/lib/utils/app-info-parser/app.js +15 -0
  55. package/lib/utils/app-info-parser/index.d.ts +21 -0
  56. package/lib/utils/app-info-parser/index.js +51 -0
  57. package/lib/utils/app-info-parser/ipa.d.ts +19 -0
  58. package/lib/utils/app-info-parser/ipa.js +101 -0
  59. package/lib/utils/app-info-parser/resource-finder.d.ts +49 -0
  60. package/lib/utils/app-info-parser/resource-finder.js +429 -0
  61. package/lib/utils/app-info-parser/utils.d.ts +36 -0
  62. package/lib/utils/app-info-parser/utils.js +140 -0
  63. package/lib/utils/app-info-parser/xml-parser/binary.d.ts +61 -0
  64. package/lib/utils/app-info-parser/xml-parser/binary.js +577 -0
  65. package/lib/utils/app-info-parser/xml-parser/manifest.d.ts +15 -0
  66. package/lib/utils/app-info-parser/xml-parser/manifest.js +207 -0
  67. package/lib/utils/app-info-parser/zip.d.ts +29 -0
  68. package/lib/utils/app-info-parser/zip.js +97 -0
  69. package/lib/utils/check-lockfile.d.ts +6 -0
  70. package/lib/utils/check-lockfile.js +93 -0
  71. package/lib/utils/check-plugin.d.ts +12 -0
  72. package/lib/utils/check-plugin.js +36 -0
  73. package/lib/utils/command-result.d.ts +8 -0
  74. package/lib/utils/command-result.js +27 -0
  75. package/lib/utils/constants.d.ts +10 -0
  76. package/lib/utils/constants.js +39 -0
  77. package/lib/utils/dep-versions.d.ts +20 -0
  78. package/lib/utils/dep-versions.js +129 -0
  79. package/lib/utils/git.d.ts +14 -0
  80. package/lib/utils/git.js +64 -0
  81. package/lib/utils/hbcTransform.d.ts +111 -0
  82. package/lib/utils/hbcTransform.js +561 -0
  83. package/lib/utils/hermes-base.d.ts +227 -0
  84. package/lib/utils/hermes-base.js +1287 -0
  85. package/lib/utils/hermes-literals.d.ts +64 -0
  86. package/lib/utils/hermes-literals.js +260 -0
  87. package/lib/utils/http-helper.d.ts +10 -0
  88. package/lib/utils/http-helper.js +150 -0
  89. package/lib/utils/i18n.d.ts +22 -0
  90. package/lib/utils/i18n.js +46 -0
  91. package/lib/utils/index.d.ts +67 -0
  92. package/lib/utils/index.js +545 -0
  93. package/lib/utils/latest-version/index.d.ts +154 -0
  94. package/lib/utils/latest-version/index.js +331 -0
  95. package/lib/utils/options.d.ts +10 -0
  96. package/lib/utils/options.js +68 -0
  97. package/lib/utils/plugin-config.d.ts +14 -0
  98. package/lib/utils/plugin-config.js +66 -0
  99. package/lib/utils/runtime.d.ts +54 -0
  100. package/lib/utils/runtime.js +263 -0
  101. package/lib/utils/slim-sourcemap.d.ts +28 -0
  102. package/lib/utils/slim-sourcemap.js +106 -0
  103. package/lib/utils/zip-entries.d.ts +10 -0
  104. package/lib/utils/zip-entries.js +211 -0
  105. package/lib/utils/zip-options.d.ts +17 -0
  106. package/lib/utils/zip-options.js +190 -0
  107. package/lib/utils/zip-range.d.ts +148 -0
  108. package/lib/utils/zip-range.js +581 -0
  109. package/lib/utils/zip-raw-writer.d.ts +52 -0
  110. package/lib/utils/zip-raw-writer.js +209 -0
  111. package/lib/version-operations.d.ts +21 -0
  112. package/lib/version-operations.js +320 -0
  113. package/lib/versions.d.ts +66 -0
  114. package/lib/versions.js +402 -0
  115. package/package.json +113 -0
  116. package/proto/Configuration.proto +183 -0
  117. 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
+ }