Repository navigation
build: migrate runtime, tooling and tests from node to bun - #3560
Draft
Pooya Raki (PooyaRaki) wants to merge 2 commits into
Draft
Pooya Raki (PooyaRaki) wants to merge 2 commits into
Pooya Raki (PooyaRaki) wants to merge 2 commits into
Conversation
Pooya Raki (PooyaRaki)
marked this pull request as draft
October 9, 2026 08:06
Pooya Raki (PooyaRaki)
force-pushed
the
claude/serene-hawking-1t7c89
branch
5 times, most recently
from
October 9, 2026 16:23
078c080 to
cd87467
Compare
Runs the app, scripts and migrations on Bun 1.4.2 straight from the TypeScript sources, swaps Yarn for Bun's package manager (frozen lockfile, exact versions, a 7-day minimum release age, no dependency lifecycle scripts, `bun audit` in CI) and moves every suite from Vitest to bun:test. dd-trace is replaced by OpenTelemetry, exported over OTLP to the Datadog Agent, since Datadog does not support dd-trace on Bun. Rebased onto main's `#/` imports: Bun resolves them through tsconfig.json `paths`, and `bun run typecheck` keeps main's guarantee of type-checking the specs, with Jest-compatible mock and matcher typings for bun:test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GStCQiW4fcycjN6k2KmRQz
The notifications "token is invalid" spec built its token with `faker.string.sample()`, which can contain two dots and then parses as a three-part JWT that jsonwebtoken rejects as "invalid token" rather than "jwt malformed"; it now uses `faker.string.alphanumeric()` like its siblings. The SafeSignature v-value spec drew a random v, and a v of 0 marks a contract signature that needs a dynamic part; v is now 1-255. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GStCQiW4fcycjN6k2KmRQz
Pooya Raki (PooyaRaki)
force-pushed
the
claude/serene-hawking-1t7c89
branch
from
October 9, 2026 16:24
cd87467 to
06bb398
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces Node.js, Yarn 4, Vitest and dd-trace with Bun 1.4.2 (latest release) as runtime, package manager and test runner. It also replaces Datadog's
dd-tracewith OpenTelemetry. This is one PR, not a split series, because the request was for a single migration PR.The branch is rebased onto current
main. It includes #3476's move to TypeScript 7 (typescript@7.0.2is kept forbun run typecheck), #3551's#/imports, #3573's blocklist removal and #3482's Biome 2.5.15. #3476'stsc-alias/concurrently/distbuild and the Yarn bumps are superseded, because Bun runs the sources directly.#/imports. Bun 1.4.2 does not resolvepackage.jsonimportssubpaths, but it does resolvetsconfig.jsonpaths. So#/*→./src/*and#/abis/*→./abis/*live intsconfig.jsonpaths, and the Node-onlyimportsmap (which pointed atdist/) is gone. The production image shipstsconfig.jsonand keepsWORKDIR /app(#3570).Why OpenTelemetry. Datadog does not support
dd-traceon Bun and recommends the OpenTelemetry SDK instead. NativeBun.otelis still an unmerged Bun PR. I checked on Bun 1.4.2 that the standard instrumentations produce spans for:node:httpviarequire, so it can be patched)pg,redisandioredis(the instrumentation supports ioredis<7, so main's ioredis 6 is covered)amqplibSpans go over OTLP/HTTP to the Datadog Agent's OTLP receiver. Log lines carry
dd.trace_id/dd.span_id, so Datadog can link each log to its trace.Deploy prerequisite. Before this ships, enable the Agent's OTLP/HTTP receiver, e.g.
DD_OTLP_CONFIG_RECEIVER_PROTOCOLS_HTTP_ENDPOINT=0.0.0.0:4318. Traces go tohttp://$DD_AGENT_HOST:4318/v1/tracesunlessOTEL_EXPORTER_OTLP_(TRACES_)ENDPOINTis set.DD_SERVICE/DD_ENV/DD_VERSIONbecome the resource's service, environment and version. The default is to sample every trace; useOTEL_TRACES_SAMPLER/OTEL_TRACES_SAMPLER_ARGto lower that.Supply-chain settings carry over from
.yarnrc.yml:npmMinimalAgeGate(7 days)bunfig.tomlminimumReleaseAge = 604800(7 days)enableScripts: falsetrustedDependencies: []enableHardenedMode/--immutablebun install --frozen-lockfile(bun.lock integrity hashes)A new CI job,
ci / audit, runsbun audit(currently: no vulnerabilities). The earlierfastifyoverride is dropped:@nestjs/platform-fastify@12.1.2frommainalready pins the patchedfastify@5.12.5. Dependabot now uses thebunecosystem.Formatter/linter. Bun 1.4.2 has no built-in formatter or linter, so Biome stays.
Behaviour changes for reviewers:
tscemit, nodist/, notsconfig.build.json. TypeORM loadsmigrations/*.ts. The image isoven/bun:1.4.2-alpinewith production dependencies only. One-off scripts run asbun scripts/generate-token.ts.fetchignores undici's global dispatcher (verified), so the undici Agent, its shutdown hook and theUNDICI_*variables were no-ops and are gone. Bun'sfetchpools keep-alive connections itself, and was 2.5–5× faster than undici-on-Bun in a local benchmark. Per-request timeouts (AbortSignal.timeout) are unchanged.undefined, now fail to load. They are broken withz.lazy(User↔Wallet, notification schemas), TypeORM'sRelation<T>on relation properties,forwardRef(UserIdentityResolverModule), and a dedicatedfetch-client.entity.tsforFetchClientToken.import type.tsconfigusesverbatimModuleSyntax/module: Preserve/moduleResolution: bundler/noEmit.chain.entity.tshad been importing a type as a value.@Injectare emitted asObjectwhen the class has no class decorator. The only affected class,KilnApiManager, got@Injectable().Tests. All suites run on
bun:testwith explicit imports and--parallelfile isolation.main:bun run typecheckistsc --noEmit -p tsconfig.json, specs included. To keep main's specs compiling underbun:test's stricter typings,src/__tests__/mocks.tserases generics from a mock's implementation type (as Vitest/Jest do) andtest/bun-test.d.tsgives the equality matchers Jest's genericexpected(so a datasource'sRaw<T>can be compared withT).bun test --shardjobs in CI, matching main's split.mainshowed the same tests run. Two undici-dispatcher tests were removed. 34 migration specs undermigrations/that Vitest'ssrc/**glob never ran now run too.mainin the same environment.test/setup.ts:AsyncLocalStorage#enterWith()during test-file evaluation (nestjs-cls does one at import) hangs that file's tests.jest.resetAllMocks()dropsjest.fn(impl)implementations. Vitest 4 kept them, and shared pass-through doubles rely on that.bun:testhas notoFakeoption, so the entitlements spec that faked onlyDate(Postgres needs real timers) usessetSystemTimeinstead.checkGuardIsAppliedpassed on any first guard, because Vitest'sexpectreturns a truthy object.stringMatchingregex from[, which matched almost anything.main):faker.string.sample(), which can contain two dots and then parses as a three-part JWT ("invalid token" instead of "jwt malformed"). It now usesfaker.string.alphanumeric()like its siblings. CI seed7016067276651539reproduced it.SafeSignature > should return the v valuedrew a random v; v0marks a contract signature, which needs a dynamic part (about 1 in 256 runs). v is now 1–255.*.e2e.spec.ts, because Bun only discovers*.spec.*. Biome's test-globals list is removed now that every spec imports frombun:test.Changes
package.json,bun.lock,bunfig.toml: Bun scripts and supply-chain settings, the OpenTelemetry dependencies, andtypescript@7.0.2for type-checking. Removedyarn.lock,.yarn/,.yarnrc.yml,.nvmrc,vitest.config.ts,tsconfig.build.json,test/faker-setup.ts,test/shared-module-cache-setup.ts, Vitest, SWC,tsc-alias,concurrently,@types/node,undicianddd-trace.tsconfig.json: Bun types,#/paths,verbatimModuleSyntax,noEmit.src/tracing/:src/config/,.env.sample.json: addedtracingconfig (OTEL_SDK_DISABLED, OTLP endpoints,DD_AGENT_HOST/DD_SERVICE/DD_ENV/DD_VERSION); removedundici.src/datasources/network/: outbound requests wrapped in client spans withtraceparentpropagation; undici dispatcher removed.src/logging/:dd.trace_id/dd.span_idon log lines.src/modules/{users,wallets,notifications}schemas:z.lazy**/*.entity.db.ts:Relation<T>src/modules/spaces/spaces.module.ts:forwardRefsrc/modules/staking/datasources/kiln-api.manager.ts:@Injectable()bun:test, including those added or changed onmainsince. Addssrc/__tests__/mocks.ts(Mocked* types,mocked(),automock()),test/setup.ts(preload) andtest/bun-test.d.ts(matcher typings), plus new specs for the tracing helpers.Dockerfile,.dockerignore:oven/bunimage, TS sources, and the--preload ./src/tracing/tracing.tsCMD..github/workflows/_ci.yml(renamed from_ci-node.yml, same job names plusaudit);pull-request.yml,devstaging.ymlandproduction.ymlcallers;.github/dependabot.yml..devcontainer/,.vscode/,.husky/pre-commit: Bun.README.md(new Observability section),AGENTS.md(pre-commit is nowbun run format/lint/typecheck/test),TESTING.md,docs/agents/*,.claude/skills/*,.specify/,specs/, module READMEs. Also adds SPDX headers to the touched files that lacked one.🤖 Generated with Claude Code
https://claude.ai/code/session_01GStCQiW4fcycjN6k2KmRQz