Skip to content

perf: cut SDK bundle size, import memory and call latency; add per-SDK benchmark dashboard - #873

Draft
Mkassabov wants to merge 127 commits into
mainfrom
perf/sdk-footprint
Draft

Mkassabov wants to merge 127 commits into
mainfrom
perf/sdk-footprint

Conversation

@Mkassabov

Copy link
Copy Markdown
Collaborator

Cuts the runtime footprint of every distilled SDK: bundle size, import memory, first call, and per-call latency, in normal and ResponseValidation.strict mode. It also adds the per-SDK benchmark (benches/sdks) and the dashboard that tracked every step (benches/dashboard).

Draft. 123 commits, each one benchmarked. The branch is 13 commits behind main. A merge conflicts only in generated service files (packages/{aws,cloudflare,fly-io,gcp,turso}/src/services/*); regenerate those packages after the merge to resolve them.

Results

Median per program. A program is 1 auth setup + 1 operation for one SDK and protocol; 98 programs over all SDKs. Base is main at 40a9500a7, head is f6019b376. Both are in benches/dashboard/runs/.

Metric main this PR change
Bundle size (minified, gzip -9) 57.1 KB 38.3 KB −33%
Bundle size, strict 57.1 KB 39.9 KB −30%
Added latency per call (p50) 19.5 µs 4.0 µs −79%
Added latency, strict 24.1 µs 5.3 µs −78%
Import heap 2.15 MB 0.57 MB −74%
Import heap, sum of all programs 318 MB 63 MB −80%
Live objects at import 6,046 850 −86%
First call (cold) 4.9 ms 3.3 ms −34%
First call, strict 5.6 ms 3.6 ms −35%

Import heap with every service of a provider imported:

Provider Services main this PR Import time main → PR
AWS 438 227 MB 13.9 MB 1.61 s → 0.14 s
GCP 318 111 MB 9.4 MB 0.75 s → 0.09 s
Azure 224 112 MB 8.0 MB 0.82 s → 0.07 s
Cloudflare 129 76 MB 3.2 MB 0.63 s → 0.05 s

Against the official SDKs (same call, official = import the one client package, distilled = import the whole provider):

Call Bundle gz Added latency First call Import heap
S3 ListBuckets 157.0 → 61.0 KB 140 → 19.4 µs 9.2 → 7.9 ms 4.43 → 1.05 MB
DynamoDB 144.8 → 53.8 KB 87 → 14.3 µs 8.0 → 6.0 ms 3.27 → 1.02 MB
KMS 134.1 → 53.2 KB 83 → 14.4 µs 7.8 → 5.9 ms 2.77 → 1.02 MB
STS 130.1 → 55.2 KB 93 → 16.1 µs 8.2 → 6.6 ms 2.26 → 0.98 MB
EC2 265.2 → 55.1 KB 97 → 16.1 µs 8.3 → 7.0 ms 12.21 → 1.51 MB
Lambda 141.3 → 55.0 KB 79 → 15.3 µs 8.1 → 6.6 ms 3.56 → 1.05 MB
Cloudflare workers.scripts.list 124.5 → 55.8 KB 15.5 → 3.0 µs 1.9 → 3.4 ms 15.31 → 0.65 MB
GCP storage.getBuckets 239.1 → 39.7 KB 674 → 6.2 µs 4.9 → 2.8 ms 5.14 → 0.69 MB

Official → distilled. Distilled wins every cell except the Cloudflare first call.

What changed

1. Codegen: fewer, smaller, lazier schemas

  • Structural dedupe (core/src/codegen/dedupe.ts): Moore partition refinement merges structurally identical schemas, also through enum aliases. Stripe goes from 12,529 to 2,720 shapes. AWS keeps shapes whose identifier is an XML element or query action name, because merging those changes the wire format.
  • Private schemas (breaking): schema consts are module-private. Reach them through op.input / op.output.
  • Lazy schema table and chunks: private schemas are built on first use from chunked builder functions, grouped by the operations that reach them, so a bundle keeps only its operation's chunks.
  • PURE annotations on every call nested in a top-level initializer (codegen/pure.ts). An outer /*@__PURE__*/ does not cover the calls in its arguments; EC2 describeRegions kept 1,695 schemas before this.
  • Latin-1 only output (codegen/latin1.ts, guard test latin1-source.test.ts). V8 stores a whole module source as two-byte if one character is outside Latin-1. Escaping 3 characters cut S3 import heap from 2,240 to 1,570 KB.
  • datafy: operations and error classes ship as data and become objects on first use.

2. schema-lite: plain schema trees for non-AWS SDKs

  • Generated non-AWS services use @distilled.cloud/core/schema-lite: plain AST-shaped nodes. AWS uses lite schemas too, with its own codec traits (packages/aws/src/lite.ts).
  • Encode/decode compile to per-schema request and response plans. Calls go through one boundary, without Effect.gen.
  • Strict mode (ResponseValidation.strict) checks responses with a compiled lite-AST checker (response-validation*.ts). Its compiler is built on first use, so only strict users pay for it. Effect Schema leaves the default bundle.

3. Core runtime

  • Import effect/<Module> directly; the root effect barrel loaded 3.3 MB of heap.
  • core/config replaces effect/Config (about 280 KB heap and 5 KB gz per bundle).
  • Retry without Effect.retry / Schedule; pagination without Stream.flatMap (that pulled in Queue and Semaphore).
  • Operations share methods through one prototype (about 1.3 KB → 230 B per operation).
  • Error classes are Data-based and built on first use (breaking: they do not validate on construction; ParseError.cause is a SchemaMismatch).
  • DISTILLED_DEBUG_HTTP is read once; credentials resolve without generators in 70 REST protocols.
  • One XML parser with fast paths for plain documents.
  • New @distilled.cloud/core/crypto: a swappable Crypto service (SHA-256, HMAC, key import) with Crypto.webCrypto, Crypto.nodeCrypto and Crypto.layer(impl).

4. AWS

  • Synchronous SigV4 with node:crypto where available, reused HMAC KeyObjects, one-shot hashing, and no URL/Headers parsing. SigV4.SigningKeyCache is a plain store (breaking).
  • Synchronous protocol handlers with per-schema (de)serialization plans; a warm call runs as one effect.
  • Credential providers, protocols and the event-stream codec load on first use. A bundle keeps only the protocol it uses.
  • Precomputed Region-only endpoint templates; big endpoint rules stay module constants.
  • Error map built on the first error response; non-streaming bodies read as text.

5. Build: lib/ layout

  • bundle-lib (scripts/bundle-lib.ts): each package's lib/ is bundled into a few group chunks. Output is minified (names kept), Latin-1, keeps PURE annotations, and has shifted source maps. lib/ is now generated output (breaking for anyone who reads it).
  • strip-lib-docs (scripts/strip-lib-docs.ts) removes JSDoc from compiled JS.
  • Lazy service stubs (scripts/lazy-services.ts): under Node (module-sync export condition) a service module is a stub that loads per-operation slices on first use. Bundlers get the full module (services.full). Each stub writes each export name once and imports one helper, lib/_bundle/lazy.js.
  • Deep AWS subpath exports collapse into patterns.

6. Tooling

  • rolldown 1.2.8. The HttpClientErrorSchema retention (it pinned about 15 KB gz of Effect Schema into every REST bundle) is a rolldown 1.2.5/1.2.6 tree-shaking bug, fixed in 1.2.7. The temporary Effect patch is removed. Alchemy should bump rolldown to ≥ 1.2.7 to get the same win.
  • pnpm-workspace.yaml overrides 17 @aws-sdk/* internals so the official AWS SDK used by the benchmark passes the minimum release age.

7. Benchmarks and dashboard

  • benches/sdks: per SDK, offline. Download size, 1-auth + 1-operation bundle (normal and strict), import heap and objects, import time, first call, added latency, consumer typecheck cost. One program per protocol a provider uses. An official suite runs the same calls through the official AWS v3, Cloudflare and @google-cloud/storage SDKs.

    • Import time: minimum of 3 fresh processes. Import heap: median of 4. First call: a sequential cold pass. undici is warmed for both sides.
  • benches/schema-lab: Effect Schema (eager, lazy, JIT), valibot, zod-mini, typebox and own descriptors side by side. Own static descriptors won on every axis and keep strict validation.

  • benches/dashboard: the page used for this work. It charts every run over time (normal and strict lines, official SDKs as reference lines), shows per-provider and per-service detail, and compares experiments with their baseline. runs/ holds the 84 runs (46 timeline, 38 experiments) slimmed to 8.5 MB; pnpm slim slims new runs.

    cd benches/dashboard
    pnpm dev                                         # http://127.0.0.1:5199
    pnpm bench -- --experiment "what I tried" --build
    pnpm history -- --fetch                          # benchmark main's tip
    pnpm single                                      # one offline HTML file

Breaking changes

  • Schema values are no longer exported. Use op.input / op.output.
  • Error classes are Data-based and do not validate on construction. ParseError.cause is a SchemaMismatch.
  • SigV4.SigningKeyCache is a plain store.
  • lib/ is minified, bundled, generated output.

Tried and dropped

  • Effect SchemaAOTCompiler (cannot compile through Suspend; MBs of code) and JIT schemas.
  • valibot, zod, typebox, packed-string descriptors.
  • Whitespace-only minify of lib/.
  • Upstreaming a PURE IIFE for HttpClientErrorSchema to Effect: the cause is rolldown, and the IIFE breaks tsc -b with TS4023.
  • PR feat(aws)!: type-only SDK driven by lazy descriptors #671 (descriptor-based, no schemas) was benchmarked as an experiment. It breaks ResponseValidation.strict; this branch reaches lower numbers with strict intact.

Open items

  • The floor for whole-provider import. Importing all 438 AWS modules cannot go below about 10 MB: V8 costs about 325 B per named export and 4.8 KB per module. To beat one official client on whole-provider import, the package root would need to export a lazy barrel. TypeScript would then load about 31–36 MB of .d.ts. This is an API decision.
  • Cloudflare first call is 3.4 ms against the official SDK's 1.9 ms.
  • 4 strict-mode benchmark programs fail (expo-eas, fly-io GraphQL, slack, zerossl). The cause is the canned response fixtures, not the SDKs.
  • Merge main and regenerate the 5 conflicting packages.

Checks

Run at f6019b376. The 2 commits after it add benches/dashboard and whitespace-only formatting.

  • pnpm typecheck:ci: pass.
  • pnpm vitest run: 1,538 tests pass.
  • Import every service module of every package: pass.
  • pnpm format:check: pass.

extractItems flattens pulled page chunks with Stream.transformPull, and single-page lists use Stream.fromEffect. Both drop Effect's concurrent-merge machinery (Queue, Semaphore, MutableList) from every bundle that uses a paginated operation.
DISTILLED_SERVER_RETRY_HINT_CAP_MS is read from a caller-provided ConfigProvider (looked up by its context key) or process.env. Importing effect/Config and effect/ConfigProvider for this one optional knob added about 5 KB gzip to every SDK bundle.
The root "effect" barrel loads every Effect module at runtime: 3.3 MB of heap and about 35 ms on import for every SDK that uses error-category (Cloudflare among them).
Each generated operation allocated its own method object, five getters and several closures: 1.3 KB per operation (2.1 KB paginated) at import, so EC2's 807 operations cost about 1 MB before any call. Methods and getters now live on a shared prototype; .pages/.items are built on first access. 231 B / 272 B per operation.
A PURE marker lets a bundler drop an unused call, but its arguments are still evaluated, and an unmarked call among them counts as a side effect. So `S.Array(Tag.pipe(T.XmlName("item")))` kept `Tag` and everything it references in every bundle that imports the module. annotateNestedCalls marks every call and `new` in top-level initializers and class extends clauses, outside function bodies, after generation.
Output of annotateNestedCalls + oxfmt over packages/*/src/services: the same code the generator now writes. EC2 describeRegions bundle: 137 KB -> 75 KB gzip; Elasticsearch 68 KB -> 53 KB.
Service modules declare thousands of top-level S.suspend(...).annotate(...) schemas (EC2 2,575, Elasticsearch 3,272), and importing a module built all of them: about 550 B each. core/schema's suspend now returns a small object on Effect's schema prototype that builds the same AST on first .ast access: about 180 B each.
Generalizes the lazy suspend: a lazy schema holds a builder and pending annotations, and on first use of .ast (or an own member like an array's value) builds the real schema and copies its own properties onto itself. Top-level S.Array / S.Record consts (EC2 784, Elasticsearch 2,265) no longer construct at import.
V8 keeps a module's source text in the heap and stores it at one byte per character only while every character is Latin-1. One em dash in an API description doubled the whole file: AWS S3's compiled module retained 2,240 KB at import, 1,570 KB without its 3 non-Latin-1 comment characters. toLatin1 spells comment punctuation in ASCII (U+XXXX for the rest) and escapes characters inside string, template and type literals, so values are unchanged.
Output of toLatin1 + oxfmt over packages/*/src/services (707 files): the same code the generator now writes.
gcp and google-workspace also generate src/unstable-services through the same CLI.
Same reason as the generated modules: a non-Latin-1 character anywhere in a module (an em dash in a comment, usually) makes V8 store its whole source at two bytes per character. core's runtime modules alone went from 633 KB to 410 KB of import heap, paid by every SDK. latin1-source.test.ts keeps it that way for every package's src (codegen and tests excluded).
An Effect S.TaggedError class carries its own copies of about fifteen static methods plus a tagged struct schema: about 4.6 KB per class at import, and generated services declare 9,848 of them (EC2 248). core/schema's TaggedError now returns a small constructor that builds the real class on first construction or first schema static (ast, fields, make, ...), then inherits from it; decoding still yields instances of the declared class. About 1.3 KB per class until used.
The lazy TaggedError looked up Effect's error base at module load with an unmarked S.TaggedError() call, so every bundle importing core/schema kept Effect's class machinery even with no error classes (Resend +0.2 KB gzip). The lookup and the shared statics object are now created on first use.
schema-lab compares schema representations on real distilled shapes (EC2, S3, DynamoDB, Stripe, Cloudflare Workers): Effect Schema eager/lazy/JIT/AOT, valibot, zod/mini, typebox, and static descriptors; plus structural dedupe of named shapes (Moore partition refinement).
Specs repeat shapes under different names; each copy was its own runtime schema. dedupeSchemas keeps the first schema of each structural class (iterated to a fixpoint through references) and turns the rest into typed aliases, so exports and types are unchanged. Operation inputs keep their own object, and AWS XML/query modules are skipped because those protocols put a shape's identifier on the wire.
90,119 schema consts become aliases (Stripe 1,535, Cloudflare Workers 403).
WebCrypto is async-only, and signing one request made six promise round trips (two digests, four HMAC key imports+signs on cache miss, one on hit). On Node, Bun and Deno the signer now uses node:crypto (found via process.getBuiltinModule, so the module still loads elsewhere) and falls back to WebCrypto. Added latency per call: S3 listBuckets 352 -> 149 us, DynamoDB listTables 218 -> 103 us, STS getCallerIdentity 282 -> 125 us.
core/api: the call is a plain flatMap chain (no Effect.gen, protocol read from its cached context instead of provideContext), and retry machinery (Ref + schedule) is built only after a failure the policy retries; the first failure is replayed as attempt one so delays and counts are unchanged. AWS: hot protocol/client functions use Effect.fnUntraced instead of Effect.fn, which captured a stack trace per call. Added latency: Resend listLogs 27.6 -> 12.1 us, S3 listBuckets 149 -> 117 us, DynamoDB listTables 103 -> 81 us.
createHmac with a raw key re-prepares it on every call (7 us); a KeyObject made once per derived key costs 1.6 us. DynamoDB listTables added latency 81 -> 60 us.
Every response body went Effect Stream -> web ReadableStream -> new Response(stream).text(). Outputs with no streaming or payload member are now read with response.text directly (decided once per operation). Also drops four per-call Effect.logDebug calls. Added latency: S3 listBuckets 117 -> 66 us, DynamoDB listTables 60 -> 43 us, STS 125 -> 55 us.
V8 keeps every loaded module's source text in the heap, so doc comments in lib/*.js cost memory in every process that imports an SDK (44% of AWS S3's compiled service module). scripts/strip-lib-docs.ts removes whole-line JSDoc blocks from lib/**.js after tsc (pnpm build, the release workflow, the SDK bench's --build), keeping line numbers so source maps stay valid and checking that no string literal changed. Docs remain in .d.ts and src/. 17.9 MB removed across 2,778 files.
@distilled.cloud/core/crypto: a context reference (never in requirements) with SHA-256, HMAC-SHA256 and key import. The default uses node:crypto when the runtime has it and WebCrypto otherwise; Crypto.webCrypto / Crypto.nodeCrypto force one, Crypto.layer(impl) plugs in any other (HSM, native addon, test double). Effect's own Crypto service has no HMAC and is a required service, so it would change every operation's requirements. AWS SigV4 now signs through it; derived signing keys are cached as imported key handles, keyed by implementation.
References to leaf aliases (export const Status = S.String) now compare by the schema they alias, so structs that differ only in which string enum a field names merge too.
21,887 more schema consts become aliases.
Every export costs ~280 B per importing process (binding cell, namespace accessor, name string); Stripe's service module had 14,224. Generated modules now export only operations, error classes and types; schema consts are module-private and the ones no operation or error reaches are deleted. Schemas stay reachable as op.input / op.output (now part of OperationMethod's type).

BREAKING: schema/enum values such as stripe.Customer or s3.PutObjectRequest are no longer exported; the types are. Use op.input / op.output.
440,717 schema consts made private, 135,290 unreachable ones removed.
Each schema initializer evaluated at import cost ~330 B (thunk closure, function record, lazy wrappers, annotation object). Initializers now live in builder functions of 8 schemas (switch on an index), and each const is one lazy object holding its builder and index; V8 compiles a builder on its first use and the code text stays only in the source. A packed-string interpreter was tried first and lost: string literals are stored twice (source + heap string). Import heap: Stripe service module 3.1 -> 1.5 MB, EC2 3.5 -> 2.1 MB.
302,975 schema initializers moved into builders.
A top-level const costs memory at import even when its schema is never built: a context slot, its name in the module scope info, module-body bytecode and the lazy wrapper. Private schema consts, their aliases and leaf aliases (S.String enums) are removed; references become __S(i), which makes the lazy wrapper on first reach and memoizes it. Operation configs keep their types through the original casts. Stripe's service module import: 1.36 MB -> 0.48 MB.

Also: core/schema re-exports effect/Schema types only (a value export * cost ~70 KB per process for 345 bindings) plus the values code uses, and core's own errors use the lazy TaggedError.
…document scanner

The separate plain-document scanner doubled the parser's code. The full
parser now gets the speed in place: parsed objects inherit from an empty
prototype that has no prototype itself (V8 keeps them in fast mode), ASCII
names match a cheap non-Unicode pattern before the Unicode one, text without
entities or CRs is not rebuilt, and element text is concatenated instead of
joined. Small AWS responses parse in ~1.4 us (6-8 us at f966856). The
differential tests now compare against the f966856 parser, kept as a test
fixture.
…rger than before

The call path added ~2.4-3.7 KB gzip per AWS program, mostly code kept twice
(sync and effect versions). Now there is one of each:

- Protocol handlers are serialize / deserializeText / deserializeStream /
  deserializeError; the lazy protocol traits only hand over the loaded
  handler. deserialize() in client/protocol.ts is the one 2xx path (stream
  outputs, else the body read as text), used by the response parser.
- makeRequestBuilder takes the loaded handler and returns the request
  directly for operations without middleware or streaming payloads, an
  effect otherwise. protocol.ts compiles one plan per operation (builder +
  synchronous 2xx decoder) that call, encode and decode all use, and keeps
  one protocol handler per operation instead of three.

- canonicalize builds the header record directly for ordinary headers and
  only goes through Headers for ones it would normalize or reject, so the
  separate URL-parts canonicalizer is gone. A test compares signatures,
  headers and URLs with the f966856 signer (kept as a test fixture) over
  960 combinations.
- Derived signing keys live in one small per-secret map shared by sync and
  async signing. SigningKeyCache is now an optional SigningKeyStore
  ({ get(scope) }) a scope can provide; Cache.get(cache, scope) adapts an
  effect Cache.
- hashPayload signs the body's SHA-256 as X-Amz-Content-Sha256 (S3 Control,
  Glacier) with the context's crypto instead of a separate WebCrypto digest.
- SigV2 (SimpleDB) moves to sigv2.ts and is reached through the AwsAuthSigv2
  trait, so other programs do not bundle it.
…ficial probe

official-probe.ts ran 200 mocked fetches before importing the SDK; probe.ts did not, so distilled's first call paid undici's first-use cost (~1.1 ms) and the official SDKs did not.
# Conflicts:
#	packages/aws/src/rules-engine/endpoint-resolver.ts
…cies

The official suite now records size.{tarball,withDeps,unpackedWithDeps,packages}
for each official SDK: its npm tarball plus every dependency npm install
fetches, counted once. size.distilled holds the same measurement for the
distilled package it is compared with. Tarballs are rebuilt locally (tar +
gzip -9, within 1-3% of npm pack) because npm pack crashes in the pnpm
store; runs offline.
…ue entries, operation-name stems

Module constants only data reads (service traits, endpoint rules) move into
the refs of the holder of the roots that reach them and are built on first
use. Constant expressions repeated across holders (the service trait pipe, a
base URL) become value entries (~N, ~R:N). New holders only as many as moved
constants free, so no module gains objects at import. An operation whose name
recurs in its strings writes it once (API.named). #R:N replaces ^R:N.
The bundler renames namespace imports to short bindings already; the
module-level alias consts only cost source, locals and bundle bytes.
A moved constant is parsed again when its holder first runs; for S3's
120 KB rules that added 0.5 ms to the first call.
…the full modules

Each generated service module in lib/ moves to lib/services.full/ (what
bundlers resolve: module/default conditions) and lib/services/ gets a stub
(Node: module-sync condition) with one placeholder per operation and error
class and no operation data. The first use loads the full module with
require(esm); its op/opPaginated/error definitions adopt the placeholders,
so identities stay single. Service barrels become lazy namespaces in Node.
…ion reaches

bundle-lib ends each full service module with a slice index (a trailing
comment bundlers drop): per top-level declaration its byte range and the
names it reads. A stub placeholder's first use reads its operation's data
and the holders and values it reaches from the file, instead of loading the
whole module: data functions in their generated shape are read as data,
other code is compiled with Function, function arguments of values (endpoint
resolvers) only when called. Without an index the whole module loads as
before. Lib code that imports a service by path names it through the
package, so Node gets the stub and bundlers the full module.
…hole

- The slice index is compact text with a fixed-width line table; names
  sit on their lines; data functions resolve their refs without compiling.
- Function arguments of values (endpoint resolvers) compile only when
  called; a body compiled alone is a block, so its declarations shadow.
- The first stub of a process reads its index in a microtask after import.
- Error placeholders carry their class name (computed key, no own
  property); data functions are called with themselves as this.
- Service modules under 8 KB stay whole for Node.
…ckage helper

V8 keeps a module's source on the heap and each import costs a module
~1 KB. Stubs now import only lib/_bundle/lazy.js, which passes its own URL
and the namespaces every stub imports to API.lazyService; they declare
'export const name=$o()' instead of a declaration plus an export list, and
error classes exported under their class name are destructured from a
proxy (export const{A,B}=$E), so each name is written once.
Provider heap, all services imported: aws 14.49 -> 13.29 MB, gcp 9.73 ->
9.00, azure 8.26 -> 7.67, cloudflare 3.22 -> 3.07; first call unchanged.
…oard)

The page charts every benches/sdks run over time, compares experiments, and
shows the official SDKs as reference lines. runs/ holds the 84 runs this
branch was measured with, slimmed to the fields the page reads (pnpm slim).
# Conflicts:
#	packages/aws/src/services/emr.ts
#	packages/aws/src/services/lambda-core.ts
#	packages/aws/src/services/lambda-web.ts
#	packages/aws/src/services/mwaa.ts
#	packages/cloudflare/package.json
#	packages/cloudflare/src/services/durable_objects.ts
#	packages/cloudflare/src/services/magic_transit.ts
#	packages/cloudflare/src/services/ssl.ts
#	packages/cloudflare/src/services/stream.ts
#	packages/cloudflare/src/services/workers.ts
#	packages/cloudflare/src/services/workers_for_platforms.ts
#	packages/cloudflare/src/services/zaraz.ts
#	packages/cloudflare/src/services/zero_trust.ts
#	packages/fly-io/src/services/sprites.ts
#	packages/gcp/package.json
#	packages/gcp/src/services/dataplex_v1.ts
#	packages/gcp/src/services/storage_v1.ts
#	packages/turso/src/services/turso.ts
#	pnpm-lock.yaml
#	pnpm-workspace.yaml
@alchemy-version-bot

Copy link
Copy Markdown
Contributor

Install the packages built from this commit:

@distilled.cloud

pnpm install https://pkg.distilled.cloud/@distilled.cloud/acme/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/adyen/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/apache-superset/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/archil/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/argocd/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/auth0/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/aws/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/axiom/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/azure/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/boat-dev/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/celld/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/chronosphere/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/clerk/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/cloudflare/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/coinbase/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/coolify/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/core/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/customerio/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/datadog/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/daytona/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/digitalocean/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/discord/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/docker/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/doppler/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/elasticsearch/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/expo-eas/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/fly-io/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/forgejo/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/gcp/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/github/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/google-workspace/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/grafana/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/growthbook/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/gusto/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/hetzner/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/hostinger/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/huggingface/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/iceberg/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/infisical/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/inngest/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/intercom/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/kubernetes/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/launchdarkly/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/meilisearch/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/mercury/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/metabase/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/modal/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/modrinth/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/mongodb-atlas/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/neon/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/notion/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/okta/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/onepassword/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/opencode/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/ovh/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/paypal/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/plaid/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/planetscale/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/polar/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/porkbun/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/posthog/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/prisma/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/railway/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/redis-cloud/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/remote/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/render/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/resend/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/s2/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/sentry/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/slack/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/spacetimedb/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/squarespace/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/stackit/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/stripe/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/supabase/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/surrealdb/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/temporal/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/trigger-dev/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/turbopuffer/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/turso/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/typesense/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/unkey/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/vanta/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/vercel/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/whop/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/workos/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/xata/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/zendesk/pr:873:bd1a2ca
pnpm install https://pkg.distilled.cloud/@distilled.cloud/zerossl/pr:873:bd1a2ca

Published Oct 15, 2026, 4:29 PM UTC. Expires Oct 22, 2026, 4:29 PM UTC, extended while this pull request is open.

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