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,561 @@
1
+ "use strict";
2
+ /**
3
+ * [INPUT]: 依赖 Hermes HBC 二进制布局、Buffer/文件句柄与差分字段规则
4
+ * [OUTPUT]: 对外提供 HBC 布局探测、可逆变换、文件变换、段范围解析及版本元数据能力
5
+ * [POS]: CLI Hermes delta 优化底座,为 base-bytecode 差分提供确定性字节变换
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.HBC_TRANSFORM_VERSION = exports.HBC_LAYOUTS = void 0;
9
+ exports.compileLayoutToWire = compileLayoutToWire;
10
+ exports.getHbcVersion = getHbcVersion;
11
+ exports.findLayouts = findLayouts;
12
+ exports.resolveHbcSections = resolveHbcSections;
13
+ exports.transformHbc = transformHbc;
14
+ exports.pickLayout = pickLayout;
15
+ exports.transformHbcWithLayout = transformHbcWithLayout;
16
+ exports.transformHbcWithLayoutInPlace = transformHbcWithLayoutInPlace;
17
+ exports.transformHbcFile = transformHbcFile;
18
+ exports.tryTransformPair = tryTransformPair;
19
+ exports.tryTransformPairFiles = tryTransformPairFiles;
20
+ /**
21
+ * Hermes 字节码(HBC)delta-friendly 可逆变换。
22
+ *
23
+ * 原理:HBC 中 functionHeaders / smallStringTable 等表的条目携带绝对偏移,
24
+ * 任意一处内容增删都会让其后所有条目的偏移整体 +N,精确匹配型 diff(hdiff)
25
+ * 无法复用这些被移位的条目。对这些偏移字段做前项差分(wrapping,模字段位宽)
26
+ * 后,整体移位在差分域退化为单点变化,diff 显著变小。
27
+ *
28
+ * 布局描述表是纯数据:解释器(本文件 + 客户端 C 版)不含任何版本分支。
29
+ * 新 Hermes 版本只需在 HBC_LAYOUTS 增加/扩展一个条目。CLI 生成 diff 时把
30
+ * 实际使用的描述表以 wire 格式写入 __diff.json,客户端按表执行 T⁻¹,
31
+ * 因此客户端无需跟进 HBC 版本。
32
+ *
33
+ * wrapping 差分与单调性无关、恒可逆:T⁻¹(T(x)) === x 对任意输入成立
34
+ * (只要两侧使用同一份描述表)。
35
+ */
36
+ const promises_1 = require("node:fs/promises");
37
+ const HBC_MAGIC = Buffer.from([0xc6, 0x1f, 0xbc, 0x03, 0xc1, 0x03, 0x19, 0x1f]);
38
+ const HEADER_COUNTS_OFFSET = 32; // magic(8) + version(4) + sourceHash(20)
39
+ const HBC_HEADER_SIZE = 128;
40
+ const FUNC_OFFSET_25 = { byte: 0, bit: 0, bits: 25 };
41
+ const STRING_OFFSET_23 = { byte: 0, bit: 1, bits: 23 };
42
+ const PAIR_OFFSET_32 = { byte: 0, bit: 0, bits: 32 };
43
+ // v98 两种头部变体共享的段布局
44
+ const V98_SECTIONS = [
45
+ {
46
+ name: 'functionHeaders',
47
+ countField: 'functionCount',
48
+ entrySize: 12,
49
+ deltaFields: [FUNC_OFFSET_25],
50
+ },
51
+ { name: 'stringKinds', countField: 'stringKindCount', entrySize: 4 },
52
+ { name: 'identifierHashes', countField: 'identifierCount', entrySize: 4 },
53
+ {
54
+ name: 'smallStringTable',
55
+ countField: 'stringCount',
56
+ entrySize: 4,
57
+ deltaFields: [STRING_OFFSET_23],
58
+ },
59
+ {
60
+ name: 'overflowStringTable',
61
+ countField: 'overflowStringCount',
62
+ entrySize: 8,
63
+ deltaFields: [PAIR_OFFSET_32],
64
+ },
65
+ { name: 'stringStorage', countField: 'stringStorageSize', entrySize: 1 },
66
+ {
67
+ name: 'literalValueBuffer',
68
+ countField: 'literalValueBufferSize',
69
+ entrySize: 1,
70
+ },
71
+ { name: 'objKeyBuffer', countField: 'objKeyBufferSize', entrySize: 1 },
72
+ // ShapeTableEntry 8B:{keyBufferOffset u32, numProps u32}
73
+ {
74
+ name: 'objShapeTable',
75
+ countField: 'objShapeTableCount',
76
+ entrySize: 8,
77
+ deltaFields: [PAIR_OFFSET_32],
78
+ },
79
+ {
80
+ name: 'bigIntTable',
81
+ countField: 'bigIntCount',
82
+ entrySize: 8,
83
+ deltaFields: [PAIR_OFFSET_32],
84
+ },
85
+ { name: 'bigIntStorage', countField: 'bigIntStorageSize', entrySize: 1 },
86
+ {
87
+ name: 'regExpTable',
88
+ countField: 'regExpCount',
89
+ entrySize: 8,
90
+ deltaFields: [PAIR_OFFSET_32],
91
+ },
92
+ { name: 'regExpStorage', countField: 'regExpStorageSize', entrySize: 1 },
93
+ { name: 'cjsModuleTable', countField: 'cjsModuleCount', entrySize: 8 },
94
+ {
95
+ name: 'functionSourceTable',
96
+ countField: 'functionSourceCount',
97
+ entrySize: 8,
98
+ },
99
+ ];
100
+ /**
101
+ * 布局描述表(唯一需要跟随 Hermes 版本演进的数据)。
102
+ * 字段序照抄 hermes 源码 include/hermes/BCGen/HBC/BytecodeFileFormat.h。
103
+ * v97 为 Static Hermes 短期预览版本,生产占比为零,不支持(自动回退)。
104
+ */
105
+ exports.HBC_LAYOUTS = [
106
+ {
107
+ // hermes 0.12 主线(RN 0.69~0.81+),v87 引入 bigInt 字段后布局稳定
108
+ minVersion: 87,
109
+ maxVersion: 96,
110
+ headerFields: [
111
+ 'fileLength',
112
+ 'globalCodeIndex',
113
+ 'functionCount',
114
+ 'stringKindCount',
115
+ 'identifierCount',
116
+ 'stringCount',
117
+ 'overflowStringCount',
118
+ 'stringStorageSize',
119
+ 'bigIntCount',
120
+ 'bigIntStorageSize',
121
+ 'regExpCount',
122
+ 'regExpStorageSize',
123
+ 'arrayBufferSize',
124
+ 'objKeyBufferSize',
125
+ 'objValueBufferSize',
126
+ 'segmentID',
127
+ 'cjsModuleCount',
128
+ 'functionSourceCount',
129
+ 'debugInfoOffset',
130
+ ],
131
+ sections: [
132
+ // SmallFuncHeader 16B:word0 offset:25|paramCount:7,word2 infoOffset:25|frameSize:7
133
+ {
134
+ name: 'functionHeaders',
135
+ countField: 'functionCount',
136
+ entrySize: 16,
137
+ deltaFields: [FUNC_OFFSET_25, { byte: 8, bit: 0, bits: 25 }],
138
+ },
139
+ { name: 'stringKinds', countField: 'stringKindCount', entrySize: 4 },
140
+ { name: 'identifierHashes', countField: 'identifierCount', entrySize: 4 },
141
+ // SmallStringTableEntry 4B:isUTF16:1|offset:23|length:8
142
+ {
143
+ name: 'smallStringTable',
144
+ countField: 'stringCount',
145
+ entrySize: 4,
146
+ deltaFields: [STRING_OFFSET_23],
147
+ },
148
+ // OverflowStringTableEntry 8B:{offset u32, length u32}
149
+ {
150
+ name: 'overflowStringTable',
151
+ countField: 'overflowStringCount',
152
+ entrySize: 8,
153
+ deltaFields: [PAIR_OFFSET_32],
154
+ },
155
+ { name: 'stringStorage', countField: 'stringStorageSize', entrySize: 1 },
156
+ { name: 'arrayBuffer', countField: 'arrayBufferSize', entrySize: 1 },
157
+ { name: 'objKeyBuffer', countField: 'objKeyBufferSize', entrySize: 1 },
158
+ {
159
+ name: 'objValueBuffer',
160
+ countField: 'objValueBufferSize',
161
+ entrySize: 1,
162
+ },
163
+ {
164
+ name: 'bigIntTable',
165
+ countField: 'bigIntCount',
166
+ entrySize: 8,
167
+ deltaFields: [PAIR_OFFSET_32],
168
+ },
169
+ { name: 'bigIntStorage', countField: 'bigIntStorageSize', entrySize: 1 },
170
+ {
171
+ name: 'regExpTable',
172
+ countField: 'regExpCount',
173
+ entrySize: 8,
174
+ deltaFields: [PAIR_OFFSET_32],
175
+ },
176
+ { name: 'regExpStorage', countField: 'regExpStorageSize', entrySize: 1 },
177
+ { name: 'cjsModuleTable', countField: 'cjsModuleCount', entrySize: 8 },
178
+ {
179
+ name: 'functionSourceTable',
180
+ countField: 'functionSourceCount',
181
+ entrySize: 8,
182
+ },
183
+ ],
184
+ },
185
+ {
186
+ // Static Hermes 晚期 v98(生产观测到的主流形态):相对早期 v98 头部
187
+ // 多一个 numStringSwitchImms 槽(hermes static_h 在不 bump 版本号的
188
+ // 情况下插入,同一版本号 98 因此存在两种布局,依结构校验区分)。
189
+ // 段布局与早期 v98 相同。
190
+ minVersion: 98,
191
+ maxVersion: 98,
192
+ headerFields: [
193
+ 'fileLength',
194
+ 'globalCodeIndex',
195
+ 'functionCount',
196
+ 'stringKindCount',
197
+ 'identifierCount',
198
+ 'stringCount',
199
+ 'overflowStringCount',
200
+ 'stringStorageSize',
201
+ 'bigIntCount',
202
+ 'bigIntStorageSize',
203
+ 'regExpCount',
204
+ 'regExpStorageSize',
205
+ 'literalValueBufferSize',
206
+ 'objKeyBufferSize',
207
+ 'objShapeTableCount',
208
+ 'numStringSwitchImms',
209
+ 'segmentID',
210
+ 'cjsModuleCount',
211
+ 'functionSourceCount',
212
+ 'debugInfoOffset',
213
+ ],
214
+ sections: V98_SECTIONS,
215
+ },
216
+ {
217
+ // Static Hermes 早期 v98(hermes c00cc57595 时点,无 numStringSwitchImms)。
218
+ // 相对 v96:SmallFuncHeader 16B→12B(去 infoOffset/environmentSize);
219
+ // objValueBuffer(字节段)→ objShapeTable(8B 条目);
220
+ // arrayBufferSize 更名 literalValueBufferSize(槽位不变)。
221
+ minVersion: 98,
222
+ maxVersion: 98,
223
+ headerFields: [
224
+ 'fileLength',
225
+ 'globalCodeIndex',
226
+ 'functionCount',
227
+ 'stringKindCount',
228
+ 'identifierCount',
229
+ 'stringCount',
230
+ 'overflowStringCount',
231
+ 'stringStorageSize',
232
+ 'bigIntCount',
233
+ 'bigIntStorageSize',
234
+ 'regExpCount',
235
+ 'regExpStorageSize',
236
+ 'literalValueBufferSize',
237
+ 'objKeyBufferSize',
238
+ 'objShapeTableCount',
239
+ 'segmentID',
240
+ 'cjsModuleCount',
241
+ 'functionSourceCount',
242
+ 'debugInfoOffset',
243
+ ],
244
+ sections: V98_SECTIONS,
245
+ },
246
+ ];
247
+ function compileLayoutToWire(layout) {
248
+ if (layout.headerFields[0] !== 'fileLength' ||
249
+ layout.headerFields[layout.headerFields.length - 1] !== 'debugInfoOffset') {
250
+ throw new Error('HbcLayout must start with fileLength and end with debugInfoOffset');
251
+ }
252
+ return {
253
+ counts: layout.headerFields.length,
254
+ sections: layout.sections.map((s) => [
255
+ layout.headerFields.indexOf(s.countField),
256
+ s.entrySize,
257
+ (s.deltaFields ?? []).map((f) => [f.byte, f.bit, f.bits]),
258
+ ]),
259
+ };
260
+ }
261
+ function getHbcVersion(buf) {
262
+ if (buf.length < HBC_HEADER_SIZE)
263
+ return null;
264
+ if (!buf.subarray(0, 8).equals(HBC_MAGIC))
265
+ return null;
266
+ return buf.readUInt32LE(8);
267
+ }
268
+ /** 同一版本号可能有多个候选布局(按声明顺序即优先级)。 */
269
+ function findLayouts(version) {
270
+ return exports.HBC_LAYOUTS.filter((l) => version >= l.minVersion && version <= l.maxVersion);
271
+ }
272
+ const align4 = (x) => (x + 3) & ~3;
273
+ /**
274
+ * 按描述表解析并校验段边界。任何不一致(截断、计数越界、段区间超出
275
+ * debugInfoOffset)都返回 null——调用方回退到不变换路径。
276
+ * 描述表视为不可信输入,所有算术做显式边界检查。
277
+ */
278
+ function resolveSections(buf, layout) {
279
+ return resolveSectionsFromHeader(buf, buf.length, layout);
280
+ }
281
+ /**
282
+ * 只依赖头部 128 字节与文件总长即可解析段边界(表偏移全部来自头部计数),
283
+ * 文件版变换据此无需把整个 bundle 读进内存。
284
+ */
285
+ function resolveSectionsFromHeader(header, fileLength, layout) {
286
+ const headerEnd = HEADER_COUNTS_OFFSET + layout.headerFields.length * 4;
287
+ if (header.length < HBC_HEADER_SIZE ||
288
+ fileLength < HBC_HEADER_SIZE ||
289
+ headerEnd > HBC_HEADER_SIZE) {
290
+ return null;
291
+ }
292
+ const counts = {};
293
+ layout.headerFields.forEach((name, i) => {
294
+ counts[name] = header.readUInt32LE(HEADER_COUNTS_OFFSET + i * 4);
295
+ });
296
+ if (counts.fileLength !== fileLength)
297
+ return null;
298
+ const debugInfoOffset = counts.debugInfoOffset;
299
+ if (!Number.isInteger(debugInfoOffset) ||
300
+ debugInfoOffset < HBC_HEADER_SIZE ||
301
+ debugInfoOffset > fileLength) {
302
+ return null;
303
+ }
304
+ const resolved = [];
305
+ let off = HBC_HEADER_SIZE;
306
+ for (const desc of layout.sections) {
307
+ const count = counts[desc.countField];
308
+ if (count === undefined || count > 0x0fffffff)
309
+ return null;
310
+ const size = count * desc.entrySize;
311
+ off = align4(off);
312
+ if (off + size > debugInfoOffset)
313
+ return null;
314
+ for (const f of desc.deltaFields ?? []) {
315
+ // 差分字段必须完整落在条目内且可用一次 u32 读写覆盖
316
+ if (f.bits < 1 ||
317
+ f.bits > 32 ||
318
+ f.bit + f.bits > 32 ||
319
+ f.byte + 4 > desc.entrySize) {
320
+ return null;
321
+ }
322
+ }
323
+ resolved.push({ start: off, size, desc });
324
+ off += size;
325
+ }
326
+ return resolved;
327
+ }
328
+ /**
329
+ * Section byte ranges of an HBC file from its 128-byte header and total
330
+ * length, keyed by section name (arrayBuffer, objKeyBuffer, ... for v87–96;
331
+ * literalValueBuffer, objShapeTable, ... for v98). Null when no known layout
332
+ * fits, which callers treat as "cannot read this file structurally".
333
+ */
334
+ function resolveHbcSections(header, fileLength) {
335
+ const version = getHbcVersion(header);
336
+ if (version === null)
337
+ return null;
338
+ for (const layout of findLayouts(version)) {
339
+ const resolved = resolveSectionsFromHeader(header, fileLength, layout);
340
+ if (resolved) {
341
+ return {
342
+ version,
343
+ sections: new Map(resolved.map((s) => [s.desc.name, { start: s.start, size: s.size }])),
344
+ };
345
+ }
346
+ }
347
+ return null;
348
+ }
349
+ function applyDelta(out, section, inverse) {
350
+ const { start, size, desc } = section;
351
+ for (const f of desc.deltaFields ?? []) {
352
+ // bits === 32 时掩码为全 1;>>> 保持无符号语义
353
+ const mask = f.bits === 32 ? 0xffffffff : ((1 << f.bits) - 1) << f.bit;
354
+ const fieldMask = f.bits === 32 ? 0xffffffff : (1 << f.bits) - 1;
355
+ let prev = 0;
356
+ for (let p = start + f.byte; p < start + size; p += desc.entrySize) {
357
+ const word = out.readUInt32LE(p);
358
+ const val = (word >>> f.bit) & fieldMask;
359
+ let enc;
360
+ if (!inverse) {
361
+ enc = (val - prev) & fieldMask;
362
+ prev = val;
363
+ }
364
+ else {
365
+ enc = (val + prev) & fieldMask;
366
+ prev = enc;
367
+ }
368
+ out.writeUInt32LE(((word & ~mask) | (enc << f.bit)) >>> 0, p);
369
+ }
370
+ }
371
+ }
372
+ /**
373
+ * 对 HBC buffer 执行变换(inverse=false)或逆变换(inverse=true)。
374
+ * 同一版本号可能存在多个候选布局(如 v98 早/晚两种头部),按声明顺序
375
+ * 取第一个通过结构校验的。非 HBC / 无可用布局 → 返回 null。
376
+ * 注意:变换不改动头部计数,同一文件正/逆向解析选中的布局必然一致。
377
+ */
378
+ function transformHbc(buf, inverse = false) {
379
+ const layout = pickLayout(buf);
380
+ if (!layout)
381
+ return null;
382
+ return transformHbcWithLayout(buf, layout, inverse);
383
+ }
384
+ /** 返回该 buffer 结构上匹配的布局(候选中第一个校验通过者)。 */
385
+ function pickLayout(buf) {
386
+ const version = getHbcVersion(buf);
387
+ if (version === null)
388
+ return null;
389
+ for (const layout of findLayouts(version)) {
390
+ if (resolveSections(buf, layout))
391
+ return layout;
392
+ }
393
+ return null;
394
+ }
395
+ function transformHbcWithLayout(buf, layout, inverse) {
396
+ const sections = resolveSections(buf, layout);
397
+ if (!sections)
398
+ return null;
399
+ const out = Buffer.from(buf);
400
+ applyDeltaSections(out, sections, inverse);
401
+ return out;
402
+ }
403
+ /**
404
+ * 原地变换:直接改写 buf,不再分配整块副本。调用方须持有 buf 的所有权
405
+ * (例如 patch() 刚返回的新 Buffer)。结构不匹配时返回 false 且 buf 不变。
406
+ */
407
+ function transformHbcWithLayoutInPlace(buf, layout, inverse) {
408
+ const sections = resolveSections(buf, layout);
409
+ if (!sections)
410
+ return false;
411
+ applyDeltaSections(buf, sections, inverse);
412
+ return true;
413
+ }
414
+ function applyDeltaSections(out, sections, inverse) {
415
+ for (const s of sections) {
416
+ if (s.desc.deltaFields?.length)
417
+ applyDelta(out, s, inverse);
418
+ }
419
+ }
420
+ /** 读取文件头 128 字节与总长;文件过短或非 HBC 时返回 null。 */
421
+ async function readHbcFileHeader(filePath) {
422
+ const { size } = await (0, promises_1.stat)(filePath);
423
+ if (size < HBC_HEADER_SIZE)
424
+ return null;
425
+ const header = Buffer.alloc(HBC_HEADER_SIZE);
426
+ const handle = await (0, promises_1.open)(filePath, 'r');
427
+ try {
428
+ await readExact(handle, header, 0);
429
+ }
430
+ finally {
431
+ await handle.close();
432
+ }
433
+ if (getHbcVersion(header) === null)
434
+ return null;
435
+ return { header, size };
436
+ }
437
+ async function readExact(handle, buffer, position) {
438
+ let done = 0;
439
+ while (done < buffer.length) {
440
+ const { bytesRead } = await handle.read(buffer, done, buffer.length - done, position + done);
441
+ if (bytesRead <= 0) {
442
+ throw new Error('unexpected end of file while reading HBC section');
443
+ }
444
+ done += bytesRead;
445
+ }
446
+ }
447
+ async function writeExact(handle, buffer, position) {
448
+ let done = 0;
449
+ while (done < buffer.length) {
450
+ const { bytesWritten } = await handle.write(buffer, done, buffer.length - done, position + done);
451
+ if (bytesWritten <= 0) {
452
+ throw new Error('short write while rewriting HBC section');
453
+ }
454
+ done += bytesWritten;
455
+ }
456
+ }
457
+ /**
458
+ * 文件版 transformHbcWithLayout:把 src 复制到 dst,再仅对含差分字段的
459
+ * 段(位于 [128, 最后一段末尾) 区间内)做定位读写改写。任一时刻内存中
460
+ * 只有一个段的内容,不会持有整个 bundle。输出与内存版逐字节一致。
461
+ * 结构不匹配时返回 false 并清理 dst。
462
+ */
463
+ async function transformHbcFile(src, dst, layout, inverse) {
464
+ const info = await readHbcFileHeader(src);
465
+ const sections = info
466
+ ? resolveSectionsFromHeader(info.header, info.size, layout)
467
+ : null;
468
+ if (!sections) {
469
+ return false;
470
+ }
471
+ await (0, promises_1.copyFile)(src, dst);
472
+ try {
473
+ const handle = await (0, promises_1.open)(dst, 'r+');
474
+ try {
475
+ for (const s of sections) {
476
+ if (!s.desc.deltaFields?.length || s.size === 0)
477
+ continue;
478
+ const chunk = Buffer.alloc(s.size);
479
+ await readExact(handle, chunk, s.start);
480
+ applyDelta(chunk, { start: 0, size: s.size, desc: s.desc }, inverse);
481
+ await writeExact(handle, chunk, s.start);
482
+ }
483
+ }
484
+ finally {
485
+ await handle.close();
486
+ }
487
+ }
488
+ catch (error) {
489
+ await (0, promises_1.rm)(dst, { force: true });
490
+ throw error;
491
+ }
492
+ return true;
493
+ }
494
+ exports.HBC_TRANSFORM_VERSION = 1;
495
+ /**
496
+ * 若 old/new 均为同版本且存在两侧都通过结构校验的候选布局,返回变换结果、
497
+ * 选中布局与写入 __diff.json 的元数据;否则返回 null。
498
+ */
499
+ function tryTransformPair(oldBuf, newBuf) {
500
+ const oldVersion = getHbcVersion(oldBuf);
501
+ const newVersion = getHbcVersion(newBuf);
502
+ if (oldVersion === null || newVersion === null || oldVersion !== newVersion) {
503
+ return null;
504
+ }
505
+ for (const layout of findLayouts(newVersion)) {
506
+ const tOld = transformHbcWithLayout(oldBuf, layout, false);
507
+ const tNew = transformHbcWithLayout(newBuf, layout, false);
508
+ if (!tOld || !tNew)
509
+ continue;
510
+ return {
511
+ tOld,
512
+ tNew,
513
+ layout,
514
+ meta: {
515
+ v: exports.HBC_TRANSFORM_VERSION,
516
+ hbcVersion: newVersion,
517
+ layout: compileLayoutToWire(layout),
518
+ },
519
+ };
520
+ }
521
+ return null;
522
+ }
523
+ /**
524
+ * tryTransformPair 的文件版:只读两侧头部即可选定布局,变换结果直接写到
525
+ * tOldPath / tNewPath,全程不把 bundle 读进内存。无可用布局时返回 null
526
+ * (不产生输出文件)。
527
+ */
528
+ async function tryTransformPairFiles(oldPath, newPath, tOldPath, tNewPath) {
529
+ const [oldInfo, newInfo] = await Promise.all([
530
+ readHbcFileHeader(oldPath),
531
+ readHbcFileHeader(newPath),
532
+ ]);
533
+ if (!oldInfo || !newInfo)
534
+ return null;
535
+ const oldVersion = getHbcVersion(oldInfo.header);
536
+ const newVersion = getHbcVersion(newInfo.header);
537
+ if (oldVersion === null || newVersion === null || oldVersion !== newVersion) {
538
+ return null;
539
+ }
540
+ for (const layout of findLayouts(newVersion)) {
541
+ if (!resolveSectionsFromHeader(oldInfo.header, oldInfo.size, layout) ||
542
+ !resolveSectionsFromHeader(newInfo.header, newInfo.size, layout)) {
543
+ continue;
544
+ }
545
+ const [tOld, tNew] = await Promise.all([
546
+ transformHbcFile(oldPath, tOldPath, layout, false),
547
+ transformHbcFile(newPath, tNewPath, layout, false),
548
+ ]);
549
+ if (!tOld || !tNew)
550
+ continue;
551
+ return {
552
+ layout,
553
+ meta: {
554
+ v: exports.HBC_TRANSFORM_VERSION,
555
+ hbcVersion: newVersion,
556
+ layout: compileLayoutToWire(layout),
557
+ },
558
+ };
559
+ }
560
+ return null;
561
+ }