feat!: convert SDK to TypeScript with a dual ESM/CJS build - #243
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: d3baddf 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; |
658f886
into
claude/sdk-cleanup-dead-code
* chore: remove dead code and reclassify test-only dependencies Delete unused internal modules that were unreachable from the public API: - lib/core/labs.js + lib/core/labs/index.js (legacy `proxy-cage` helper) - lib/utils/dataHelper.js (imported `big.js`, which was never a declared dependency, and was referenced nowhere) - lib/utils/environment.js (legacy Function env-var extraction, unused) Move `crc-32` and `uuid` to devDependencies since they are only used by the test suite, not by lib/. Remove the leftover EV_CAGE_RUN_URL reference from the config test (the env var no longer exists in config). Test baseline unchanged: 204 passing (the 5 pre-existing proxy.test.js failures are environmental and unrelated to this change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5 * feat!: convert SDK to TypeScript with a dual ESM/CJS build (#243) * feat!: convert SDK to TypeScript with a dual ESM/CJS build 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 * fix: don't clobber global https.request from non-relay clients; inject 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 * test: run the suite against compiled CJS under plain Node instead of tsx 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 * fix: stop mocharc spec from pulling the unit suite into the e2e run 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 * feat: infer real types across the SDK, replacing any at the public API 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> * fix: raise e2e mocha timeout to 30s to absorb httpbin latency 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> * Format and fix * Rename lint to format --------- Co-authored-by: Claude <noreply@anthropic.com> * Remove uuid dependency and cleanup changelog --------- Co-authored-by: Claude <noreply@anthropic.com>
* chore: remove dead code and reclassify test-only dependencies Delete unused internal modules that were unreachable from the public API: - lib/core/labs.js + lib/core/labs/index.js (legacy `proxy-cage` helper) - lib/utils/dataHelper.js (imported `big.js`, which was never a declared dependency, and was referenced nowhere) - lib/utils/environment.js (legacy Function env-var extraction, unused) Move `crc-32` and `uuid` to devDependencies since they are only used by the test suite, not by lib/. Remove the leftover EV_CAGE_RUN_URL reference from the config test (the env var no longer exists in config). Test baseline unchanged: 204 passing (the 5 pre-existing proxy.test.js failures are environmental and unrelated to this change). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011HwoTRLdV2YMub47j88mv5 * feat!: convert SDK to TypeScript with a dual ESM/CJS build (#243) * feat!: convert SDK to TypeScript with a dual ESM/CJS build 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 * fix: don't clobber global https.request from non-relay clients; inject 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 * test: run the suite against compiled CJS under plain Node instead of tsx 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 * fix: stop mocharc spec from pulling the unit suite into the e2e run 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 * feat: infer real types across the SDK, replacing any at the public API 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> * fix: raise e2e mocha timeout to 30s to absorb httpbin latency 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> * Format and fix * Rename lint to format --------- Co-authored-by: Claude <noreply@anthropic.com> * Remove uuid dependency and cleanup changelog --------- Co-authored-by: Claude <noreply@anthropic.com>
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