serverless-ircd 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +2 -2
- package/.github/workflows/deploy-aws.yml +1 -3
- package/.github/workflows/deploy-cf-tcp.yml +87 -0
- package/.github/workflows/deploy-cf.yml +1 -1
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +174 -18
- package/README.md +63 -30
- package/apps/aws-stack/README.md +2 -2
- package/apps/aws-stack/bin/aws.ts +7 -0
- package/apps/aws-stack/package.json +4 -4
- package/apps/aws-stack/src/aws-stack.ts +118 -6
- package/apps/aws-stack/tests/stack.test.ts +98 -3
- package/apps/cf-tcp-container/Dockerfile +69 -0
- package/apps/cf-tcp-container/package.json +34 -0
- package/apps/cf-tcp-container/src/config-loader.ts +145 -0
- package/apps/cf-tcp-container/src/container-do.ts +38 -0
- package/apps/cf-tcp-container/src/container-server.ts +363 -0
- package/apps/cf-tcp-container/src/main.ts +77 -0
- package/apps/cf-tcp-container/src/persistence.ts +144 -0
- package/apps/cf-tcp-container/src/worker.ts +41 -0
- package/apps/cf-tcp-container/terraform/provider.tf +24 -0
- package/apps/cf-tcp-container/terraform/spectrum.tf +81 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +217 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +465 -0
- package/apps/cf-tcp-container/tests/persistence.test.ts +227 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +275 -0
- package/apps/cf-tcp-container/tsconfig.build.json +17 -0
- package/apps/cf-tcp-container/tsconfig.test.json +15 -0
- package/apps/cf-tcp-container/vitest.config.ts +26 -0
- package/apps/cf-tcp-container/wrangler.toml +63 -0
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/wrangler.test.toml +6 -0
- package/apps/cf-worker/wrangler.toml +28 -2
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +10 -0
- package/apps/local-cli/src/server.ts +20 -3
- package/package.json +14 -10
- package/packages/aws-adapter/package.json +3 -3
- package/packages/aws-adapter/src/account-store.ts +1 -1
- package/packages/aws-adapter/src/admission.ts +74 -0
- package/packages/aws-adapter/src/aws-runtime.ts +6 -4
- package/packages/aws-adapter/src/config-loader.ts +32 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +35 -96
- package/packages/aws-adapter/src/handlers/connect.ts +64 -8
- package/packages/aws-adapter/src/handlers/default.ts +35 -2
- package/packages/aws-adapter/src/handlers/index.ts +69 -3
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +481 -0
- package/packages/aws-adapter/src/handlers/ping-checker.ts +1 -1
- package/packages/aws-adapter/src/index.ts +6 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +45 -34
- package/packages/aws-adapter/tests/account-store.test.ts +19 -20
- package/packages/aws-adapter/tests/admission.test.ts +70 -0
- package/packages/aws-adapter/tests/aws-harness.ts +13 -1
- package/packages/aws-adapter/tests/config-loader.test.ts +20 -0
- package/packages/aws-adapter/tests/connect.test.ts +78 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +47 -40
- package/packages/aws-adapter/tests/gone-exception.test.ts +31 -26
- package/packages/aws-adapter/tests/handlers.test.ts +154 -53
- package/packages/aws-adapter/tests/nlb-stream.test.ts +478 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +34 -29
- package/packages/aws-adapter/tests/sweeper.test.ts +25 -18
- package/packages/aws-adapter/tests/transactions.test.ts +25 -20
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +2 -4
- package/packages/cf-adapter/src/config-loader.ts +33 -0
- package/packages/cf-adapter/src/connection-do.ts +111 -46
- package/packages/cf-adapter/src/d1-account-store.ts +198 -0
- package/packages/cf-adapter/src/env.ts +33 -9
- package/packages/cf-adapter/src/index.ts +9 -8
- package/packages/cf-adapter/tests/cf-harness.ts +11 -1
- package/packages/cf-adapter/tests/cf-integration.test.ts +2 -1
- package/packages/cf-adapter/tests/config-loader.test.ts +22 -0
- package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +37 -0
- package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +52 -0
- package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +166 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +226 -0
- package/packages/cf-adapter/tests/raw-modules.d.ts +11 -0
- package/packages/cf-adapter/tests/worker/main.ts +8 -1
- package/packages/cf-adapter/wrangler.test.toml +8 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +6 -1
- package/packages/irc-core/scripts/generate-build-info.mjs +31 -0
- package/packages/irc-core/src/caps/capabilities.ts +23 -2
- package/packages/irc-core/src/cloak.ts +1 -1
- package/packages/irc-core/src/commands/cap.ts +8 -1
- package/packages/irc-core/src/commands/index.ts +9 -0
- package/packages/irc-core/src/commands/ison.ts +61 -0
- package/packages/irc-core/src/commands/quit.ts +12 -0
- package/packages/irc-core/src/commands/registration.ts +18 -12
- package/packages/irc-core/src/commands/sasl.ts +72 -9
- package/packages/irc-core/src/commands/server-info.ts +129 -0
- package/packages/irc-core/src/commands/userhost.ts +84 -0
- package/packages/irc-core/src/commands/whowas.ts +113 -0
- package/packages/irc-core/src/config.ts +58 -0
- package/packages/irc-core/src/credential-hashing.ts +124 -0
- package/packages/irc-core/src/effects.ts +6 -29
- package/packages/irc-core/src/index.ts +1 -0
- package/packages/irc-core/src/ports.ts +181 -12
- package/packages/irc-core/src/protocol/numerics.ts +14 -8
- package/packages/irc-core/src/types.ts +38 -1
- package/packages/irc-core/tests/account-store.test.ts +45 -2
- package/packages/irc-core/tests/caps/capabilities.test.ts +4 -3
- package/packages/irc-core/tests/commands/cap.test.ts +33 -1
- package/packages/irc-core/tests/commands/ison.test.ts +166 -0
- package/packages/irc-core/tests/commands/quit.test.ts +69 -2
- package/packages/irc-core/tests/commands/registration.test.ts +151 -6
- package/packages/irc-core/tests/commands/sasl.test.ts +118 -10
- package/packages/irc-core/tests/commands/server-info.test.ts +274 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +9 -35
- package/packages/irc-core/tests/commands/userhost.test.ts +264 -0
- package/packages/irc-core/tests/commands/whowas.test.ts +312 -0
- package/packages/irc-core/tests/config.test.ts +95 -1
- package/packages/irc-core/tests/credential-hashing.test.ts +170 -0
- package/packages/irc-core/tests/effects.test.ts +0 -27
- package/packages/irc-core/tests/nick-history-store.test.ts +162 -0
- package/packages/irc-core/tests/numerics.test.ts +12 -0
- package/packages/irc-core/tests/types.test.ts +35 -1
- package/packages/irc-core/tsconfig.build.json +1 -1
- package/packages/irc-core/tsconfig.test.json +1 -1
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +158 -14
- package/packages/irc-server/src/dispatch.ts +0 -3
- package/packages/irc-server/src/index.ts +10 -2
- package/packages/irc-server/src/routing.ts +12 -0
- package/packages/irc-server/src/transport.ts +101 -0
- package/packages/irc-server/tests/actor.test.ts +400 -3
- package/packages/irc-server/tests/dispatch.test.ts +0 -17
- package/packages/irc-server/tests/routing.test.ts +4 -0
- package/packages/irc-server/tests/transport.test.ts +230 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/harness.ts +44 -9
- package/packages/irc-test-support/src/in-memory-harness.ts +73 -9
- package/packages/irc-test-support/src/index.ts +3 -0
- package/packages/irc-test-support/src/scenarios.ts +132 -2
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +2 -1
- package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +23 -9
- package/pnpm-workspace.yaml +9 -1
- package/tools/ci-hardening/package.json +1 -1
- package/tools/package.json +4 -0
- package/tools/seed-aws-accounts.ts +5 -6
- package/tools/seed-cf-accounts.ts +104 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/docs/ADR-001-pure-reducers-and-effect-system.md +0 -74
- package/docs/ADR-002-location-of-authority.md +0 -82
- package/docs/ADR-003-durable-object-sharding.md +0 -93
- package/docs/ADR-004-dynamodb-schema.md +0 -96
- package/docs/ADR-005-wss-only-transport-v1.md +0 -83
- package/docs/ADR-006-sasl-mechanism-scope.md +0 -86
- package/docs/ADR-007-deterministic-ports.md +0 -82
- package/docs/ADR-008-monorepo-tooling.md +0 -60
- package/docs/AWS-Adapter-Architecture.md +0 -496
- package/docs/AWS-Deployment.md +0 -1186
- package/docs/Cloudflare-Deployment-Guide.md +0 -660
- package/docs/Home.md +0 -11
- package/docs/Observability.md +0 -87
- package/docs/PlanIRCv3Websocket.md +0 -489
- package/docs/PlanWebClient.md +0 -451
- package/docs/Release-Process.md +0 -443
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
# ADR-006: SASL mechanism scope (PLAIN only, EXTERNAL deferred)
|
|
2
|
-
|
|
3
|
-
**Date:** 2025-01-20 (Phase 1)
|
|
4
|
-
**Status:** Accepted (EXTERNAL re-evaluation tracked in TICKET-079)
|
|
5
|
-
**Supersedes:** —
|
|
6
|
-
**Superseded by:** —
|
|
7
|
-
|
|
8
|
-
## Context
|
|
9
|
-
|
|
10
|
-
IRCv3 SASL (`CAP sasl`) lets a client authenticate during registration,
|
|
11
|
-
before the welcome sequence. The server advertises supported mechanisms in
|
|
12
|
-
the `sasl` cap value (e.g. `sasl=PLAIN,EXTERNAL`). Each mechanism has
|
|
13
|
-
different infrastructure requirements:
|
|
14
|
-
|
|
15
|
-
- **PLAIN:** the client sends `AUTHENTICATE PLAIN`, then a base64 payload of
|
|
16
|
-
`\0username\0password`. The server verifies the credentials against an
|
|
17
|
-
`AccountStore`. No client certificates, no TLS-passthrough — works over
|
|
18
|
-
any transport, including wss with edge-terminated TLS.
|
|
19
|
-
- **EXTERNAL:** the server authenticates the client based on an external
|
|
20
|
-
identity, typically a client certificate verified via mutual TLS (mTLS).
|
|
21
|
-
This requires the TLS handshake to present the client cert to the
|
|
22
|
-
application layer. On serverless platforms with edge-terminated TLS
|
|
23
|
-
(Cloudflare, API Gateway), the application does not see the raw client
|
|
24
|
-
cert — it must be configured at the edge (API Shield mTLS, custom-domain
|
|
25
|
-
mTLS trust store). That infrastructure does not exist in v1 (ADR-005:
|
|
26
|
-
wss-only, no raw TCP; mTLS passthrough is Phase 7, TICKET-054).
|
|
27
|
-
|
|
28
|
-
## Decision
|
|
29
|
-
|
|
30
|
-
v1 ships **SASL PLAIN only** as a functional mechanism. EXTERNAL is
|
|
31
|
-
**recognized but stubbed**: the reducer accepts `AUTHENTICATE EXTERNAL`,
|
|
32
|
-
acknowledges the mechanism, and immediately returns `904 ERR_SASLFAIL` with a
|
|
33
|
-
documented code comment cross-referencing TICKET-054 (mTLS) and TICKET-079
|
|
34
|
-
(decision: implement or stop advertising).
|
|
35
|
-
|
|
36
|
-
The `sasl` cap advertises `PLAIN,EXTERNAL` (both names listed) so clients
|
|
37
|
-
that probe mechanism availability see EXTERNAL as nominally supported. The
|
|
38
|
-
actual `904` on EXTERNAL attempt is the functional gate. TICKET-079 is the
|
|
39
|
-
follow-up that decides whether to implement EXTERNAL against a pluggable
|
|
40
|
-
`MtlsIdentityProvider` or stop advertising it until mTLS lands.
|
|
41
|
-
|
|
42
|
-
`AccountStore` is a port in `irc-core` consumed by the SASL reducer. The
|
|
43
|
-
`verify(mechanism, payload)` method is **synchronous** — adapters pre-load
|
|
44
|
-
credentials at boot so the reducer stays pure (ADR-001). PLAIN verification
|
|
45
|
-
decodes the base64 payload, extracts username + password, and matches against
|
|
46
|
-
the store.
|
|
47
|
-
|
|
48
|
-
## Consequences
|
|
49
|
-
|
|
50
|
-
**Positive:**
|
|
51
|
-
- PLAIN works end-to-end in v1 with no special infrastructure. The
|
|
52
|
-
`AccountStore` port abstracts the credential store; adapters seed it from
|
|
53
|
-
config (in-memory), DynamoDB (AWS), or a persistent backend (D1/DO for CF,
|
|
54
|
-
TICKET-093).
|
|
55
|
-
- EXTERNAL is forward-compatible: advertising it now means clients that
|
|
56
|
-
prefer EXTERNAL will attempt it; once mTLS lands (Phase 7), the same
|
|
57
|
-
`AUTHENTICATE EXTERNAL` flow succeeds without a client-side change.
|
|
58
|
-
- The synchronous `AccountStore.verify()` keeps the reducer pure and
|
|
59
|
-
deterministic. Async credential lookups (DynamoDB) are resolved at cold
|
|
60
|
-
start or via pre-loading, not inside the reducer.
|
|
61
|
-
|
|
62
|
-
**Negative:**
|
|
63
|
-
- EXTERNAL is advertised but non-functional in v1 — a client that negotiates
|
|
64
|
-
`sasl` and sends `AUTHENTICATE EXTERNAL` gets `904` with no actionable
|
|
65
|
-
error message. This is a poor UX until TICKET-079 resolves (stop-advertising
|
|
66
|
-
is the recommended interim per the ticket).
|
|
67
|
-
- PLAIN sends the password base64-encoded (not encrypted) inside the wss
|
|
68
|
-
frame. wss provides the TLS layer, so the password is encrypted in transit
|
|
69
|
-
— but PLAIN is still weaker than EXTERNAL (no mutual auth).
|
|
70
|
-
- The synchronous `AccountStore` contract means the adapter must pre-load all
|
|
71
|
-
credentials. For deployments with many accounts, cold-start latency grows
|
|
72
|
-
linearly with account count. A lazy/async lookup variant is a documented
|
|
73
|
-
follow-up but would require an async-reducer or effect-based credential
|
|
74
|
-
query — a significant architecture change.
|
|
75
|
-
|
|
76
|
-
## References
|
|
77
|
-
|
|
78
|
-
- PLAN §3 — "sasl (mechanisms: PLAIN, EXTERNAL reserved for later with mTLS)"
|
|
79
|
-
- PLAN §9 — SASL mechanism scope
|
|
80
|
-
- TICKET-026 — SASL reducer implementation
|
|
81
|
-
- TICKET-071 — AccountStore wiring into ConnectionActor
|
|
82
|
-
- TICKET-072 — AWS DynamoDB AccountStore (scrypt hashing)
|
|
83
|
-
- TICKET-079 — SASL EXTERNAL: implement or stop advertising (interim decision)
|
|
84
|
-
- TICKET-054 — mTLS support (Phase 7, enables functional EXTERNAL)
|
|
85
|
-
- `packages/irc-core/src/commands/sasl.ts` — `authenticateReducer`
|
|
86
|
-
- `packages/irc-core/src/ports.ts` — `AccountStore` port
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
# ADR-007: Deterministic ports for time and ID generation
|
|
2
|
-
|
|
3
|
-
**Date:** 2025-01-15 (Phase 1)
|
|
4
|
-
**Status:** Accepted
|
|
5
|
-
**Supersedes:** —
|
|
6
|
-
**Superseded by:** —
|
|
7
|
-
|
|
8
|
-
## Context
|
|
9
|
-
|
|
10
|
-
IRC protocol behaviour is time-sensitive: idle/PING timers, WHOIS signon
|
|
11
|
-
time, `server-time` tags (ISO-8601 timestamps on every message), SASL nonce
|
|
12
|
-
generation, chathistory message IDs. A traditional ircd calls `Date.now()`
|
|
13
|
-
and `crypto.randomUUID()` directly in handlers. This makes tests
|
|
14
|
-
non-deterministic: the same input produces different output on every run,
|
|
15
|
-
depending on when the test executed.
|
|
16
|
-
|
|
17
|
-
Non-determinism is fatal for the parametrized contract suite (TICKET-032),
|
|
18
|
-
which runs identical scenarios across in-memory, CF, and AWS runtimes. If a
|
|
19
|
-
reducer embeds `Date.now()`, the expected `@time=` tag changes every
|
|
20
|
-
millisecond and the assertion cannot be written.
|
|
21
|
-
|
|
22
|
-
## Decision
|
|
23
|
-
|
|
24
|
-
Time and ID generation are **injected ports**, not global calls:
|
|
25
|
-
|
|
26
|
-
```ts
|
|
27
|
-
interface Clock {
|
|
28
|
-
now(): number; // epoch milliseconds
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
interface IdFactory {
|
|
32
|
-
nonce(): string; // unique message ID for message-tags/chathistory
|
|
33
|
-
traceId(): string; // per-request trace ID for observability
|
|
34
|
-
}
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Both are passed through `Ctx` to every reducer. Reducers read `ctx.clock.now()`
|
|
38
|
-
and `ctx.ids.nonce()` — never `Date.now()` or `Math.random()`.
|
|
39
|
-
|
|
40
|
-
Concrete implementations:
|
|
41
|
-
- **`FakeClock`** (tests): returns a fixed value set at construction.
|
|
42
|
-
Deterministic: `new FakeClock(10_000)` always reports `now() === 10_000`.
|
|
43
|
-
- **`SystemClock`** (production): wraps `Date.now()`.
|
|
44
|
-
- **`SequentialIdFactory`** (tests): returns `id-0`, `id-1`, `id-2`, …
|
|
45
|
-
Deterministic: the same sequence of calls produces the same IDs.
|
|
46
|
-
- **`RandomIdFactory`** (production): wraps `crypto.randomUUID()`.
|
|
47
|
-
|
|
48
|
-
## Consequences
|
|
49
|
-
|
|
50
|
-
**Positive:**
|
|
51
|
-
- Reducer tests are fully deterministic. The same `(state, message, ctx)`
|
|
52
|
-
triple always produces the same `{ state, effects }`. No flaky tests.
|
|
53
|
-
- The contract suite can assert exact wire output including `@time=` tags
|
|
54
|
-
and `msgid=` tags, because the fake clock and ID factory are pinned.
|
|
55
|
-
- Time travel is trivial in tests: advance the `FakeClock` between messages
|
|
56
|
-
to test idle/PING logic, flood-control refill, and chathistory bounds.
|
|
57
|
-
- The reducer purity contract (ADR-001) is enforceable: a reducer that calls
|
|
58
|
-
`Date.now()` is simply not using the injected port, and the test will fail
|
|
59
|
-
because the output is wrong.
|
|
60
|
-
|
|
61
|
-
**Negative:**
|
|
62
|
-
- Every call site that needs time or an ID must thread `ctx` — there is no
|
|
63
|
-
shortcut. This is a small constant cost per reducer.
|
|
64
|
-
- The `Clock` and `IdFactory` must be consistent across the actor's lifetime.
|
|
65
|
-
The actor pins them at construction; switching mid-connection would
|
|
66
|
-
produce inconsistent timestamps. This is enforced by the actor's readonly
|
|
67
|
-
fields.
|
|
68
|
-
- `Date.now()` resolution is milliseconds; sub-millisecond ordering is not
|
|
69
|
-
possible with this port. This has not been a problem for IRC (message
|
|
70
|
-
ordering is preserved by connection/channel authority serialization, not
|
|
71
|
-
by timestamp).
|
|
72
|
-
|
|
73
|
-
## References
|
|
74
|
-
|
|
75
|
-
- PLAN §9 — "Time/IDs: injected ports (Clock, IdFactory) for deterministic
|
|
76
|
-
tests"
|
|
77
|
-
- PLAN §8 — "Determinism rules: inject a Clock and deterministic ID/nonce
|
|
78
|
-
ports. No real timers/random in reducers."
|
|
79
|
-
- ADR-001 — pure reducers (why determinism matters)
|
|
80
|
-
- `packages/irc-core/src/ports.ts` — `Clock`, `IdFactory`, `FakeClock`,
|
|
81
|
-
`SequentialIdFactory`, `SystemClock`, `RandomIdFactory`
|
|
82
|
-
- `packages/irc-core/src/types.ts` — `Ctx.clock`, `Ctx.ids`
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
# ADR-008: Monorepo and tooling stack
|
|
2
|
-
|
|
3
|
-
**Date:** 2025-01-10 (Phase 0)
|
|
4
|
-
**Status:** Accepted
|
|
5
|
-
**Supersedes:** —
|
|
6
|
-
**Superseded by:** —
|
|
7
|
-
|
|
8
|
-
## Context
|
|
9
|
-
|
|
10
|
-
ServerlessIRCd is a multi-package TypeScript project: a platform-agnostic
|
|
11
|
-
core (`irc-core`), an orchestration layer (`irc-server`), a reference runtime
|
|
12
|
-
(`in-memory-runtime`), two cloud adapters (`cf-adapter`, `aws-adapter`), and
|
|
13
|
-
several apps (`local-cli`, `cf-worker`, `aws-stack`). The toolchain must
|
|
14
|
-
support cross-package type checking, incremental builds, a unified test
|
|
15
|
-
runner, and CI that enforces coverage gates per package.
|
|
16
|
-
|
|
17
|
-
## Decision
|
|
18
|
-
|
|
19
|
-
| Concern | Choice | Rationale |
|
|
20
|
-
|---|---|---|
|
|
21
|
-
| Package manager | **pnpm** (workspaces) | Fast, disk-efficient, first-class monorepo support |
|
|
22
|
-
| Build orchestration | **turbo** (caching) | Incremental builds, remote cache, task dependency graph |
|
|
23
|
-
| TypeScript | Project references + `tsconfig.base.json` | Strict mode, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess` |
|
|
24
|
-
| Test runner | **vitest** | ESM-native, fast, integrates with Workers (`vitest-pool-workers`), built-in coverage |
|
|
25
|
-
| Coverage | v8 provider, gate ≥90% (100% on `irc-core`) | Enforced in CI (`.github/workflows/ci.yml`) |
|
|
26
|
-
| Lint/format | **Biome** | Single tool (replaces ESLint + Prettier), fast, zero config drift |
|
|
27
|
-
| CF bundler | **wrangler** (esbuild internally) | Native Workers toolchain |
|
|
28
|
-
| AWS bundler | **esbuild** (Lambda bundles) + **CDK v2** (infra) | Tree-shaking, fast cold starts |
|
|
29
|
-
| Runtime target | Node 20 (Lambda) + latest Workers runtime; ES2022+ | Aligns with platform-supported runtimes |
|
|
30
|
-
|
|
31
|
-
## Consequences
|
|
32
|
-
|
|
33
|
-
**Positive:**
|
|
34
|
-
- pnpm workspaces + turbo give fast local dev: only changed packages rebuild;
|
|
35
|
-
tests run incrementally.
|
|
36
|
-
- vitest's ESM-native design avoids the CJS/ESM interop pain that plagues
|
|
37
|
-
Jest-based monorepos. The same test files run under `vitest-pool-workers`
|
|
38
|
-
(real `workerd`) for CF adapter tests.
|
|
39
|
-
- Biome eliminates the ESLint/Prettier config drift problem — one tool, one
|
|
40
|
-
config, one command (`biome check`).
|
|
41
|
-
- TypeScript project references enforce package boundaries at compile time:
|
|
42
|
-
`irc-core` cannot accidentally import from `cf-adapter`.
|
|
43
|
-
|
|
44
|
-
**Negative:**
|
|
45
|
-
- pnpm's strict node-linker can surprise contributors used to npm/yarn's
|
|
46
|
-
hoisting. `node_modules` is per-package; global scripts need
|
|
47
|
-
`pnpm --filter`.
|
|
48
|
-
- turbo's caching requires consistent environment hashing; a CI cache miss
|
|
49
|
-
(e.g. different Node minor version) degrades to a full rebuild silently.
|
|
50
|
-
- Biome is newer than ESLint and lacks some ecosystem plugins. Custom rules
|
|
51
|
-
(e.g. "no ticket IDs in source files") are enforced via convention
|
|
52
|
-
(`AGENTS.md`) rather than a Biome plugin.
|
|
53
|
-
|
|
54
|
-
## References
|
|
55
|
-
|
|
56
|
-
- PLAN §9 — "Monorepo: pnpm + TypeScript project references; turbo for
|
|
57
|
-
caching"
|
|
58
|
-
- `pnpm-workspace.yaml`, `turbo.json`, `tsconfig.base.json`, `biome.json`
|
|
59
|
-
- `.github/workflows/ci.yml` — coverage gates and CI pipeline
|
|
60
|
-
- `AGENTS.md` — code style conventions
|