Skip to content

chore(runtime): remove @lifeomic/alpha in favor of plain axios - #1209

Open
Gonzalo-Avalos-Ribas wants to merge 2 commits into
mainfrom
chore/remove-lifeomic-alpha
Open

chore(runtime): remove @lifeomic/alpha in favor of plain axios#1209
Gonzalo-Avalos-Ribas wants to merge 2 commits into
mainfrom
chore/remove-lifeomic-alpha

Conversation

@Gonzalo-Avalos-Ribas

Copy link
Copy Markdown
Contributor

Removes @lifeomic/alpha from the SDK. createApiClient now returns a plain axios instance, with alpha's retry behavior reimplemented in packages/integration-sdk-runtime/src/api/retry.ts.

Why

Alpha is a Lambda-aware HTTP client whose purpose is resolving lambda:// URLs via SigV4. The SDK never uses that. createApiClient is only ever pointed at JUPITERONE_PROD_API_BASE_URL or JUPITERONE_DEV_API_BASE_URL; there is no lambda:// URL anywhere in this repo. Everything the SDK actually used from alpha — an axios-compatible client, the retry option, and interceptors — is either plain axios or ~130 lines of retry logic.

What alpha did bring was a large, stale dependency subtree: 28 @aws-sdk and 45 @smithy packages, plus a pin to axios@0.27.2.

Bumping alpha to 7.1.0 was tried first and rejected. It fails to compile — 8x TS2416 inside alpha's own shipped .d.ts, because alpha 7.1.0 predates axios's two-generic AxiosRequestConfig<D, P>:

node_modules/@lifeomic/alpha/src/alpha.d.ts:9:5 - error TS2416: Property 'request' in type 'Alpha' is not assignable to the same property in base type 'Axios'.

Pinning axios back to 1.12.2 makes it compile but reintroduces 28 advisories. Alpha 7.1.0 also drags in aws-xray-sdk-core -> cls-hooked, which enables async_hooks process-wide and breaks 3 FileSystemGraphObjectStore tests. Downgrading to 5.1.3 does not work either: it declares axios as a peerDependency (0.24.x || ... || 0.27.x), which npm 7+ auto-installs, so the old copy survives and cannot accept axios 1.x.

What changed

  • packages/integration-sdk-runtime/package.json — drop @lifeomic/alpha, add axios ^1.20.0
  • src/api/index.tsaxios.create() instead of new Alpha(); ApiClient is now AxiosInstance
  • src/api/retry.tsnew, alpha's retry reimplemented
  • src/synchronization/index.ts, packages/cli/src/import/importAssetsFromCsv.ts — type-only updates
  • packages/cli/package.json — axios devDependency for typecheck

Retry semantics are unchanged

The replacement was written against alpha 5.2.0's original TypeScript (recovered from its published sourcemap), not approximated. Same defaults (3 retries -> 4 attempts, factor 2, 10s cap), same min(factor^n * random()*1000 * (1 - random()%0.3), maxTimeout) backoff, same retry condition. axios-retry was deliberately not used because its backoff differs.

Two bugs fixed along the way

Interceptor ordering. Retry must register before the redaction interceptor. Redaction overwrites error.config.headers = '[REDACTED]' and retry replays that config, so the reverse order makes retried requests silently drop their Authorization header. Alpha got this ordering for free inside its constructor. Covered by a test that fails when the order is swapped.

Double compression on retry (ef84156). A retry replays the same config through the full interceptor chain, so compressRequest saw config.data already holding the gzip buffer from the previous attempt and compressed it again — sending a doubly-gzipped body under a single Content-Encoding: gzip. Verified against a live server:

before:  gzipData call#1 -> req1 gunzipOK=true
         gzipData call#2 -> req2 gunzipOK=false     <- corrupt
after:   gzipData call#1 -> req1 gunzipOK=true
                            req2 gunzipOK=true

This predates the branch (alpha replayed config the same way) but is fixed here. It fires on exactly the case retry exists to serve — a 5xx from the persister under load — and fails silently client-side.

A latent phantom dependency

axios was imported in 10 files but declared in no manifest, resolving only through alpha's hoisted copy. Removing alpha would have broken the build without declaring it.

Compatibility

ApiClient is now AxiosInstance. Since Alpha extended Axios, callers using .get/.post/.request are unaffected.

alphaOptions is retained and still honored, marked @deprecated, with axiosOptions added alongside. AlphaOptions was never re-exported, so it was not public API.

compressRequest is exported and its signature changes from AlphaInterceptor to InternalAxiosRequestConfig.

Checked against JupiterOne/integrations: zero alphaOptions callers, and the single real consumer of the SDK's createApiClient (deployments/snyk/src/ecs/microIngestion/microSync.ts:266) passes only account and apiBaseUrl and calls .post(). No alpha-specific keys (lambda, context, signAwsV4) are used anywhere.

This warrants a major bump (17.6.1 -> 18.0.0) since ApiClient and compressRequest are exported.

Impact

Prunes 94 production-reachable packages (28 @aws-sdk, 45 @smithy) and clears 33 of this repo's open Dependabot alerts: 23 axios, 6 fast-xml-parser, 2 form-data, 1 follow-redirects, 1 @smithy/config-resolver.

The 22 remaining axios alerts are all nx's bundled axios@1.6.8 — build tooling, devDependency only. The new axios@1.20.0 carries zero alerts.

Testing

  • npm run build — clean, with no skipLibCheck
  • npx jest73 suites, 750 passed, 1 skipped, 0 failed
  • eslint and prettier --check clean
  • gzip compression verified end-to-end against a real local HTTP server (content-encoding: gzip received, gunzipSync round-trips)
  • the double-compression regression test was negative-controlled: disabling the guard fails it, restoring it passes

Not verified: no downstream integration was executed against this build — only SDK packages typecheck against the new ApiClient. No authenticated calls to the real J1 API were made, so retry and compression are proven against a local server, not production.

Sequencing

This rewrites 7,269 lines of package-lock.json and will conflict hard with any other lockfile PR. Whichever lands second should discard its lockfile diff and re-run npm install rather than resolving the conflict by hand.

Gonzalo-Avalos-Ribas and others added 2 commits August 28, 2026 12:41
The SDK only ever pointed its API client at https api.us.jupiterone.io
or api.dev.jupiterone.io. Alpha's reason to exist, SigV4-signed
lambda:// invocation, was never used here, but it anchored axios 0.27.2
plus 94 transitive production packages (28 @aws-sdk, 45 @smithy).

createApiClient now returns a plain axios instance. Alpha's retry
behavior is reimplemented in api/retry.ts with identical semantics:
the same defaults (3 retries, factor 2, 10s cap), the same
exponential-backoff-with-jitter formula, and the same retry condition
(5xx or no response, never ECONNABORTED).

The retry interceptor is registered before the header-redaction
interceptor. Redaction overwrites error.config.headers, and retry
replays that config, so the reverse order would strip Authorization
from every retried request. Alpha got this ordering for free by
registering retry inside its own constructor.

axios was previously a phantom dependency: imported in 10 files but
declared nowhere, resolved only via alpha's hoisted copy. It is now a
real dependency of integration-sdk-runtime and a devDependency of cli.

Adds api/__tests__/retry.test.ts, covering retry counts, backoff
opt-out, connection-level failures, gzip upload compression and token
redaction against a real local HTTP server. Retry and compression
previously had no direct coverage.

BREAKING CHANGE: ApiClient is now AxiosInstance rather than Alpha. The
createApiClient alphaOptions parameter is deprecated in favor of
axiosOptions; both are still honored.
…tries

A retry replays the same config object through the full interceptor chain, so
compressRequest saw config.data already holding the gzip buffer from the
previous attempt and compressed it again. The retried request advertised a
single Content-Encoding: gzip for a doubly-compressed body, which the server
cannot decode -- silent corruption on exactly the path retry exists to serve.

Mark the config once compressed and skip on replay. Also:

- clone retry options before applying defaults, so a caller reusing one
  retryOptions object across clients does not leak state
- require isAxiosError on the retry condition, so a programming error thrown
  from a downstream interceptor surfaces immediately instead of being replayed
- document that axiosOptions takes precedence over the deprecated alphaOptions

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Gonzalo-Avalos-Ribas
Gonzalo-Avalos-Ribas requested a review from a team as a code owner August 28, 2026 17:29
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updatedform-data@​4.0.0 ⏵ 4.0.699100 +75100 +187100
Updatedaxios@​0.27.2 ⏵ 1.20.098 -1100 +75100 +194 +2100

View full report

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant