Skip to content

Commit 3ac05b5

Browse files
sunnylqmclaude
andcommitted
feat(publish): report bytecodeVersion 0 for plain JS bundles and for native packages
The server could not tell "plain JS" from "not reported" (both NULL), so a plain JS version stayed the "unknown epoch" hermes base candidate forever and every Hermes build downloaded and probed it again. - version publish always sends bytecodeVersion: the HBC version, or 0 for a plain JS bundle (no longer the base's version as a fallback) - package/create sends the embedded bundle's bytecodeVersion the same way - a server base reported as 0 is logged as "plain JS" Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 7e6ef3e commit 3ac05b5

6 files changed

Lines changed: 48 additions & 10 deletions

File tree

‎src/package.ts‎

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,11 @@ import {
1717
import { updateJson } from './utils/constants';
1818
import { getDepVersions } from './utils/dep-versions';
1919
import { getCommitInfo } from './utils/git';
20-
import { bundleEntryMatcher, cachePut } from './utils/hermes-base';
20+
import {
21+
bundleEntryMatcher,
22+
cachePut,
23+
reportedBytecodeVersion,
24+
} from './utils/hermes-base';
2125
import { t } from './utils/i18n';
2226
import { getStringListOption } from './utils/options';
2327
import { bundleLocationFields, locateZipEntry } from './utils/zip-range';
@@ -196,6 +200,11 @@ async function uploadNativePackage(
196200
// against the client-reported bundleHash to decide pdiff applicability.
197201
// Old servers strip unknown fields, so this is forward-compatible.
198202
...(bundleHash ? { bundleHash } : {}),
203+
// HBC version of the embedded bundle (0 = plain JS), so the server
204+
// offers this package as hermes base only to a matching compile
205+
...(bundleFile
206+
? { bytecodeVersion: reportedBytecodeVersion(bundleFile) }
207+
: {}),
199208
...bundleLocation,
200209
deps: getDepVersions(),
201210
commit: await getCommitInfo(),

‎src/utils/hermes-base.ts‎

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,29 @@ export class HbcVersionMismatchError extends PermanentBaseError {
6363
readonly version: number | null,
6464
readonly expected: number,
6565
) {
66-
super(`downloaded base is HBC ${version ?? 'n/a'}, need ${expected}`);
66+
super(
67+
`downloaded base is ${version == null ? 'plain JS' : `HBC ${version}`}, need ${expected}`,
68+
);
6769
}
6870
}
6971

72+
/**
73+
* `bytecodeVersion` reported for a bundle that is not Hermes bytecode, so the
74+
* server can tell "plain JS" from "not reported" (NULL) and never offers it
75+
* as a base.
76+
*/
77+
export const PLAIN_JS_BYTECODE_VERSION = 0;
78+
79+
/** the `bytecodeVersion` to report for `bundle` at publish / upload time */
80+
export function reportedBytecodeVersion(bundle: Buffer): number {
81+
return getHbcVersion(bundle) ?? PLAIN_JS_BYTECODE_VERSION;
82+
}
83+
84+
const describeBytecodeVersion = (version: number | null) =>
85+
version === PLAIN_JS_BYTECODE_VERSION
86+
? 'plain JS'
87+
: `HBC ${version ?? 'n/a'}`;
88+
7089
/** bytes `getHbcVersion` needs to read the version */
7190
const HBC_HEAD_BYTES = 128;
7291

@@ -905,7 +924,7 @@ export async function resolveHermesBase(
905924
) {
906925
log(
907926
t('hermesBaseNone', {
908-
reason: `server base is HBC ${record.bytecodeVersion}, need ${bytecodeVersion}`,
927+
reason: `server base is ${describeBytecodeVersion(record.bytecodeVersion)}, need ${bytecodeVersion}`,
909928
}),
910929
);
911930
return null;

‎src/versions.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import type { Package, Platform, Version } from './types';
1010
import { isNonInteractive, loadTtyTable, question } from './utils';
1111
import { getDepVersions } from './utils/dep-versions';
1212
import { getCommitInfo } from './utils/git';
13-
import { getHbcVersion } from './utils/hbcTransform';
1413
import {
1514
BUNDLE_ENTRY_NAMES,
1615
cachePut,
1716
extractBundleFromArchive,
1817
type HermesBaseMeta,
18+
reportedBytecodeVersion,
1919
sha256Hex,
2020
truncateHermesBaseDetail,
2121
} from './utils/hermes-base';
@@ -497,8 +497,8 @@ async function describePpkBundle(
497497
if (bundle) {
498498
const bundleHash = sha256Hex(bundle);
499499
meta.bundleHash = bundleHash;
500-
const hbcVersion = getHbcVersion(bundle) ?? base?.bytecodeVersion;
501-
if (hbcVersion != null) meta.bytecodeVersion = hbcVersion;
500+
// 0 for plain JS: the server then never offers it as a Hermes base
501+
meta.bytecodeVersion = reportedBytecodeVersion(bundle);
502502
await cachePut(bundle, undefined, bundleHash).catch(() => {});
503503
Object.assign(meta, bundleLocationFields(found?.location));
504504
}

‎tests/hermes-base.test.ts‎

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -842,11 +842,18 @@ describe('publish metadata never sends JSON null', () => {
842842
});
843843
expect(used.hermesBaseOutcome).toBe('used');
844844
expect('hermesBaseDetail' in used).toBe(false);
845-
// plain JS bundle: no bytecodeVersion at all rather than null
845+
// plain JS bundle: reported as 0, never null — even when compiled
846+
// against a base (the bundle, not the base, decides)
846847
const js = path.join(dir, 'js.ppk');
847848
await writeZip(js, { 'index.bundlejs': Buffer.from('var a = 1;') });
848849
const plain = await describePpkBundleForTests(js, undefined);
849-
expect('bytecodeVersion' in plain).toBe(false);
850+
expect(plain.bytecodeVersion).toBe(0);
851+
const plainWithBase = await describePpkBundleForTests(js, {
852+
bytecodeVersion: 98,
853+
baseVersionId: null,
854+
baseHash: null,
855+
});
856+
expect(plainWithBase.bytecodeVersion).toBe(0);
850857
} finally {
851858
fs.removeSync(dir);
852859
}

‎tests/package.test.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,8 @@ describe('packageCommands native upload', () => {
215215
infoSpy = spyOn(utils, 'getApkInfo').mockResolvedValue({
216216
versionName: '1.0.0',
217217
buildTime: 123,
218-
});
218+
bundleFile: Buffer.from('var a = 1;'),
219+
} as never);
219220
postSpy = spyOn(api, 'post').mockResolvedValue({ id: 9 });
220221
});
221222

@@ -269,6 +270,8 @@ describe('packageCommands native upload', () => {
269270
buildTime: '123',
270271
hash: 'slim-package-hash',
271272
name: '1.0.0',
273+
// the embedded bundle is plain JS
274+
bytecodeVersion: 0,
272275
}),
273276
);
274277
expect(fs.existsSync(uploadedPath)).toBe(false);

‎tests/zip-range.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ describe('zip-range', () => {
464464
98,
465465
).catch((e) => e);
466466
expect(error).toBeInstanceOf(HbcVersionMismatchError);
467-
expect(error.message).toBe('downloaded base is HBC n/a, need 98');
467+
expect(error.message).toBe('downloaded base is plain JS, need 98');
468468
expect(messages).toEqual([]);
469469
// tail + one header/head chunk, not the bundle body
470470
expect(server.log.length).toBeLessThanOrEqual(2);

0 commit comments

Comments
 (0)