react-native-update-cli 2.9.4 → 2.9.5

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/src/diff.ts CHANGED
@@ -6,9 +6,54 @@ import type { CommandContext } from './types';
6
6
  import { translateOptions } from './utils';
7
7
  import { isPPKBundleFileName, scriptName, tempDir } from './utils/constants';
8
8
  import { t } from './utils/i18n';
9
- import { enumZipEntries, readEntry } from './utils/zip-entries';
9
+ import {
10
+ enumZipEntries,
11
+ readEntry,
12
+ readEntryPrefix,
13
+ } from './utils/zip-entries';
14
+ import {
15
+ ZIP_ENTRY_SNIFF_BYTES,
16
+ zipOptionsForManifestEntry,
17
+ zipOptionsForPatchEntry,
18
+ zipOptionsForPayloadEntry,
19
+ } from './utils/zip-options';
10
20
 
11
21
  type Diff = (oldSource?: Buffer, newSource?: Buffer) => Buffer;
22
+ type HpatchCover = {
23
+ oldPos: number | string | bigint;
24
+ newPos: number | string | bigint;
25
+ len: number | string | bigint;
26
+ };
27
+ type HpatchCompatiblePlan = {
28
+ covers?: HpatchCover[];
29
+ };
30
+ type HdiffWithCoversOptions = {
31
+ mode?: 'replace' | 'merge' | 'native-coalesce';
32
+ };
33
+ type HdiffModule = {
34
+ diff?: Diff;
35
+ diffWithCovers?: (
36
+ oldSource: Buffer,
37
+ newSource: Buffer,
38
+ covers: HpatchCover[],
39
+ options?: HdiffWithCoversOptions,
40
+ ) => { diff?: Buffer };
41
+ };
42
+ type BsdiffModule = {
43
+ diff?: Diff;
44
+ };
45
+ type ChiffModule = {
46
+ hpatchCompatiblePlanResult?: (
47
+ oldSource: Buffer,
48
+ newSource: Buffer,
49
+ ) => HpatchCompatiblePlan;
50
+ hpatchApproximatePlanResult?: (
51
+ oldSource: Buffer,
52
+ newSource: Buffer,
53
+ ) => HpatchCompatiblePlan;
54
+ };
55
+ type ChiffHpatchPolicy = 'off' | 'costed';
56
+ type ChiffHpatchExactPolicy = 'off' | 'on';
12
57
  type EntryMap = Record<string, { crc32: number; fileName: string }>;
13
58
  type CrcMap = Record<number, string>;
14
59
  type CopyMap = Record<string, string>;
@@ -28,22 +73,134 @@ type DiffCommandConfig = {
28
73
 
29
74
  export { enumZipEntries, readEntry };
30
75
 
31
- const loadDiffModule = (pkgName: string): Diff | undefined => {
76
+ const loadModule = <T>(pkgName: string): T | undefined => {
32
77
  const resolvePaths = ['.', npm.packages, yarn.packages];
33
78
 
34
79
  try {
35
80
  const resolved = require.resolve(pkgName, { paths: resolvePaths });
36
- const mod = require(resolved);
37
- if (mod?.diff) {
38
- return mod.diff as Diff;
39
- }
81
+ return require(resolved) as T;
40
82
  } catch {}
41
83
 
42
84
  return undefined;
43
85
  };
44
86
 
45
- const hdiff = loadDiffModule('node-hdiffpatch');
46
- const bsdiff = loadDiffModule('node-bsdiff');
87
+ const hdiff = loadModule<HdiffModule>('node-hdiffpatch');
88
+ const bsdiff = loadModule<BsdiffModule>('node-bsdiff');
89
+ const chiff = loadModule<ChiffModule>('@chiff/node');
90
+
91
+ // Structured covers are experimental and can be expensive on real Hermes input.
92
+ // Keep native hdiff as the default unless the server explicitly opts in.
93
+ function resolveChiffHpatchPolicy(policy?: unknown): ChiffHpatchPolicy {
94
+ const value = String(
95
+ policy ?? process.env.RNU_CHIFF_HPATCH_POLICY ?? 'off',
96
+ ).toLowerCase();
97
+ if (
98
+ value === 'costed' ||
99
+ value === 'on' ||
100
+ value === 'true' ||
101
+ value === '1'
102
+ ) {
103
+ return 'costed';
104
+ }
105
+ return 'off';
106
+ }
107
+
108
+ function resolveChiffHpatchMinNativeBytes(value?: unknown): number {
109
+ const raw = value ?? process.env.RNU_CHIFF_HPATCH_MIN_NATIVE_BYTES ?? 4096;
110
+ const parsed = Number(raw);
111
+ if (!Number.isFinite(parsed) || parsed < 0) {
112
+ return 4096;
113
+ }
114
+ return Math.floor(parsed);
115
+ }
116
+
117
+ function resolveChiffHpatchExactPolicy(policy?: unknown): ChiffHpatchExactPolicy {
118
+ const value = String(
119
+ policy ?? process.env.RNU_CHIFF_HPATCH_EXACT_COVERS ?? 'off',
120
+ ).toLowerCase();
121
+ if (value === 'on' || value === 'true' || value === '1') {
122
+ return 'on';
123
+ }
124
+ return 'off';
125
+ }
126
+
127
+ function createChiffAwareHdiff(
128
+ hdiffModule: HdiffModule,
129
+ chiffModule: ChiffModule | undefined,
130
+ policy: ChiffHpatchPolicy,
131
+ minNativeBytes: number,
132
+ exactPolicy: ChiffHpatchExactPolicy,
133
+ ): Diff {
134
+ const baseDiff = hdiffModule.diff;
135
+ if (!baseDiff) {
136
+ throw new Error(t('nodeHdiffpatchRequired', { scriptName }));
137
+ }
138
+
139
+ if (policy === 'off') {
140
+ return baseDiff;
141
+ }
142
+
143
+ return (oldSource?: Buffer, newSource?: Buffer) => {
144
+ const nativeDiff = baseDiff(oldSource, newSource);
145
+ if (!oldSource || !newSource || !hdiffModule.diffWithCovers) {
146
+ return nativeDiff;
147
+ }
148
+
149
+ let bestDiff = nativeDiff;
150
+ const tryDiffWithCovers = (
151
+ covers: HpatchCover[],
152
+ mode: 'replace' | 'merge' | 'native-coalesce',
153
+ ) => {
154
+ try {
155
+ const result = hdiffModule.diffWithCovers?.(
156
+ oldSource,
157
+ newSource,
158
+ covers,
159
+ { mode },
160
+ );
161
+ if (
162
+ Buffer.isBuffer(result?.diff) &&
163
+ result.diff.length < bestDiff.length
164
+ ) {
165
+ bestDiff = result.diff;
166
+ }
167
+ } catch {}
168
+ };
169
+
170
+ tryDiffWithCovers([], 'native-coalesce');
171
+
172
+ if (nativeDiff.length < minNativeBytes) {
173
+ return bestDiff;
174
+ }
175
+
176
+ try {
177
+ const approximatePlan = chiffModule?.hpatchApproximatePlanResult?.(
178
+ oldSource,
179
+ newSource,
180
+ );
181
+ if (Array.isArray(approximatePlan?.covers)) {
182
+ tryDiffWithCovers(approximatePlan.covers, 'merge');
183
+ }
184
+ } catch {}
185
+
186
+ if (
187
+ exactPolicy === 'off' ||
188
+ !chiffModule?.hpatchCompatiblePlanResult
189
+ ) {
190
+ return bestDiff;
191
+ }
192
+
193
+ try {
194
+ const plan = chiffModule.hpatchCompatiblePlanResult(oldSource, newSource);
195
+ if (Array.isArray(plan.covers)) {
196
+ tryDiffWithCovers(plan.covers, 'replace');
197
+ tryDiffWithCovers(plan.covers, 'merge');
198
+ }
199
+ } catch {}
200
+
201
+ return bestDiff;
202
+ };
203
+ }
47
204
 
48
205
  function basename(fn: string): string | undefined {
49
206
  const m = /^(.+\/)[^\/]+\/?$/.exec(fn);
@@ -123,6 +280,7 @@ async function diffFromPPK(
123
280
  zipfile.addBuffer(
124
281
  diffFn(originSource, newSource),
125
282
  `${entry.fileName}.patch`,
283
+ zipOptionsForPatchEntry(),
126
284
  );
127
285
  //console.log('End diff');
128
286
  } else {
@@ -150,6 +308,11 @@ async function diffFromPPK(
150
308
  addEntry(basePath);
151
309
  }
152
310
 
311
+ const entryPrefix = await readEntryPrefix(
312
+ entry,
313
+ nextZipfile,
314
+ ZIP_ENTRY_SNIFF_BYTES,
315
+ );
153
316
  await new Promise<void>((resolve, reject) => {
154
317
  nextZipfile.openReadStream(entry, (err, readStream) => {
155
318
  if (err) {
@@ -160,7 +323,11 @@ async function diffFromPPK(
160
323
  new Error(`Unable to read zip entry: ${entry.fileName}`),
161
324
  );
162
325
  }
163
- zipfile.addReadStream(readStream, entry.fileName);
326
+ zipfile.addReadStream(
327
+ readStream,
328
+ entry.fileName,
329
+ zipOptionsForPayloadEntry(entry.fileName, entryPrefix),
330
+ );
164
331
  readStream.on('end', () => {
165
332
  //console.log('add finished');
166
333
  resolve(void 0);
@@ -183,6 +350,7 @@ async function diffFromPPK(
183
350
  zipfile.addBuffer(
184
351
  Buffer.from(JSON.stringify({ copies, deletes })),
185
352
  '__diff.json',
353
+ zipOptionsForManifestEntry(),
186
354
  );
187
355
  zipfile.end();
188
356
  await writePromise;
@@ -248,6 +416,7 @@ async function diffFromPackage(
248
416
  zipfile.addBuffer(
249
417
  diffFn(originSource, newSource),
250
418
  `${entry.fileName}.patch`,
419
+ zipOptionsForPatchEntry(),
251
420
  );
252
421
  //console.log('End diff');
253
422
  } else {
@@ -263,6 +432,11 @@ async function diffFromPackage(
263
432
  return;
264
433
  }
265
434
 
435
+ const entryPrefix = await readEntryPrefix(
436
+ entry,
437
+ nextZipfile,
438
+ ZIP_ENTRY_SNIFF_BYTES,
439
+ );
266
440
  await new Promise<void>((resolve, reject) => {
267
441
  nextZipfile.openReadStream(entry, (err, readStream) => {
268
442
  if (err) {
@@ -273,7 +447,11 @@ async function diffFromPackage(
273
447
  new Error(`Unable to read zip entry: ${entry.fileName}`),
274
448
  );
275
449
  }
276
- zipfile.addReadStream(readStream, entry.fileName);
450
+ zipfile.addReadStream(
451
+ readStream,
452
+ entry.fileName,
453
+ zipOptionsForPayloadEntry(entry.fileName, entryPrefix),
454
+ );
277
455
  readStream.on('end', () => {
278
456
  //console.log('add finished');
279
457
  resolve(void 0);
@@ -283,13 +461,22 @@ async function diffFromPackage(
283
461
  }
284
462
  });
285
463
 
286
- zipfile.addBuffer(Buffer.from(JSON.stringify({ copies })), '__diff.json');
464
+ zipfile.addBuffer(
465
+ Buffer.from(JSON.stringify({ copies })),
466
+ '__diff.json',
467
+ zipOptionsForManifestEntry(),
468
+ );
287
469
  zipfile.end();
288
470
  await writePromise;
289
471
  }
290
472
 
291
473
  type DiffCommandOptions = {
292
474
  customDiff?: Diff;
475
+ customHdiffModule?: HdiffModule;
476
+ customChiffModule?: ChiffModule;
477
+ chiffHpatchPolicy?: ChiffHpatchPolicy;
478
+ chiffHpatchMinNativeBytes?: number | string;
479
+ chiffHpatchExactCovers?: ChiffHpatchExactPolicy | boolean | string | number;
293
480
  [key: string]: any;
294
481
  };
295
482
 
@@ -302,16 +489,23 @@ function resolveDiffImplementation(
302
489
  }
303
490
 
304
491
  if (useHdiff) {
305
- if (!hdiff) {
492
+ const hdiffModule = options.customHdiffModule ?? hdiff;
493
+ if (!hdiffModule?.diff) {
306
494
  throw new Error(t('nodeHdiffpatchRequired', { scriptName }));
307
495
  }
308
- return hdiff;
496
+ return createChiffAwareHdiff(
497
+ hdiffModule,
498
+ options.customChiffModule ?? chiff,
499
+ resolveChiffHpatchPolicy(options.chiffHpatchPolicy),
500
+ resolveChiffHpatchMinNativeBytes(options.chiffHpatchMinNativeBytes),
501
+ resolveChiffHpatchExactPolicy(options.chiffHpatchExactCovers),
502
+ );
309
503
  }
310
504
 
311
- if (!bsdiff) {
505
+ if (!bsdiff?.diff) {
312
506
  throw new Error(t('nodeBsdiffRequired', { scriptName }));
313
507
  }
314
- return bsdiff;
508
+ return bsdiff.diff;
315
509
  }
316
510
 
317
511
  function diffArgsCheck(
@@ -40,10 +40,6 @@ export class ModuleManager {
40
40
  if (module.init) {
41
41
  module.init(this.provider);
42
42
  }
43
-
44
- // console.log(
45
- // `Module '${module.name}' (v${module.version}) registered successfully`,
46
- // );
47
43
  }
48
44
 
49
45
  unregisterModule(moduleName: string): void {
package/src/package.ts CHANGED
@@ -99,7 +99,7 @@ async function uploadNativePackage(
99
99
  const { appId: appIdInPkg, appKey: appKeyInPkg } = info;
100
100
  const { appId, appKey } = await getSelectedApp(config.platform);
101
101
 
102
- if (appIdInPkg && appIdInPkg != appId) {
102
+ if (appIdInPkg && String(appIdInPkg) !== appId) {
103
103
  throw new Error(t(config.appIdMismatchKey, { appIdInPkg, appId }));
104
104
  }
105
105
 
@@ -175,10 +175,11 @@ export async function listPackage(appId: string) {
175
175
 
176
176
  export async function choosePackage(appId: string) {
177
177
  const list = await listPackage(appId);
178
+ const packageMap = new Map(list?.map((v) => [v.id.toString(), v]));
178
179
 
179
180
  while (true) {
180
181
  const id = await question(t('enterNativePackageId'));
181
- const app = list?.find((v) => v.id.toString() === id);
182
+ const app = packageMap.get(id);
182
183
  if (app) {
183
184
  return app;
184
185
  }
@@ -329,7 +330,7 @@ export const packageCommands = {
329
330
  packages: async ({ options }: { options: { platform: Platform } }) => {
330
331
  const platform = await getPlatform(options.platform);
331
332
  const { appId } = await getSelectedApp(platform);
332
- await listPackage(appId);
333
+ await listPackage(String(appId));
333
334
  },
334
335
  deletePackage: async ({
335
336
  args,
@@ -347,7 +348,7 @@ export const packageCommands = {
347
348
 
348
349
  if (!appId) {
349
350
  const platform = await getPlatform(options.platform);
350
- appId = (await getSelectedApp(platform)).appId as string;
351
+ appId = (await getSelectedApp(platform)).appId;
351
352
  }
352
353
 
353
354
  // If no packageId provided as argument, let user choose from list
@@ -28,6 +28,75 @@ export function readEntry(
28
28
  });
29
29
  }
30
30
 
31
+ export function readEntryPrefix(
32
+ entry: Entry,
33
+ zipFile: YauzlZipFile,
34
+ maxBytes: number,
35
+ ): Promise<Buffer> {
36
+ if (maxBytes <= 0) {
37
+ return Promise.resolve(Buffer.alloc(0));
38
+ }
39
+
40
+ const buffers: Buffer[] = [];
41
+ let length = 0;
42
+
43
+ return new Promise((resolve, reject) => {
44
+ zipFile.openReadStream(entry, (err, stream) => {
45
+ if (err) {
46
+ return reject(err);
47
+ }
48
+ if (!stream) {
49
+ return reject(new Error(`Unable to read zip entry: ${entry.fileName}`));
50
+ }
51
+
52
+ let settled = false;
53
+ const cleanup = () => {
54
+ stream.off('data', onData);
55
+ stream.off('end', onEnd);
56
+ stream.off('error', onError);
57
+ };
58
+ const finish = () => {
59
+ if (settled) {
60
+ return;
61
+ }
62
+ settled = true;
63
+ cleanup();
64
+ resolve(Buffer.concat(buffers, length));
65
+ };
66
+ const onData = (chunk: Buffer) => {
67
+ const remaining = maxBytes - length;
68
+ if (remaining <= 0) {
69
+ finish();
70
+ stream.destroy();
71
+ return;
72
+ }
73
+
74
+ const slice =
75
+ chunk.length > remaining ? chunk.subarray(0, remaining) : chunk;
76
+ buffers.push(slice);
77
+ length += slice.length;
78
+ if (length >= maxBytes) {
79
+ finish();
80
+ stream.destroy();
81
+ }
82
+ };
83
+ const onEnd = () => finish();
84
+ const onError = (error: Error) => {
85
+ if (settled) {
86
+ return;
87
+ }
88
+ settled = true;
89
+ cleanup();
90
+ reject(error);
91
+ };
92
+
93
+ stream.on('data', onData);
94
+ stream.once('end', onEnd);
95
+ stream.once('error', onError);
96
+ });
97
+ });
98
+ }
99
+
31
100
  export async function enumZipEntries(
32
101
  zipFn: string,
33
102
  callback: (
@@ -0,0 +1,173 @@
1
+ import path from 'path';
2
+ import * as fs from 'fs';
3
+
4
+ export type ZipEntryOptions = {
5
+ compress?: boolean;
6
+ compressionLevel?: number;
7
+ };
8
+
9
+ export const ZIP_ENTRY_SNIFF_BYTES = 64;
10
+
11
+ const alreadyCompressedExtensions = new Set([
12
+ '.7z',
13
+ '.aab',
14
+ '.apk',
15
+ '.br',
16
+ '.bz2',
17
+ '.gif',
18
+ '.gz',
19
+ '.heic',
20
+ '.jpeg',
21
+ '.jpg',
22
+ '.lzma',
23
+ '.mp3',
24
+ '.mp4',
25
+ '.ogg',
26
+ '.png',
27
+ '.webm',
28
+ '.webp',
29
+ '.woff',
30
+ '.woff2',
31
+ '.xz',
32
+ '.zip',
33
+ '.zst',
34
+ ]);
35
+
36
+ const HERMES_MAGIC = Buffer.from([
37
+ 0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f,
38
+ ]);
39
+ const HERMES_DELTA_MAGIC = Buffer.from([
40
+ 0x39, 0xe0, 0x43, 0xfc, 0x3e, 0xfc, 0xe6, 0xe0,
41
+ ]);
42
+
43
+ function startsWith(bytes: Buffer, signature: Buffer): boolean {
44
+ return (
45
+ bytes.length >= signature.length &&
46
+ bytes.subarray(0, signature.length).equals(signature)
47
+ );
48
+ }
49
+
50
+ function hasHermesBytecodeMagic(bytes: Buffer): boolean {
51
+ return (
52
+ startsWith(bytes, HERMES_MAGIC) || startsWith(bytes, HERMES_DELTA_MAGIC)
53
+ );
54
+ }
55
+
56
+ function hasAlreadyCompressedMagic(bytes: Buffer): boolean {
57
+ if (bytes.length < 2) {
58
+ return false;
59
+ }
60
+
61
+ if (startsWith(bytes, Buffer.from([0x89, 0x50, 0x4e, 0x47]))) {
62
+ return true;
63
+ }
64
+ if (startsWith(bytes, Buffer.from([0xff, 0xd8, 0xff]))) {
65
+ return true;
66
+ }
67
+ if (
68
+ startsWith(bytes, Buffer.from('GIF87a', 'ascii')) ||
69
+ startsWith(bytes, Buffer.from('GIF89a', 'ascii'))
70
+ ) {
71
+ return true;
72
+ }
73
+ if (
74
+ bytes.length >= 12 &&
75
+ bytes.subarray(0, 4).equals(Buffer.from('RIFF', 'ascii')) &&
76
+ bytes.subarray(8, 12).equals(Buffer.from('WEBP', 'ascii'))
77
+ ) {
78
+ return true;
79
+ }
80
+ if (
81
+ startsWith(bytes, Buffer.from([0x50, 0x4b, 0x03, 0x04])) ||
82
+ startsWith(bytes, Buffer.from([0x50, 0x4b, 0x05, 0x06])) ||
83
+ startsWith(bytes, Buffer.from([0x50, 0x4b, 0x07, 0x08]))
84
+ ) {
85
+ return true;
86
+ }
87
+ if (startsWith(bytes, Buffer.from([0x1f, 0x8b]))) {
88
+ return true;
89
+ }
90
+ if (startsWith(bytes, Buffer.from('BZh', 'ascii'))) {
91
+ return true;
92
+ }
93
+ if (startsWith(bytes, Buffer.from([0xfd, 0x37, 0x7a, 0x58, 0x5a, 0x00]))) {
94
+ return true;
95
+ }
96
+ if (startsWith(bytes, Buffer.from([0x37, 0x7a, 0xbc, 0xaf, 0x27, 0x1c]))) {
97
+ return true;
98
+ }
99
+ if (startsWith(bytes, Buffer.from([0x28, 0xb5, 0x2f, 0xfd]))) {
100
+ return true;
101
+ }
102
+ if (bytes.length >= 12 && bytes.subarray(4, 8).equals(Buffer.from('ftyp'))) {
103
+ return true;
104
+ }
105
+ if (startsWith(bytes, Buffer.from('OggS', 'ascii'))) {
106
+ return true;
107
+ }
108
+ if (startsWith(bytes, Buffer.from('ID3', 'ascii'))) {
109
+ return true;
110
+ }
111
+ if (
112
+ bytes[0] === 0xff &&
113
+ bytes.length >= 2 &&
114
+ (bytes[1] & 0xe0) === 0xe0
115
+ ) {
116
+ return true;
117
+ }
118
+ if (
119
+ startsWith(bytes, Buffer.from('wOFF', 'ascii')) ||
120
+ startsWith(bytes, Buffer.from('wOF2', 'ascii'))
121
+ ) {
122
+ return true;
123
+ }
124
+
125
+ return false;
126
+ }
127
+
128
+ function readFilePrefix(filePath: string): Buffer {
129
+ const buffer = Buffer.alloc(ZIP_ENTRY_SNIFF_BYTES);
130
+ const fd = fs.openSync(filePath, 'r');
131
+ try {
132
+ const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0);
133
+ return buffer.subarray(0, bytesRead);
134
+ } finally {
135
+ fs.closeSync(fd);
136
+ }
137
+ }
138
+
139
+ export function zipOptionsForPatchEntry(): ZipEntryOptions {
140
+ return { compress: false };
141
+ }
142
+
143
+ export function zipOptionsForManifestEntry(): ZipEntryOptions {
144
+ return { compress: false };
145
+ }
146
+
147
+ export function zipOptionsForPayloadEntry(
148
+ fileName: string,
149
+ prefix?: Buffer,
150
+ ): ZipEntryOptions {
151
+ if (prefix && prefix.length > 0) {
152
+ if (hasHermesBytecodeMagic(prefix)) {
153
+ // Hermes bytecode is binary, but still benefits significantly from zip deflate.
154
+ return { compress: true, compressionLevel: 9 };
155
+ }
156
+ if (hasAlreadyCompressedMagic(prefix)) {
157
+ return { compress: false };
158
+ }
159
+ }
160
+
161
+ const extension = path.extname(fileName).toLowerCase();
162
+ if (alreadyCompressedExtensions.has(extension)) {
163
+ return { compress: false };
164
+ }
165
+ return { compress: true, compressionLevel: 9 };
166
+ }
167
+
168
+ export function zipOptionsForPayloadFile(
169
+ filePath: string,
170
+ entryName = filePath,
171
+ ): ZipEntryOptions {
172
+ return zipOptionsForPayloadEntry(entryName, readFilePrefix(filePath));
173
+ }
package/src/versions.ts CHANGED
@@ -480,12 +480,12 @@ export const versionCommands = {
480
480
  versions: async ({ options }: { options: VersionCommandOptions }) => {
481
481
  const platform = await getPlatform(options.platform);
482
482
  const { appId } = await getSelectedApp(platform);
483
- await listVersions(appId);
483
+ await listVersions(String(appId));
484
484
  },
485
485
  update: async ({ options }: { options: VersionCommandOptions }) => {
486
486
  const platform = await getPlatform(options.platform);
487
487
  const appId = options.appId || (await getSelectedApp(platform)).appId;
488
- let versionId = options.versionId || (await chooseVersion(appId)).id;
488
+ let versionId = options.versionId || (await chooseVersion(String(appId))).id;
489
489
  if (versionId === 'null') {
490
490
  versionId = undefined;
491
491
  }
@@ -508,7 +508,7 @@ export const versionCommands = {
508
508
  }
509
509
  }
510
510
 
511
- const allPkgs = await getAllPackages(appId);
511
+ const allPkgs = await getAllPackages(String(appId));
512
512
 
513
513
  if (!allPkgs) {
514
514
  throw new Error(t('noPackagesFound', { appId }));
@@ -558,7 +558,7 @@ export const versionCommands = {
558
558
  }
559
559
  } else {
560
560
  if (!pkgId) {
561
- pkgId = (await choosePackage(appId)).id;
561
+ pkgId = (await choosePackage(String(appId))).id;
562
562
  }
563
563
 
564
564
  if (!pkgId) {
@@ -575,15 +575,15 @@ export const versionCommands = {
575
575
  }
576
576
 
577
577
  await printDepsChangesForPublish({
578
- appId,
579
- versionId,
578
+ appId: String(appId),
579
+ versionId: String(versionId),
580
580
  pkgs: pkgsToBind,
581
581
  providedVersionDeps: options.versionDeps,
582
582
  });
583
583
 
584
584
  await bindVersionToPackages({
585
- appId,
586
- versionId,
585
+ appId: String(appId),
586
+ versionId: String(versionId),
587
587
  pkgs: pkgsToBind,
588
588
  rollout,
589
589
  dryRun: options.dryRun,
@@ -596,14 +596,14 @@ export const versionCommands = {
596
596
  }) => {
597
597
  const platform = await getPlatform(options.platform);
598
598
  const { appId } = await getSelectedApp(platform);
599
- const versionId = options.versionId || (await chooseVersion(appId)).id;
599
+ const versionId = options.versionId || (await chooseVersion(String(appId))).id;
600
600
 
601
601
  const updateParams: Record<string, string> = {};
602
602
  if (options.name) updateParams.name = options.name;
603
603
  if (options.description) updateParams.description = options.description;
604
604
  if (options.metaInfo) updateParams.metaInfo = options.metaInfo;
605
605
 
606
- await put(`/app/${appId}/version/${versionId}`, updateParams);
606
+ await put(`/app/${String(appId)}/version/${versionId}`, updateParams);
607
607
  console.log(t('operationSuccess'));
608
608
  },
609
609
  deleteVersion: async ({
@@ -619,11 +619,11 @@ export const versionCommands = {
619
619
 
620
620
  let versionId = options.versionId;
621
621
  if (!versionId) {
622
- versionId = (await chooseVersion(appId as string)).id;
622
+ versionId = (await chooseVersion(String(appId))).id;
623
623
  }
624
624
 
625
625
  try {
626
- await doDelete(`/app/${appId}/version/${versionId}`);
626
+ await doDelete(`/app/${String(appId)}/version/${versionId}`);
627
627
  console.log(t('deleteVersionSuccess', { versionId }));
628
628
  } catch (error: any) {
629
629
  throw new Error(