Skip to content

Support declaration-guided native compilation for JS packages with .d.ts sidecars #2569

Description

@andrewtdiz

Problem

Modern npm packages often publish JavaScript implementation files plus .d.ts declaration sidecars. Perry currently treats node_modules JavaScript implementation files as runtime-JS inputs unless the package is explicitly routed through compilePackages, but the compiler does not yet use the package's .d.ts metadata to type and lower those JS implementations as native Perry code.

This blocks native use of packages like @openai/codex-sdk, which publishes:

dist/index.js
dist/index.d.ts

TypeScript consumers retain API types from dist/index.d.ts, but Perry native codegen sees the executable entry as dist/index.js and routes it into the JS-runtime gate instead of a declaration-guided native package compile.

Context

The architecture Perry likely needs is a declaration-guided npm package compiler path:

  1. Resolve package exports to both implementation (.js) and type sidecar (.d.ts / exports.types / types).
  2. Use declarations as API/type metadata for JS exports, classes, functions, overloads, and object shapes.
  3. Lower the JS implementation through Perry HIR under the existing perry.compilePackages + perry.allow.compilePackages trust boundary.
  4. Route supported Node APIs to Perry-native stdlib shims and emit precise diagnostics for unsupported APIs/patterns.
  5. Prefer original TS sources when available via source maps or declaration maps.

For @openai/codex-sdk, this would let Perry compile the SDK wrapper natively instead of requiring a Node worker or runtime JS fallback. The SDK's core surface is small (Codex, Thread, startThread, resumeThread, run, runStreamed) and mostly wraps codex exec --experimental-json, so it is a good test case for this architecture.

Verification

Temp project:

mkdir /tmp/perry-codex-sdk-smoke
cd /tmp/perry-codex-sdk-smoke
npm init -y
npm install --save-exact @perryts/perry@0.5.1025 @openai/codex-sdk@0.135.0 typescript@6.0.3

With src/main.ts directly importing the SDK:

import { Codex } from "@openai/codex-sdk";

const codex = new Codex({ codexPathOverride: "./fake-codex.mjs" });
const thread = codex.startThread({ skipGitRepoCheck: true });
console.log(typeof thread.run);

Native compile fails at the package JS boundary:

npx perry compile src/main.ts -o dist/direct

Observed:

Collecting modules...
  JS module: @openai/codex-sdk -> .../node_modules/@openai/codex-sdk/dist/index.js
Error: build pulled in `perry-jsruntime` ... [@openai/codex-sdk]

Trying the runtime-JS path also fails with the published package on macOS arm64:

npx perry compile src/main.ts --enable-js-runtime -o dist/direct

Observed:

JavaScript modules found but libperry_jsruntime.a not found

A workaround was verified: compile a native Perry launcher that uses Perry's native child_process.spawnSync to run a tiny Node worker, and let that worker import @openai/codex-sdk. That proves the SDK API and protocol are usable, but it is not native Perry integration.

Useful open-source references

  • Google Closure Compiler: externs are the closest mature model for treating declarations as compiler-visible API contracts for JS.
  • tsickle: useful prior art for translating TypeScript type information into Closure-compatible metadata/externs.
  • Deno: useful reference for resolving JS modules with declaration sidecars in a TypeScript-aware runtime/toolchain.
  • Porffor: useful lower-level reference for ahead-of-time JS compilation tradeoffs, although it does not solve the .d.ts package sidecar architecture directly.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    rfcProposal that needs design agreement before implementation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions