react-native-update-cli 2.4.1 → 2.5.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/cli.json CHANGED
@@ -55,6 +55,7 @@
55
55
  "parseApp": {},
56
56
  "parseIpa": {},
57
57
  "parseApk": {},
58
+ "parseAab": {},
58
59
  "packages": {
59
60
  "options": {
60
61
  "platform": {
@@ -172,6 +173,19 @@
172
173
  }
173
174
  }
174
175
  },
176
+ "deleteVersion": {
177
+ "options": {
178
+ "platform": {
179
+ "hasValue": true
180
+ },
181
+ "versionId": {
182
+ "hasValue": true
183
+ },
184
+ "appId": {
185
+ "hasValue": true
186
+ }
187
+ }
188
+ },
175
189
  "build": {
176
190
  "description": "Bundle javascript and copy assets."
177
191
  },
package/lib/bundle.js CHANGED
@@ -509,7 +509,6 @@ async function diffFromPPK(origin, next, output) {
509
509
  throw new Error((0, _i18n.t)('bundleFileNotFound'));
510
510
  }
511
511
  const copies = {};
512
- const copiesv2 = {};
513
512
  const zipfile = new _yazl.ZipFile();
514
513
  const writePromise = new Promise((resolve, reject)=>{
515
514
  zipfile.outputStream.on('error', (err)=>{
@@ -560,7 +559,6 @@ async function diffFromPPK(origin, next, output) {
560
559
  addEntry(base);
561
560
  }
562
561
  copies[entry.fileName] = originMap[entry.crc32];
563
- copiesv2[entry.crc32] = entry.fileName;
564
562
  return;
565
563
  }
566
564
  // New file.
@@ -591,7 +589,6 @@ async function diffFromPPK(origin, next, output) {
591
589
  //console.log({copies, deletes});
592
590
  zipfile.addBuffer(Buffer.from(JSON.stringify({
593
591
  copies,
594
- copiesv2,
595
592
  deletes
596
593
  })), '__diff.json');
597
594
  zipfile.end();
@@ -622,7 +619,6 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
622
619
  throw new Error((0, _i18n.t)('bundleFileNotFound'));
623
620
  }
624
621
  const copies = {};
625
- const copiesv2 = {};
626
622
  const zipfile = new _yazl.ZipFile();
627
623
  const writePromise = new Promise((resolve, reject)=>{
628
624
  zipfile.outputStream.on('error', (err)=>{
@@ -652,7 +648,6 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
652
648
  // If moved from other place
653
649
  if (originMap[entry.crc32]) {
654
650
  copies[entry.fileName] = originMap[entry.crc32];
655
- copiesv2[entry.crc32] = entry.fileName;
656
651
  return;
657
652
  }
658
653
  return new Promise((resolve, reject)=>{
@@ -670,8 +665,7 @@ async function diffFromPackage(origin, next, output, originBundleName, transform
670
665
  }
671
666
  });
672
667
  zipfile.addBuffer(Buffer.from(JSON.stringify({
673
- copies,
674
- copiesv2
668
+ copies
675
669
  })), '__diff.json');
676
670
  zipfile.end();
677
671
  await writePromise;
package/lib/locales/en.js CHANGED
@@ -100,6 +100,7 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
100
100
  uploadingSourcemap: 'Uploading sourcemap',
101
101
  usageDiff: 'Usage: cresc {{command}} <origin> <next>',
102
102
  usageParseApk: 'Usage: cresc parseApk <apk file>',
103
+ usageParseAab: 'Usage: cresc parseAab <aab file>',
103
104
  usageParseApp: 'Usage: cresc parseApp <app file>',
104
105
  usageParseIpa: 'Usage: cresc parseIpa <ipa file>',
105
106
  usageUnderDevelopment: 'Usage is under development now.',
@@ -119,6 +120,8 @@ This can reduce the risk of inconsistent dependencies and supply chain attacks.
119
120
  deletePackageSuccess: 'Native package {{packageId}} deleted successfully',
120
121
  deletePackageError: 'Failed to delete native package {{packageId}}: {{error}}',
121
122
  usageDeletePackage: 'Usage: cresc deletePackage [packageId] --appId [appId]',
123
+ deleteVersionSuccess: 'Version {{versionId}} deleted successfully',
124
+ deleteVersionError: 'Failed to delete version {{versionId}}: {{error}}',
122
125
  bundleFileNotFound: 'Bundle file not found! Please use default bundle file name and path.',
123
126
  diffPackageGenerated: '{{- output}} generated.',
124
127
  nodeBsdiffRequired: 'This function needs "node-bsdiff". Please run "{{scriptName}} install node-bsdiff" to install',
package/lib/locales/zh.js CHANGED
@@ -100,6 +100,7 @@ const _default = {
100
100
  uploadingSourcemap: '正在上传 sourcemap',
101
101
  usageDiff: '用法:pushy {{command}} <origin> <next>',
102
102
  usageParseApk: '使用方法: pushy parseApk apk后缀文件',
103
+ usageParseAab: '使用方法: pushy parseAab aab后缀文件',
103
104
  usageParseApp: '使用方法: pushy parseApp app后缀文件',
104
105
  usageParseIpa: '使用方法: pushy parseIpa ipa后缀文件',
105
106
  usageUploadApk: '使用方法: pushy uploadApk apk后缀文件',
@@ -118,6 +119,8 @@ const _default = {
118
119
  deletePackageSuccess: '原生包 {{packageId}} 删除成功',
119
120
  deletePackageError: '删除原生包 {{packageId}} 失败: {{error}}',
120
121
  usageDeletePackage: '使用方法: pushy deletePackage [packageId] --appId [appId]',
122
+ deleteVersionSuccess: '热更包 {{versionId}} 删除成功',
123
+ deleteVersionError: '删除热更包 {{versionId}} 失败: {{error}}',
121
124
  bundleFileNotFound: '未找到 bundle 文件!请使用默认的 bundle 文件名和路径。',
122
125
  diffPackageGenerated: '{{- output}} 已生成。',
123
126
  nodeBsdiffRequired: '此功能需要 "node-bsdiff"。请运行 "{{scriptName}} install node-bsdiff" 来安装',
package/lib/package.js CHANGED
@@ -235,6 +235,13 @@ const packageCommands = {
235
235
  }
236
236
  console.log(await (0, _utils.getApkInfo)(fn));
237
237
  },
238
+ parseAab: async ({ args })=>{
239
+ const fn = args[0];
240
+ if (!fn || !fn.endsWith('.aab')) {
241
+ throw new Error((0, _i18n.t)('usageParseAab'));
242
+ }
243
+ console.log(await (0, _utils.getAabInfo)(fn));
244
+ },
238
245
  packages: async ({ options })=>{
239
246
  const platform = await (0, _app.getPlatform)(options.platform);
240
247
  const { appId } = await (0, _app.getSelectedApp)(platform);
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ const Zip = require('./zip');
3
+ const yazl = require('yazl');
4
+ const fs = require('fs-extra');
5
+ const path = require('path');
6
+ const { open: openZipFile } = require('yauzl');
7
+ const os = require('os');
8
+ class AabParser extends Zip {
9
+ /**
10
+ * 从 AAB 提取通用 APK
11
+ * 这个方法会合并 base/ 和所有 split/ 目录的内容
12
+ *
13
+ * @param {String} outputPath - 输出 APK 文件路径
14
+ * @param {Object} options - 选项
15
+ * @param {Boolean} options.includeAllSplits - 是否包含所有 split APK(默认 false,只提取 base)
16
+ * @param {Array<String>} options.splits - 指定要包含的 split APK 名称(如果指定,则只包含这些)
17
+ * @returns {Promise<String>} 返回输出文件路径
18
+ */ async extractApk(outputPath, options = {}) {
19
+ const { includeAllSplits = false, splits = null } = options;
20
+ return new Promise((resolve, reject)=>{
21
+ if (typeof this.file !== 'string') {
22
+ return reject(new Error('AAB file path must be a string in Node.js environment'));
23
+ }
24
+ openZipFile(this.file, {
25
+ lazyEntries: true
26
+ }, async (err, zipfile)=>{
27
+ if (err) {
28
+ return reject(err);
29
+ }
30
+ try {
31
+ // 1. 收集所有条目及其数据
32
+ const baseEntries = [];
33
+ const splitEntries = [];
34
+ const metaInfEntries = [];
35
+ let pendingReads = 0;
36
+ let hasError = false;
37
+ const processEntry = (entry, fileName)=>{
38
+ return new Promise((resolve, reject)=>{
39
+ zipfile.openReadStream(entry, (err, readStream)=>{
40
+ if (err) {
41
+ return reject(err);
42
+ }
43
+ const chunks = [];
44
+ readStream.on('data', (chunk)=>chunks.push(chunk));
45
+ readStream.on('end', ()=>{
46
+ const buffer = Buffer.concat(chunks);
47
+ resolve(buffer);
48
+ });
49
+ readStream.on('error', reject);
50
+ });
51
+ });
52
+ };
53
+ zipfile.on('entry', async (entry)=>{
54
+ const fileName = entry.fileName;
55
+ // 跳过目录
56
+ if (fileName.endsWith('/')) {
57
+ zipfile.readEntry();
58
+ return;
59
+ }
60
+ pendingReads++;
61
+ try {
62
+ const buffer = await processEntry(entry, fileName);
63
+ if (fileName.startsWith('base/')) {
64
+ // 将 base/manifest/AndroidManifest.xml 转换为 androidmanifest.xml(APK 中通常是小写)
65
+ // 将 base/resources.arsc 转换为 resources.arsc
66
+ let apkPath = fileName.replace(/^base\//, '');
67
+ if (apkPath === 'manifest/AndroidManifest.xml') {
68
+ apkPath = 'androidmanifest.xml';
69
+ }
70
+ baseEntries.push({
71
+ buffer,
72
+ zipPath: fileName,
73
+ apkPath
74
+ });
75
+ } else if (fileName.startsWith('split/')) {
76
+ splitEntries.push({
77
+ buffer,
78
+ zipPath: fileName
79
+ });
80
+ } else if (fileName.startsWith('META-INF/')) {
81
+ metaInfEntries.push({
82
+ buffer,
83
+ zipPath: fileName,
84
+ apkPath: fileName
85
+ });
86
+ }
87
+ // BundleConfig.pb 和其他文件不需要包含在 APK 中
88
+ pendingReads--;
89
+ zipfile.readEntry();
90
+ } catch (error) {
91
+ pendingReads--;
92
+ if (!hasError) {
93
+ hasError = true;
94
+ reject(error);
95
+ }
96
+ zipfile.readEntry();
97
+ }
98
+ });
99
+ zipfile.on('end', async ()=>{
100
+ // 等待所有读取完成
101
+ while(pendingReads > 0){
102
+ await new Promise((resolve)=>setTimeout(resolve, 10));
103
+ }
104
+ if (hasError) {
105
+ return;
106
+ }
107
+ try {
108
+ // 2. 创建新的 APK 文件
109
+ const zipFile = new yazl.ZipFile();
110
+ // 3. 添加 base 目录的所有文件
111
+ for (const { buffer, apkPath } of baseEntries){
112
+ zipFile.addBuffer(buffer, apkPath);
113
+ }
114
+ // 4. 添加 split APK 的内容(如果需要)
115
+ if (includeAllSplits || splits) {
116
+ const splitsToInclude = splits ? splitEntries.filter((se)=>splits.some((s)=>se.zipPath.includes(s))) : splitEntries;
117
+ await this.mergeSplitApksFromBuffers(zipFile, splitsToInclude);
118
+ }
119
+ // 5. 添加 META-INF(签名信息,虽然可能无效,但保留结构)
120
+ for (const { buffer, apkPath } of metaInfEntries){
121
+ zipFile.addBuffer(buffer, apkPath);
122
+ }
123
+ // 6. 写入文件
124
+ zipFile.outputStream.pipe(fs.createWriteStream(outputPath)).on('close', ()=>{
125
+ resolve(outputPath);
126
+ }).on('error', (err)=>{
127
+ reject(err);
128
+ });
129
+ zipFile.end();
130
+ } catch (error) {
131
+ reject(error);
132
+ }
133
+ });
134
+ zipfile.on('error', reject);
135
+ zipfile.readEntry();
136
+ } catch (error) {
137
+ reject(error);
138
+ }
139
+ });
140
+ });
141
+ }
142
+ /**
143
+ * 合并 split APK 的内容(从已读取的 buffer)
144
+ */ async mergeSplitApksFromBuffers(zipFile, splitEntries) {
145
+ for (const { buffer: splitBuffer } of splitEntries){
146
+ if (splitBuffer) {
147
+ // 创建一个临时的 ZIP 文件来读取 split APK
148
+ const tempSplitPath = path.join(os.tmpdir(), `split_${Date.now()}_${Math.random().toString(36).substr(2, 9)}.apk`);
149
+ try {
150
+ await fs.writeFile(tempSplitPath, splitBuffer);
151
+ await new Promise((resolve, reject)=>{
152
+ openZipFile(tempSplitPath, {
153
+ lazyEntries: true
154
+ }, async (err, splitZipfile)=>{
155
+ if (err) {
156
+ return reject(err);
157
+ }
158
+ splitZipfile.on('entry', (splitEntry)=>{
159
+ // 跳过 META-INF,因为签名信息不需要合并
160
+ if (splitEntry.fileName.startsWith('META-INF/')) {
161
+ splitZipfile.readEntry();
162
+ return;
163
+ }
164
+ splitZipfile.openReadStream(splitEntry, (err, readStream)=>{
165
+ if (err) {
166
+ splitZipfile.readEntry();
167
+ return;
168
+ }
169
+ const chunks = [];
170
+ readStream.on('data', (chunk)=>chunks.push(chunk));
171
+ readStream.on('end', ()=>{
172
+ const buffer = Buffer.concat(chunks);
173
+ // 注意:如果文件已存在(在 base 中),split 中的会覆盖 base 中的
174
+ zipFile.addBuffer(buffer, splitEntry.fileName);
175
+ splitZipfile.readEntry();
176
+ });
177
+ readStream.on('error', ()=>{
178
+ splitZipfile.readEntry();
179
+ });
180
+ });
181
+ });
182
+ splitZipfile.on('end', resolve);
183
+ splitZipfile.on('error', reject);
184
+ splitZipfile.readEntry();
185
+ });
186
+ });
187
+ } finally{
188
+ // 清理临时文件
189
+ await fs.remove(tempSplitPath).catch(()=>{});
190
+ }
191
+ }
192
+ }
193
+ }
194
+ /**
195
+ * 解析 AAB 文件信息(类似 APK parser 的 parse 方法)
196
+ * 注意:AAB 中的 AndroidManifest.xml 在 base/manifest/AndroidManifest.xml
197
+ */ async parse() {
198
+ // 尝试从 base/manifest/AndroidManifest.xml 读取 manifest
199
+ // 但 AAB 中的 manifest 可能是二进制格式,需要特殊处理
200
+ const manifestPath = 'base/manifest/AndroidManifest.xml';
201
+ const ResourceName = /^base\/resources\.arsc$/;
202
+ try {
203
+ const manifestBuffer = await this.getEntry(new RegExp(`^${manifestPath.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`));
204
+ if (!manifestBuffer) {
205
+ throw new Error("AndroidManifest.xml can't be found in AAB base/manifest/");
206
+ }
207
+ let apkInfo = this._parseManifest(manifestBuffer);
208
+ // 尝试解析 resources.arsc
209
+ try {
210
+ const resourceBuffer = await this.getEntry(ResourceName);
211
+ if (resourceBuffer) {
212
+ const resourceMap = this._parseResourceMap(resourceBuffer);
213
+ const { mapInfoResource } = require('./utils');
214
+ apkInfo = mapInfoResource(apkInfo, resourceMap);
215
+ }
216
+ } catch (e) {
217
+ // resources.arsc 解析失败不影响基本信息
218
+ console.warn('[Warning] Failed to parse resources.arsc:', e.message);
219
+ }
220
+ return apkInfo;
221
+ } catch (error) {
222
+ throw new Error(`Failed to parse AAB: ${error.message}`);
223
+ }
224
+ }
225
+ /**
226
+ * Parse manifest
227
+ * @param {Buffer} buffer // manifest file's buffer
228
+ */ _parseManifest(buffer) {
229
+ try {
230
+ const ManifestXmlParser = require('./xml-parser/manifest');
231
+ const parser = new ManifestXmlParser(buffer, {
232
+ ignore: [
233
+ 'application.activity',
234
+ 'application.service',
235
+ 'application.receiver',
236
+ 'application.provider',
237
+ 'permission-group'
238
+ ]
239
+ });
240
+ return parser.parse();
241
+ } catch (e) {
242
+ throw new Error('Parse AndroidManifest.xml error: ' + e.message);
243
+ }
244
+ }
245
+ /**
246
+ * Parse resourceMap
247
+ * @param {Buffer} buffer // resourceMap file's buffer
248
+ */ _parseResourceMap(buffer) {
249
+ try {
250
+ const ResourceFinder = require('./resource-finder');
251
+ return new ResourceFinder().processResourceTable(buffer);
252
+ } catch (e) {
253
+ throw new Error('Parser resources.arsc error: ' + e.message);
254
+ }
255
+ }
256
+ /**
257
+ * parser for parsing .aab file
258
+ * @param {String | File | Blob} file // file's path in Node, instance of File or Blob in Browser
259
+ */ constructor(file){
260
+ super(file);
261
+ if (!(this instanceof AabParser)) {
262
+ return new AabParser(file);
263
+ }
264
+ }
265
+ }
266
+ module.exports = AabParser;
@@ -61,7 +61,7 @@ class ApkParser extends Zip {
61
61
  });
62
62
  return parser.parse();
63
63
  } catch (e) {
64
- throw new Error('Parse AndroidManifest.xml error: ', e);
64
+ throw new Error('Parse AndroidManifest.xml error: ' + (e.message || e));
65
65
  }
66
66
  }
67
67
  /**
@@ -11,10 +11,12 @@ Object.defineProperty(exports, "default", {
11
11
  const ApkParser = require('./apk');
12
12
  const IpaParser = require('./ipa');
13
13
  const AppParser = require('./app');
14
+ const AabParser = require('./aab');
14
15
  const supportFileTypes = [
15
16
  'ipa',
16
17
  'apk',
17
- 'app'
18
+ 'app',
19
+ 'aab'
18
20
  ];
19
21
  class AppInfoParser {
20
22
  parse() {
@@ -30,7 +32,7 @@ class AppInfoParser {
30
32
  const splits = (typeof file === 'string' ? file : file.name).split('.');
31
33
  const fileType = splits[splits.length - 1].toLowerCase();
32
34
  if (!supportFileTypes.includes(fileType)) {
33
- throw new Error('Unsupported file type, only support .ipa or .apk or .app file.');
35
+ throw new Error('Unsupported file type, only support .ipa, .apk, .app, or .aab file.');
34
36
  }
35
37
  this.file = file;
36
38
  switch(fileType){
@@ -43,6 +45,9 @@ class AppInfoParser {
43
45
  case 'app':
44
46
  this.parser = new AppParser(this.file);
45
47
  break;
48
+ case 'aab':
49
+ this.parser = new AabParser(this.file);
50
+ break;
46
51
  }
47
52
  }
48
53
  }
@@ -12,6 +12,9 @@ _export(exports, {
12
12
  checkPlugins: function() {
13
13
  return _checkplugin.checkPlugins;
14
14
  },
15
+ getAabInfo: function() {
16
+ return getAabInfo;
17
+ },
15
18
  getApkInfo: function() {
16
19
  return getApkInfo;
17
20
  },
@@ -169,6 +172,140 @@ async function getIpaInfo(fn) {
169
172
  ...appCredential
170
173
  };
171
174
  }
175
+ async function getAabInfo(fn) {
176
+ const protobuf = require('protobufjs');
177
+ const root = await protobuf.load(_path.default.join(__dirname, '../../proto/Resources.proto'));
178
+ const XmlNode = root.lookupType('aapt.pb.XmlNode');
179
+ const buffer = await readZipEntry(fn, 'base/manifest/AndroidManifest.xml');
180
+ const message = XmlNode.decode(buffer);
181
+ const object = XmlNode.toObject(message, {
182
+ enums: String,
183
+ longs: String,
184
+ bytes: String,
185
+ defaults: true,
186
+ arrays: true
187
+ });
188
+ const manifestElement = object.element;
189
+ if (manifestElement.name !== 'manifest') {
190
+ throw new Error('Invalid manifest');
191
+ }
192
+ let versionName = '';
193
+ for (const attr of manifestElement.attribute){
194
+ if (attr.name === 'versionName') {
195
+ versionName = attr.value;
196
+ }
197
+ }
198
+ let buildTime = 0;
199
+ const appCredential = {};
200
+ // Find application node
201
+ const applicationNode = manifestElement.child.find((c)=>c.element && c.element.name === 'application');
202
+ if (applicationNode) {
203
+ const metaDataNodes = applicationNode.element.child.filter((c)=>c.element && c.element.name === 'meta-data');
204
+ for (const meta of metaDataNodes){
205
+ let name = '';
206
+ let value = '';
207
+ let resourceId = 0;
208
+ for (const attr of meta.element.attribute){
209
+ if (attr.name === 'name') {
210
+ name = attr.value;
211
+ }
212
+ if (attr.name === 'value') {
213
+ var _attr_compiledItem_ref, _attr_compiledItem, _attr_compiledItem_prim, _attr_compiledItem1;
214
+ value = attr.value;
215
+ if ((_attr_compiledItem = attr.compiledItem) == null ? void 0 : (_attr_compiledItem_ref = _attr_compiledItem.ref) == null ? void 0 : _attr_compiledItem_ref.id) {
216
+ resourceId = attr.compiledItem.ref.id;
217
+ } else if ((_attr_compiledItem1 = attr.compiledItem) == null ? void 0 : (_attr_compiledItem_prim = _attr_compiledItem1.prim) == null ? void 0 : _attr_compiledItem_prim.intDecimalValue) {
218
+ value = attr.compiledItem.prim.intDecimalValue.toString();
219
+ }
220
+ }
221
+ }
222
+ if (name === 'pushy_build_time') {
223
+ if (resourceId > 0) {
224
+ const resolvedValue = await resolveResource(fn, resourceId, root);
225
+ if (resolvedValue) {
226
+ value = resolvedValue;
227
+ }
228
+ }
229
+ buildTime = Number(value);
230
+ }
231
+ }
232
+ }
233
+ if (buildTime === 0) {
234
+ throw new Error((0, _i18n.t)('buildTimeNotFound'));
235
+ }
236
+ return {
237
+ versionName,
238
+ buildTime,
239
+ ...appCredential
240
+ };
241
+ }
242
+ async function readZipEntry(fn, entryName) {
243
+ const yauzl = require('yauzl');
244
+ return new Promise((resolve, reject)=>{
245
+ yauzl.open(fn, {
246
+ lazyEntries: true
247
+ }, (err, zipfile)=>{
248
+ if (err) return reject(err);
249
+ let found = false;
250
+ zipfile.readEntry();
251
+ zipfile.on('entry', (entry)=>{
252
+ if (entry.fileName === entryName) {
253
+ found = true;
254
+ zipfile.openReadStream(entry, (err, readStream)=>{
255
+ if (err) return reject(err);
256
+ const chunks = [];
257
+ readStream.on('data', (chunk)=>chunks.push(chunk));
258
+ readStream.on('end', ()=>resolve(Buffer.concat(chunks)));
259
+ readStream.on('error', reject);
260
+ });
261
+ } else {
262
+ zipfile.readEntry();
263
+ }
264
+ });
265
+ zipfile.on('end', ()=>{
266
+ if (!found) reject(new Error(`${entryName} not found in AAB`));
267
+ });
268
+ zipfile.on('error', reject);
269
+ });
270
+ });
271
+ }
272
+ async function resolveResource(fn, resourceId, root) {
273
+ const pkgId = resourceId >> 24 & 0xff;
274
+ const typeId = resourceId >> 16 & 0xff;
275
+ const entryId = resourceId & 0xffff;
276
+ try {
277
+ const buffer = await readZipEntry(fn, 'base/resources.pb');
278
+ const ResourceTable = root.lookupType('aapt.pb.ResourceTable');
279
+ const message = ResourceTable.decode(buffer);
280
+ const object = ResourceTable.toObject(message, {
281
+ enums: String,
282
+ longs: String,
283
+ bytes: String,
284
+ defaults: true,
285
+ arrays: true
286
+ });
287
+ // Find package
288
+ const pkg = object.package.find((p)=>p.packageId === pkgId);
289
+ if (!pkg) return null;
290
+ // Find type
291
+ const type = pkg.type.find((t)=>t.typeId === typeId);
292
+ if (!type) return null;
293
+ // Find entry
294
+ const entry = type.entry.find((e)=>e.entryId === entryId);
295
+ if (!entry) return null;
296
+ // Get value from configValue
297
+ if (entry.configValue && entry.configValue.length > 0) {
298
+ var _val_item;
299
+ const val = entry.configValue[0].value;
300
+ if ((_val_item = val.item) == null ? void 0 : _val_item.str) {
301
+ return val.item.str.value;
302
+ }
303
+ }
304
+ } catch (e) {
305
+ console.warn('Failed to resolve resource:', e);
306
+ }
307
+ return null;
308
+ }
172
309
  const localDir = _path.default.resolve(_os.default.homedir(), _constants.tempDir);
173
310
  _fsextra.default.ensureDirSync(localDir);
174
311
  function saveToLocal(originPath, destName) {
package/lib/versions.js CHANGED
@@ -287,5 +287,27 @@ const versionCommands = {
287
287
  if (options.metaInfo) updateParams.metaInfo = options.metaInfo;
288
288
  await (0, _api.put)(`/app/${appId}/version/${versionId}`, updateParams);
289
289
  console.log((0, _i18n.t)('operationSuccess'));
290
+ },
291
+ deleteVersion: async ({ options })=>{
292
+ let appId = options.appId;
293
+ if (!appId) {
294
+ const platform = await (0, _app.getPlatform)(options.platform);
295
+ appId = (await (0, _app.getSelectedApp)(platform)).appId;
296
+ }
297
+ let versionId = options.versionId;
298
+ if (!versionId) {
299
+ versionId = (await chooseVersion(appId)).id;
300
+ }
301
+ try {
302
+ await (0, _api.doDelete)(`/app/${appId}/version/${versionId}`);
303
+ console.log((0, _i18n.t)('deleteVersionSuccess', {
304
+ versionId
305
+ }));
306
+ } catch (error) {
307
+ throw new Error((0, _i18n.t)('deleteVersionError', {
308
+ versionId,
309
+ error: error.message
310
+ }));
311
+ }
290
312
  }
291
313
  };
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "react-native-update-cli",
3
- "version": "2.4.1",
3
+ "version": "2.5.0",
4
4
  "description": "command line tool for react-native-update (remote updates for react native)",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "pushy": "lib/index.js",
8
8
  "cresc": "lib/index.js"
9
9
  },
10
- "files": ["lib", "src", "cli.json"],
10
+ "files": ["lib", "src", "proto", "cli.json"],
11
11
  "scripts": {
12
12
  "build": "swc src -d lib --strip-leading-paths",
13
13
  "prepublishOnly": "npm run build && chmod +x lib/index.js",
@@ -45,6 +45,7 @@
45
45
  "plist": "^3.1.0",
46
46
  "progress": "^2.0.3",
47
47
  "properties": "^1.2.1",
48
+ "protobufjs": "^7.5.4",
48
49
  "read": "^4.1.0",
49
50
  "registry-auth-token": "^5.1.0",
50
51
  "semver": "^7.7.2",