Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ jobs:
npm install --global --prefix "$prefix" --ignore-scripts "$tarball"
"$prefix/bin/agent-device" --version
"$prefix/bin/agent-device" help
"$prefix/bin/agent-device" devices --json
"$prefix/bin/agent-device" doctor --remote --json

fallow:
Expand Down
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@
"package:android-ime-helper:npm": "rm -rf android/ime-helper/dist && AGENT_DEVICE_ANDROID_HELPER=ime sh ./scripts/package-android-helper.sh $(node -p \"require('./package.json').version\") android/ime-helper/dist",
"build:macos-helper": "swift build -c release --package-path apple/macos-helper",
"build:macos-helper:clean": "swift package --package-path apple/macos-helper clean && pnpm build:macos-helper",
"package:npm": "pnpm build && pnpm build:xcuitest:ios && pnpm build:xcuitest:macos && pnpm build:xcuitest:tvos && pnpm build:xcuitest:visionos && pnpm build:macos-helper:clean && pnpm package:apple-runner:npm && pnpm build:android",
"package:npm": "pnpm build && pnpm check:bundle-dependencies && pnpm build:xcuitest:ios && pnpm build:xcuitest:macos && pnpm build:xcuitest:tvos && pnpm build:xcuitest:visionos && pnpm build:macos-helper:clean && pnpm package:apple-runner:npm && pnpm build:android",
"ad": "node bin/agent-device.mjs",
"bench:help-conformance": "node scripts/help-conformance-bench.mjs",
"maestro:conformance": "node --experimental-strip-types --test packages/maestro/test/conformance/verify.test.ts packages/maestro/test/conformance/differential/run.test.ts packages/maestro/test/conformance/differential/invariants.test.ts",
Expand Down Expand Up @@ -134,14 +134,15 @@
"depgraph:test": "node --experimental-strip-types --test scripts/depgraph/model.test.ts scripts/depgraph/affected.test.ts",
"check:production-exports": "fallow dead-code --config fallow-production-exports.json --production --unused-exports --fail-on-issues",
"check:bundle-owner-files": "node --experimental-strip-types scripts/check-bundle-owner-files.ts",
"check:bundle-dependencies": "node --experimental-strip-types scripts/check-bundle-dependencies.ts",
"check:command-docs": "vitest run --project unit-core src/__tests__/command-doc-coverage.test.ts",
"check:replay-compat": "node --experimental-strip-types scripts/check-replay-compat-provenance.ts",
"check:freerange": "fr",
"check:quick": "pnpm lint && pnpm typecheck",
"sync:mcp-metadata": "node scripts/sync-mcp-metadata.mjs",
"check:mcp-metadata": "node scripts/sync-mcp-metadata.mjs --check",
"version": "pnpm sync:mcp-metadata && git add server.json",
"check:tooling": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm check:layering && pnpm depgraph:test && pnpm check:production-exports && pnpm check:mcp-metadata && pnpm build && pnpm check:bundle-owner-files",
"check:tooling": "pnpm format:check && pnpm lint && pnpm typecheck && pnpm check:layering && pnpm depgraph:test && pnpm check:production-exports && pnpm check:mcp-metadata && pnpm build && pnpm check:bundle-owner-files && pnpm check:bundle-dependencies",
"check:unit": "pnpm check:contention-retry && pnpm test:unit && pnpm test:smoke",
"check": "pnpm check:tooling && pnpm check:fallow && pnpm check:unit",
"prepack": "pnpm check:mcp-metadata && pnpm package:npm",
Expand Down
59 changes: 59 additions & 0 deletions scripts/check-bundle-dependencies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import fs from 'node:fs';
import path from 'node:path';
import { parseSync } from 'oxc-parser';
import { walkFiles } from './lib/walk-files.ts';

const repoRoot = path.resolve(import.meta.dirname, '..');
const distRoot = path.join(repoRoot, 'dist', 'src');

function moduleSpecifiers(file: string, source: string): string[] {
const record = parseSync(file, source).module;
return [
...record.staticImports.map((entry) => entry.moduleRequest.value),
...record.staticExports.flatMap((entry) =>
entry.entries.flatMap((exported) => moduleRequestValue(exported.moduleRequest)),
),
...record.dynamicImports.flatMap((entry) =>
dynamicModuleRequestValue(source, entry.moduleRequest),
),
];
}

function moduleRequestValue(request: { value?: string } | undefined): string[] {
return request?.value ? [request.value] : [];
}

function dynamicModuleRequestValue(
source: string,
request: { start: number; end: number },
): string[] {
const raw = source.slice(request.start, request.end);
const literal = /^(['"])([^'"]*)\1$/.exec(raw);
return literal?.[2] ? [literal[2]] : [];
}

const bundleFiles = walkFiles(distRoot).filter(
(file) => file.endsWith('.js') || file.endsWith('.d.ts'),
);
if (bundleFiles.length === 0) {
throw new Error('No dist/src JavaScript files found. Run `pnpm build` first.');
}

const leaks = bundleFiles.flatMap((file) => {
const source = fs.readFileSync(file, 'utf8');
return moduleSpecifiers(file, source)
.filter((specifier) => specifier.startsWith('@agent-device/'))
.map((specifier) => ({ file: path.relative(repoRoot, file), specifier }));
});

if (leaks.length > 0) {
const details = leaks.map(({ file, specifier }) => `- ${specifier} in ${file}`).join('\n');
throw new Error(
`Private workspace dependencies escaped the production bundle:\n${details}\n` +
'Published installs cannot resolve private @agent-device packages.',
);
}

process.stdout.write(
`Verified ${bundleFiles.length} production module files contain no private workspace imports.\n`,
);
1 change: 1 addition & 0 deletions src/__tests__/npm-package-scripts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ test('Fallow exposes one changed-code gate and an explicit full-tree audit', ()
test('the npm package build covers every package-owned build output', () => {
assert.deepEqual(script('package:npm').split(' && '), [
'pnpm build',
'pnpm check:bundle-dependencies',
'pnpm build:xcuitest:ios',
'pnpm build:xcuitest:macos',
'pnpm build:xcuitest:tvos',
Expand Down
4 changes: 3 additions & 1 deletion tsdown.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ export default defineConfig({
'internal/png-worker': 'src/utils/png-worker.ts',
'internal/update-check-entry': 'src/utils/update-check-entry.ts',
},
noExternal: [/^@agent-device\//, 'pngjs'],
deps: {
alwaysBundle: [/^@agent-device\//, 'pngjs'],
},
format: 'esm',
platform: 'node',
target: 'es2022',
Expand Down
Loading