Skip to content

chore(js-sdk,cli): modernize tsconfig and adopt TypeScript 7 (side-by-side) - #1536

Merged
mishushakov merged 3 commits into
mainfrom
mishushakov/bump-typescript-7
Jul 9, 2026
Merged

chore(js-sdk,cli): modernize tsconfig and adopt TypeScript 7 (side-by-side)#1536
mishushakov merged 3 commits into
mainfrom
mishushakov/bump-typescript-7

Conversation

@mishushakov

@mishushakov mishushakov commented Jul 9, 2026

Copy link
Copy Markdown
Member

Supersedes #1516 (same modernization at TypeScript 6.0). Rebased onto main now that the build runs on tsdown (#1515).

What & why

Adopt TypeScript 7 for both packages and modernize the compiler config.

TypeScript 7.0's native compiler ships no programmatic API yet (it lands in 7.1), so anything built on the TS compiler API breaks on it — here that's tsdown's .d.ts generation and the codegen scripts (openapi-typescript, json-schema-to-typescript). Per the official guidance, TS 7 is installed side-by-side with TS 6:

"@typescript/native": "npm:typescript@^7.0.2",      // native tsc — used for type-checking
"typescript": "npm:@typescript/typescript6@^6.0.2"  // TS6 w/ compiler API — used by tooling
  • tsc --noEmit (typecheck) → native TypeScript 7.0.2 (verified: tsc --version → 7.0.2)
  • import 'typescript'TypeScript 6.0 with the compiler API → tsdown dts + codegen keep working
  • Bonus: tsdown's dts no longer prints the "TypeScript 7.0 does not yet have a stable API and is experimental" warning (it's on the 6.0 API now)

Internal build-config change only — no public API or runtime behavior changes.

Compiler options: before → after

packages/js-sdk/tsconfig.json

option before after
target es6 es2022
lib ["dom","ESNext"] ["dom","es2022"]
module (unset) esnext
moduleResolution node bundler
allowJs true removed (no .js sources)
allowSyntheticDefaultImports true removed (implied by esModuleInterop)
useDefineForClassFields (false, implied by es6) false (now explicit) — see note

packages/cli/tsconfig.json

option before after
moduleResolution node bundler
strictNullChecks, strictFunctionTypes, strictBindCallApply, strictPropertyInitialization, noImplicitThis, alwaysStrict true removed (implied by strict)
downlevelIteration true removed (removed in TS 7; no-op at es2022)
baseUrl "." removed (removed in TS 7)
paths { e2b } { src, "src/*", e2b } (replaces baseUrl for the existing src/... import style)
outDir "dist" removed (unused under tsc --noEmit)
exclude (none) ["dist","node_modules"] (so the built bundle is never type-checked)

target/lib for the CLI were already es2022.

Notes / decisions

  • Why side-by-side, not a plain typescript@7 bump: TS 7.0 is the native (Go) compiler rewrite — feature-identical to 6.0 for type-checking, no programmatic API until 7.1. A plain bump crashed both codegen tools (Cannot read properties of undefined (reading 'createKeywordTypeNode')). Side-by-side gives native-TS-7 checking while keeping the TS-6 API for tooling. Once 7.1 ships the API and the tools update, this collapses back to a single typescript@7 dep.
  • useDefineForClassFields: false is pinned explicitly. Raising js-sdk's target to es2022 flips this default to true, changing class-field emit and shifting stack frames. The template builder resolves the caller's directory and per-step traces via fixed-depth stack walking (getCallerDirectory in src/template/index.ts), so the extra frames threw it off by one — resolving .copy('folder/*', …) against the wrong base dir and mis-attributing build steps (tests/template/build.test.ts + stacktrace.test.ts). Pinning false keeps the exact pre-existing field semantics (es6 already implied false); adopting define semantics should be a separate, deliberately tested change.
  • Target stays at es2022, not es2023. engines still allow Node 20 (>=20.18.1 <21 || >=22).
  • moduleResolution: "bundler" typechecks + builds cleanly in both packages. The CLI's baseUrl-based bare imports (from 'src/user', from 'src') are preserved via paths; the bundled output still resolves them (build verified, binary smoke-tested).

Not done (intentionally)

  • verbatimModuleSyntax — ~177 import type conversions; left as a follow-up.
  • Shared tsconfig.base.json — the two configs diverge too much to factor out cleanly.

Verification

  • pnpm run typecheck ✅ both packages, on native TS 7.0.2
  • pnpm run build ✅ both packages (js-sdk ESM + CJS + DTS; cli CJS; binary smoke-tested)
  • codegen ✅ openapi-typescript + json2ts run and produce identical output (idempotent)
  • pnpm run lint ✅ both packages
  • pnpm run testtemplate/build + template/stacktrace now pass (stacktrace verified locally 30/30); remaining local failures are all E2B_API_KEY-gated live tests, unaffected by this change

🤖 Generated with Claude Code

@changeset-bot

changeset-bot Bot commented Jul 9, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 443f089

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@cla-bot cla-bot Bot added the cla-signed label Jul 9, 2026
@cursor

cursor Bot commented Jul 9, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Major toolchain and emit-semantics config changes touch js-sdk template stack-trace resolution; runtime API is unchanged but regressions would show up in build/typecheck and template tests.

Overview
packages/cli and packages/js-sdk now use native TypeScript 7 (@typescript/native) for tsc --noEmit while keeping @typescript/typescript6 for tooling that still needs the compiler API (tsdown, openapi-typescript, json-schema-to-typescript). Both packages switch moduleResolution to bundler; js-sdk raises target/lib to es2022, sets module to esnext, drops some legacy options, and explicitly sets useDefineForClassFields: false so template stack walking (getCallerDirectory) is unchanged. CLI drops TS7-removed options (baseUrl, redundant strict flags, downlevelIteration), adds paths for src imports, and excludes dist from typecheck. codegen.Dockerfile pins Node 22.18.0 instead of 20.19.5. Lockfile reflects the new TypeScript packages.

Reviewed by Cursor Bugbot for commit 443f089. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from 247c106. Download artifacts from this workflow run.

JS SDK (e2b@2.32.1-mishushakov-bump-typescript-7.0):

npm install ./e2b-2.32.1-mishushakov-bump-typescript-7.0.tgz

CLI (@e2b/cli@2.13.2-mishushakov-bump-typescript-7.0):

npm install ./e2b-cli-2.13.2-mishushakov-bump-typescript-7.0.tgz

Python SDK (e2b==2.31.0+mishushakov.bump.typescript.7):

pip install ./e2b-2.31.0+mishushakov.bump.typescript.7-py3-none-any.whl

@mishushakov
mishushakov force-pushed the mishushakov/bump-typescript-7 branch from 7af632c to a4aac95 Compare July 9, 2026 17:23
…-side)

Adopt TypeScript 7 for both packages. TypeScript 7.0's native compiler
ships no programmatic API yet (that lands in 7.1), so it is installed
side-by-side with TypeScript 6.0 per the official guidance:

- `@typescript/native` (npm:typescript@^7.0.2) provides the native `tsc`
  used for type-checking (`tsc --noEmit`)
- `typescript` resolves to `@typescript/typescript6@^6.0.2`, which keeps the
  compiler API available for tooling that needs it — tsdown's `.d.ts`
  generation and the codegen scripts (openapi-typescript, json2ts), which
  crash on TS 7.0's API-less native build

Now that tsdown owns the build emit and `tsc` is type-check only, the
compiler options are also modernized:

- js-sdk: target/lib `es6`->`es2022`, add `module: esnext`,
  `moduleResolution` `node`->`bundler`; drop `allowJs` (no JS sources) and
  `allowSyntheticDefaultImports` (implied by `esModuleInterop`)
- js-sdk: pin `useDefineForClassFields: false`. `target: es2022` would
  default it to `true`, changing class-field emit and shifting stack frames,
  which breaks the template builder's fixed-depth caller resolution
  (getCallerDirectory / per-step stack traces) and fails the template
  build/stacktrace tests
- cli: `moduleResolution` `node`->`bundler`; drop strict-implied flags
  (strictNullChecks, strictFunctionTypes, strictBindCallApply,
  strictPropertyInitialization, noImplicitThis, alwaysStrict); drop
  `downlevelIteration` and `baseUrl` (both removed in TS 7), replacing
  `baseUrl` with explicit `paths` for the existing `src/...` import style
- cli: drop `outDir` (unused under `tsc --noEmit`) and add an explicit
  `exclude` for `dist`/`node_modules` so the built bundle is never
  type-checked

Target stays at es2022 (engines still allow Node 20). No public API or
runtime change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mishushakov
mishushakov force-pushed the mishushakov/bump-typescript-7 branch from a4aac95 to bfa0af5 Compare July 9, 2026 17:58
@mishushakov mishushakov changed the title chore(js-sdk,cli): modernize tsconfig and bump TypeScript to 7.0 chore(js-sdk,cli): modernize tsconfig and adopt TypeScript 7 (side-by-side) Jul 9, 2026
mishushakov and others added 2 commits July 9, 2026 20:22
The codegen image was left at Node 20.19.5 while `.tool-versions` moved to
22.18.0, so the "pinned to match .tool-versions" comment was stale. Sync it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This PR only touches dev/build tooling (tsconfig, TypeScript devDependencies,
codegen Dockerfile) — nothing in the published package (dist/README/package.json
runtime surface) changes, so no version bump/release is warranted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mishushakov
mishushakov enabled auto-merge (squash) July 9, 2026 18:33
@mishushakov
mishushakov merged commit 0bd06d8 into main Jul 9, 2026
29 of 30 checks passed
@mishushakov
mishushakov deleted the mishushakov/bump-typescript-7 branch July 9, 2026 18:35
mishushakov added a commit that referenced this pull request Jul 14, 2026
…solution emit-invariant (#1539)

Follow-up to #1536, which pinned `useDefineForClassFields: false` in the
js-sdk tsconfig because raising `target` to `es2022` flips the default
to `true`, and that broke the template builder. This PR fixes the root
cause and removes the pin, so the SDK now compiles with the standard
es2022 `[[Define]]` class-field semantics.

## Root cause

`TemplateBase` resolved its default `fileContextPath` in a **class field
initializer**:

```ts
private fileContextPath: PathLike =
  runtime === 'browser' ? '.' : (getCallerDirectory(STACK_TRACE_DEPTH) ?? '.')
```

With native class fields (define semantics), V8 evaluates field
initializers in an extra `<instance_members_initializer>` stack frame:

```
at getCallerDirectory (utils.ts)
at <instance_members_initializer> (index.ts)   ← extra frame under define semantics
at new TemplateBase (index.ts)
at Template (index.ts)
at user code                                    ← fixed-depth walk lands one frame short
```

`getCallerDirectory` walks the stack at a fixed depth, so it landed on
the SDK's own `src/template` directory instead of the caller's —
`.copy('folder/*', …)` then globbed against the wrong base dir (`Error:
No files found in .../src/template/...`), and the resulting client-side
failure mis-attributed build-step stack traces (the two
`stacktrace.test.ts` failures were cascades of this one bug).

## Fix

Move the default resolution into the constructor body, where the stack
shape is identical under both emits:

```ts
constructor(options?: TemplateOptions) {
  this.fileContextPath =
    options?.fileContextPath ??
    (runtime === 'browser' ? '.' : (getCallerDirectory(STACK_TRACE_DEPTH) ?? '.'))
```

The call is now emit-invariant (same `STACK_TRACE_DEPTH`), so the
tsconfig pin is removed. The method-level `getCallerFrame` call sites
were never affected — method bodies don't change shape with class-field
semantics.

Only the js-sdk is touched: the Python SDKs resolve the caller via
`inspect` and don't have this failure mode, and the CLI bundle doesn't
include `TemplateBase`.

## Usage example

Fixes relative-path resolution for SDK consumers whose toolchain emits
native class fields (e.g. esbuild/vitest with `target: es2022+`):

```ts
// user-project/scripts/template.ts
const template = Template()
  .fromBaseImage()
  .copy('assets/*', '/app/assets') // now resolves against user-project/scripts/,
                                   // not the SDK's own directory
```

## Verification

- `tests/template/stacktrace.test.ts` — 30/30 pass with the flag
defaulted (`true`), and still 30/30 when explicitly set back to `false`
(emit-invariance)
- `tests/template/build.test.ts` — 4/4 pass against the real backend
(real `.copy` glob + build)
- Smoke-tested built `dist/index.mjs` and `dist/index.js` from an
external directory: `fileContextPath` resolves to the importing script's
directory in both
- Unit project A/B: identical results with and without this change
(remaining failures are pre-existing `E2B_API_KEY`-gated live tests)
- `pnpm run typecheck`, `lint`, `format` ✅

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants