build works now
Build & Release binary / build (push) Successful in 26m20s

This commit is contained in:
2026-08-22 16:14:56 +05:00
parent 72e07f6a01
commit 6768b0a5b2
14 changed files with 506 additions and 22 deletions
@@ -0,0 +1,33 @@
Bun's bundler mis-compiles `import * as tmp from 'tmp'`.
tmp's entrypoint ends with `Object.defineProperty(module.exports, 'tmpdir', {...})`.
When bun builds an ESM namespace object for that CJS module it emits a reference
to `exports_tmp` without ever declaring the binding, so the compiled binary dies
on startup with `ReferenceError: exports_tmp is not defined`.
Importing it as CJS instead keeps bun on its `__commonJS` path, which handles the
`defineProperty` pattern correctly. Only the `tmp.file` value and the `Options`
type are used here, so the switch is behaviour-preserving.
diff --git a/src/util/fs.ts b/src/util/fs.ts
index 21c99ef..0d2224f 100644
--- a/src/util/fs.ts
+++ b/src/util/fs.ts
@@ -1,6 +1,7 @@
import * as fs from 'fs';
import * as path from 'path';
-import * as tmp from 'tmp';
+import type { Options as TmpOptions } from 'tmp';
+const tmp: typeof import('tmp') = require('tmp');
import { lookpath } from 'lookpath';
import { isErrorLike } from '@httptoolkit/util';
@@ -62,7 +63,7 @@ export const resolveCommandPath = (path: string): Promise<string | undefined> =>
export const commandExists = (path: string): Promise<boolean> =>
resolveCommandPath(path).then((result) => result !== undefined);
-export const createTmp = (options: tmp.Options = {}) => new Promise<{
+export const createTmp = (options: TmpOptions = {}) => new Promise<{
path: string,
fd: number,
cleanupCallback: () => void
@@ -0,0 +1,33 @@
Pin the CJS default-import interop for adbkit, which bun and tsc disagree on.
adbkit is a tsc-compiled CJS package: it sets `__esModule` and puts its real
default export at `exports.default`. Upstream builds with tsc + esModuleInterop,
where `import adb from '@devicefarmer/adbkit'` therefore resolves to
`exports.default` - the Adb class.
Bun instead applies native ESM/Node semantics, where the default of a CJS module
is `module.exports` *itself*. It emits `__toESM(require(...), 1)` and, because
that `isNodeMode` flag is set, unconditionally does `default = mod` regardless of
`__esModule`. So `adb` ends up as the whole exports object and the Adb class is
one level deeper, giving `Adb.default.createClient is not a function` at startup
(AndroidAdbInterceptor is constructed eagerly by buildInterceptors, so this kills
the whole server, not just Android interception).
Requiring the module explicitly and reaching for `.default` ourselves makes the
interop unambiguous under either toolchain. `Adb` is only ever used in type
positions here, so it becomes a type-only import.
diff --git a/src/interceptors/android/adb-commands.ts b/src/interceptors/android/adb-commands.ts
index 26307ce..2f74c4d 100644
--- a/src/interceptors/android/adb-commands.ts
+++ b/src/interceptors/android/adb-commands.ts
@@ -1,7 +1,8 @@
import * as stream from 'stream';
import * as path from 'path';
-import adb, * as Adb from '@devicefarmer/adbkit';
+import type * as Adb from '@devicefarmer/adbkit';
+const adb = (require('@devicefarmer/adbkit') as typeof import('@devicefarmer/adbkit')).default;
import { delay, isErrorLike } from '@httptoolkit/util';
import { logError } from '../../error-tracking';
@@ -0,0 +1,24 @@
diff --git a/src/index.ts b/src/index.ts
index a13dcab..f2f3942 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -15,7 +15,18 @@ import {
MockRTCAdminPlugin
} from 'mockrtc';
-import updateCommand from '@oclif/plugin-update/lib/commands/update';
+// Self-update is meaningless in a single self-contained binary: oclif's updater
+// rewrites files inside an oclif install directory that doesn't exist here. Worse,
+// importing it drags @oclif/plugin-update into the bundle, and that reads
+// @oclif/command/package.json from disk when it loads - a path that only exists on
+// the build machine, so the compiled binary dies at startup everywhere else.
+// Back off for 6 hours like the EEXIT branch below, so the UI stops re-asking.
+const updateCommand = {
+ run: (_channel: string[]): Promise<void> => {
+ console.log('Self-update is not supported in this build - download a new binary to update.');
+ return delay(1000 * 60 * 60 * 6, { unref: true });
+ }
+};
import { HttpToolkitServerApi } from './api/api-server';
import { checkBrowserConfig } from './browsers';