feat!: convert SDK to TypeScript with a dual ESM/CJS build - #243
feat!: convert SDK to TypeScript with a dual ESM/CJS build#243matt-evervault wants to merge 6 commits into
Conversation
Rewrite the SDK source (lib/*.js -> lib/*.ts) in strict TypeScript,
preserving runtime behaviour and the on-the-wire encryption formats
exactly. Only module syntax and type annotations changed; the public
API is identical.
Build & packaging:
- Add tsconfig.json (strict) and build with tsup to dist/ as dual
ESM (index.mjs) + CommonJS (index.js) with bundled .d.ts.
- Point package "main"/"module"/"types"/"exports" at dist and ship
only dist; drop the tsc-based generate-types step.
- keepNames so error `type`/constructor names are preserved.
- require('@evervault/sdk') still returns the EvervaultClient class;
`import Evervault from '@evervault/sdk'` works for ESM consumers.
Types & internals:
- Fold the hand-written types.d.ts / domainTargets.d.ts into source
types; monkey-patched Node core modules use default imports so the
mutable module.exports is patched (works in both CJS and ESM).
Tests & CI:
- Run the existing mocha suite against the TS source via tsx and
replace rewire (incompatible with compiled TS) with proxyquire /
shared-singleton config mutation. 204 passing, unchanged from the
JS baseline (the 5 proxy.test.js failures are pre-existing and
environmental).
- Add typecheck + build steps to CI; bump CodeQL to v3 with the
javascript-typescript language.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5
🦋 Changeset detectedLatest commit: 169f0ed The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
…t config in sdk tests
CI ran the suite against the real network and surfaced two test-infra
regressions from the TS migration that the local sandbox hid:
- EvervaultClient's constructor called `_shouldOverloadHttpModule`, whose
else-branch unconditionally ran `https.request = originalRequest`. On every
non-relay client this reset the global `https.request`, removing nock's
interception (nock doesn't re-patch once "active"), which cascaded failures
across client/http test files. Guard the restore so it only runs when this
process actually overloaded `https.request` for Relay. This is also more
correct: a plain client no longer disables another client's outbound Relay.
- sdk.test.js pointed the client at its mock server by mutating the config
singleton. Under tsx the module-cache timing made that unreliable, so the
client hit the real API. Inject the mutated config into the client with
proxyquire (`{ './config': config }`), mirroring the old rewire `__set__`.
Local suite unchanged (204 passing; the 5 proxy.test.js failures are
environmental to this sandbox). Verified the guard preserves nock's patch and
that config injection reaches the client.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5
The nock-based tests passed locally but failed in CI only. Running the suite through tsx (on-the-fly TS transpilation with a custom module loader) interacted with nock/axios HTTP interception differently on the CI runners, so requests bypassed nock and hit the network. Compile lib/*.ts to CJS with `tsc -p tsconfig.build.json` and run mocha against the emitted JS under plain Node — the same execution model the JavaScript suite used before the TypeScript migration. The compiled lib/*.js are build artifacts (git- and prettier-ignored); tsup still builds the published dual-format bundle from the .ts sources, and `tsc --noEmit` still type-checks. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5
Mocha concatenates the `spec` from `.mocharc.json` with any CLI positional file arguments rather than letting the CLI override it. The `test:e2e` script (run by the `e2e.yml` workflow) invokes `mocha 'e2e/**/*.test.js'`, so once `.mocharc.json` declared `spec: tests/**`, that job silently ran the entire unit suite alongside the e2e tests. The e2e tests run first, call `enableOutboundRelay()` which monkey-patches the global `https.request`, and that leaves nock unable to intercept the unit tests — producing the CI-only failures. Keep only `timeout` in `.mocharc.json` and pass the unit spec explicitly on the CLI in `test` / `test:filter`, so each mocha invocation resolves exactly one suite (matching the pre-TypeScript setup). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5
Give consumers precise types instead of `any` on the client surface: - encrypt<T> returns EncryptedData<T>, preserving input shape (objects keep their keys with encrypted string leaves, Buffers stay Buffers, primitives become strings) and rejecting non-encryptable inputs - decrypt<T>, run<T> (-> FunctionRunResult<T>), createRunToken (-> RunToken) and createClientSideDecryptToken (-> ClientSideToken) now carry real types - hidden ECDH fields typed as Buffer / crypto.ECDH / NodeJS.Timeout Internally, export a reusable HttpClient type and thread it through attestationDoc/relayOutboundConfig/httpsHelper; type the PCR store, the attestation helpers, and the key/token/relay response shapes. Also make Http.getAppKey throw mapResponseCodeToError on non-2xx (mirroring getCageKey) instead of returning undefined and crashing downstream. Genuinely dynamic/vendored surfaces (crypto key material that is Buffer|string, the agent-base subclass, the asn1js DER encoder, the https.request/tls monkeypatch paths) are left untyped with rationale comments. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The two path-filtering outbound-relay tests each make two httpbin.org requests; the 5s timeout was too tight for slow-but-reachable httpbin, causing intermittent timeouts. Align with the .mocharc.json default (30s). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Automated reviewReviewed the full diff (2,256 additions / 1,187 deletions across 50 files — the TS conversion, tsup dual ESM/CJS build, and test conversions from Should-fix1. let httpsRequestOverloaded = false;
...
} else if (httpsRequestOverloaded) {
(https as any).request = originalRequest;
httpsRequestOverloaded = false;
}The JS baseline unconditionally reset 2. "exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.js"
}
}
Nits (undisclosed but likely-intentional behavior drift)3. 4. 5. Type-safety is uneven — the public surface ( 6. Checked, no issues found
Automated review — flag anything above that doesn't hold up. Generated by Claude Code |
| }); | ||
| if (response.status >= 200 && response.status < 300) { | ||
| const pollIntervalHeaderValue = response.headers['x-poll-interval']; | ||
| const pollInterval = parseFloat(String(pollIntervalHeaderValue)); |
There was a problem hiding this comment.
this is potentially changing behavior on corner cases
| console.error(`EVERVAULT :: An error occurred while polling (${e})`); | ||
| } | ||
| }, interval * 1000); | ||
| }, (interval as number) * 1000); |
There was a problem hiding this comment.
need to check if this is a number instead of coercing it to a number
| 'hex', | ||
| 'uncompressed' | ||
| ); | ||
| ) as string; |
Why
Modernize the SDK by converting it to TypeScript. This improves type-safety for consumers and maintainers and ships proper bundled type declarations, while keeping the public API and on-the-wire behaviour identical.
Stacked on top of
claude/sdk-cleanup-dead-code— review that PR first. This PR's diff is against the cleanup branch.How
lib/*.js→lib/*.tsin strict TypeScript, preserving runtime behaviour and the byte-exact encryption formats (only module syntax and type annotations changed).tsuptodist/as dual ESM (index.mjs) + CommonJS (index.js) with bundled.d.ts; repointedmain/module/types/exports.require('@evervault/sdk')still returns theEvervaultClientclass, andimport Evervault from '@evervault/sdk'works for ESM consumers (keepNamespreserveserror.type).https.request,tls.*) use default imports so the mutablemodule.exportsis patched — works in both the CJS and ESM builds.types.d.ts/domainTargets.d.tsinto source types.tsxand replacedrewire(incompatible with compiled TS) withproxyquire/ shared-singleton config mutation. 204 passing, unchanged from the JS baseline.typecheck+buildsteps; bumped CodeQL to v3 with thejavascript-typescriptlanguage.Released as a major (7.0) via changeset only because the package's internal file layout and
exportsmap changed.🤖 Generated with Claude Code
https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5
Generated by Claude Code