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/lib/app.d.ts +5 -1
- package/lib/app.js +23 -13
- package/lib/bundle-pack.js +2 -1
- package/lib/diff.js +98 -17
- package/lib/module-manager.js +0 -3
- package/lib/package.js +7 -3
- package/lib/utils/zip-entries.d.ts +1 -0
- package/lib/utils/zip-entries.js +61 -0
- package/lib/utils/zip-options.d.ts +9 -0
- package/lib/utils/zip-options.js +272 -0
- package/lib/versions.js +12 -12
- package/package.json +1 -1
- package/src/app.ts +24 -11
- package/src/bundle-pack.ts +6 -1
- package/src/diff.ts +209 -15
- package/src/module-manager.ts +0 -4
- package/src/package.ts +5 -4
- package/src/utils/zip-entries.ts +69 -0
- package/src/utils/zip-options.ts +173 -0
- package/src/versions.ts +12 -12
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 {
|
|
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
|
|
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
|
-
|
|
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 =
|
|
46
|
-
const 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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
492
|
+
const hdiffModule = options.customHdiffModule ?? hdiff;
|
|
493
|
+
if (!hdiffModule?.diff) {
|
|
306
494
|
throw new Error(t('nodeHdiffpatchRequired', { scriptName }));
|
|
307
495
|
}
|
|
308
|
-
return
|
|
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(
|
package/src/module-manager.ts
CHANGED
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
|
|
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 =
|
|
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
|
|
351
|
+
appId = (await getSelectedApp(platform)).appId;
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
// If no packageId provided as argument, let user choose from list
|
package/src/utils/zip-entries.ts
CHANGED
|
@@ -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
|
|
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(
|