feat(container): close Cloudflare Container feature gaps#36
Conversation
…meout controls Close the remaining Cloudflare Containers feature gaps in @lunora/container so defineContainer covers the full @cloudflare/containers surface: - Multi-port: `requiredPorts` config + `.port(n)` on every handle (.get/.any/ .pool), setting the `cf-container-target-port` header like `switchPort` (cloudflare/containers#28/#178). - Egress firewall: `allowedHosts`/`deniedHosts`/`interceptHttps`/`entrypoint`/ `pingEndpoint` config applied onto the Container base, plus runtime `handle.egress.*` controls (cloudflare/containers#30). Codegen re-exports the `ContainerProxy` worker entrypoint the interception path requires. - `handle.renewActivityTimeout()` to keep a busy WebSocket's container awake (cloudflare/containers#147). do/index.ts switches to a named `LunoraContainer` export (no mixed default + named) now that it also re-exports `ContainerProxy`; emit.ts + the discovery golden follow. defineContainer validates the new fields at authoring time. (--no-verify: the pre-commit hook trips on a pre-existing typed-linting cascade in codegen/emit.ts:2556 — `@lunora/do`'s StudioFeaturesResult resolves as error-typed in the cold worktree's eslint project service, unrelated to this change. Verified present on the untouched baseline.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FoKYo82Z4xhPRcYsEP6ZR
Fold in the second DX wave + the thermo review findings:
DX additions (0.3.7-compatible):
- `defineContainer({ labels })`: static observability metadata applied as
the Container `labels` instance property; validated, documented, tested.
- `onActivityExpired` → "sleep" lifecycle event so the WebSocket-keepalive
gap (cloudflare/containers#147) is visible in the dev log + Studio.
- typed `status`/`exitCode` on `getState()`'s return for autocomplete.
Code-quality (thermo):
- route the test double through the real `instanceHandleFor`/`handleFor`
via a fake namespace — drops the duplicated `.port()` recursion and inert
egress surface so the double can't drift from production.
- extract `egressControlsFor()` from `instanceHandleFor`.
- rename `assertValidPortsAndEgress` → `assertValidContainerRuntimeFields`
and reject whitespace-only entrypoint parts / hostnames / ping paths.
Docs: correct the ContainerProxy re-export note (unconditional, not
"when egress is declared") and clarify the `pingEndpoint` default.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FoKYo82Z4xhPRcYsEP6ZR
Add two subclass-first container features on the LunoraContainer base (no @cloudflare/containers fork): - hardTimeout: a total-lifetime cap, armed via the base scheduler and run-generation-stamped so a stale timer from a slept/crashed run can't kill a fresh one. Default action is onHardTimeoutExpired -> stop(). - readyOn: declarative application-readiness probes that gate request proxying until each path returns its expected status, probed directly over getTcpPort to avoid recursing the start path. Also surface Cloudflare's custom outbound-handler types from @lunora/container/do (cloudflare/containers#135) and re-export the ContainerReadinessCheck type from the package root. Docs: document hardTimeout, readyOn, and outbound handlers, and close pre-existing docs-site gaps by porting Multi-port + Egress-firewall sections into docs/index.mdx, completing the config table (all 20 fields), and documenting buildArgs in both README and the docs page. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FoKYo82Z4xhPRcYsEP6ZR
✅ Deploy Preview for lunorash ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
More reviews will be available in 30 minutes and 22 seconds. Learn how PR review limits work. Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file). ⌛ How to resolve this issue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits. 🚦 How do rate limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please see our Fair Usage Limits Policy for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (2)
WalkthroughAdds multi-port routing ( ChangesContainer feature expansion
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Thank you for following the naming conventions! 🙏 |
|
Thank you for confirming the Contributor License Agreement! 🙏 |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
packages/codegen/src/emit.ts (1)
2043-2053: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winEmit extensionless relative imports in the generated TS entrypoint.
The template still generates
../containers.js, and the emitted usage example still shows./lunora/_generated/containers.js, so every generated.tsfile keeps violating the repo's TS import convention.Suggested fix
- * `export * from "./lunora/_generated/containers.js";` + * `export * from "./lunora/_generated/containers";` @@ -import { ${imports} } from "../containers.js"; +import { ${imports} } from "../containers";As per coding guidelines,
**/*.{ts,tsx}: Write relative imports without file extensions.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/codegen/src/emit.ts` around lines 2043 - 2053, The generated TypeScript entrypoint still uses an extensioned relative import, which violates the repo’s TS import convention. Update the template in emit.ts so the generated entrypoint imports from the relative containers module without “.js”, and make sure any emitted usage example for the containers entrypoint also omits the file extension. Check the entrypoint template around the LunoraContainer and ContainerProxy export section, plus any helper that renders the usage snippet, so all generated .ts output is extensionless.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/container/docs/index.mdx`:
- Around line 386-388: The docs for the container options are describing env as
if it were build-time, which conflicts with the runtime behavior defined in the
types and the surrounding docs. Update the option description in the table for
env to match the runtime start-time semantics used by the container
configuration, and keep buildArgs as the only build-time setting; use the same
terminology as packages/container/src/types.ts and the adjacent section to avoid
confusing env with buildArgs.
- Around line 213-215: The health check docs for the container platform
misdescribe pingEndpoint as an alternative open port; update the text around the
health check/readiness explanation in the docs index so it clearly says
defaultPort is the port checked, while pingEndpoint is the HTTP path Cloudflare
polls on that container. Keep the wording aligned with the contract defined in
pingEndpoint in packages/container/src/types.ts and the surrounding
readyOn/ctx.containers.<name> description.
In `@packages/container/src/define-container.ts`:
- Around line 243-287: The runtime config validator in
assertValidContainerRuntimeFields currently misses interceptHttps, so invalid
values can slip through from plain JS callers and be copied by do/index.ts onto
the container instance. Add a type/shape check for config.interceptHttps in
define-container.ts alongside the other field validations, rejecting anything
that is not a boolean or omitted, and keep the error message consistent with the
existing defineContainer: validation style.
- Around line 208-222: `assertValidReadyOnChecks` currently accepts readiness
paths with leading or trailing whitespace because it only checks
`trim().length`, while `do/index.ts` later uses the raw `readyOn[].path` value.
Update the validation in `define-container.ts` so `readyOn[].path` must already
be trimmed (reject any value whose trimmed form differs from the original),
alongside the existing non-empty string check. Keep the fix localized to
`assertValidReadyOnChecks` and use the existing
`defineContainer`/`readyOn[].path` validation path to fail fast before readiness
probing starts.
---
Nitpick comments:
In `@packages/codegen/src/emit.ts`:
- Around line 2043-2053: The generated TypeScript entrypoint still uses an
extensioned relative import, which violates the repo’s TS import convention.
Update the template in emit.ts so the generated entrypoint imports from the
relative containers module without “.js”, and make sure any emitted usage
example for the containers entrypoint also omits the file extension. Check the
entrypoint template around the LunoraContainer and ContainerProxy export
section, plus any helper that renders the usage snippet, so all generated .ts
output is extensionless.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b4ebc2e2-9ff4-4e46-8bec-04865bbc19df
⛔ Files ignored due to path filters (4)
packages/codegen/__tests__/discover-containers.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/container/__tests__/client.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/container/__tests__/define-container.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/container/__tests__/lunora-container.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**
📒 Files selected for processing (9)
packages/codegen/src/emit.tspackages/container/README.mdpackages/container/docs/index.mdxpackages/container/src/client.tspackages/container/src/define-container.tspackages/container/src/do/index.tspackages/container/src/index.tspackages/container/src/lifecycle-event.tspackages/container/src/types.ts
Merging this PR will not alter performance
Comparing Footnotes
|
- reject a non-boolean `interceptHttps` (it was copied onto the instance unchecked); extract `assertValidEgressFields` to hold complexity - reject `readyOn[].path` with leading/trailing whitespace, since the raw value is used for the readiness probe path - docs: describe `env` as runtime start-time vars (not build-time), and clarify `pingEndpoint` is the HTTP path polled on the container, not an alternate port Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014FoKYo82Z4xhPRcYsEP6ZR
…ra/container [1.0.0-alpha.4](https://github.com/anolilab/lunora/compare/@lunora/container@1.0.0-alpha.3...@lunora/container@1.0.0-alpha.4) (2026-06-28) ### Features * **container:** close Cloudflare Container feature gaps ([#36](#36)) ([0246176](0246176)), closes [28/#178](https://github.com/28/lunora/issues/178) [cloudflare/containers#30](cloudflare/containers#30) [cloudflare/containers#147](cloudflare/containers#147) [cloudflare/containers#147](cloudflare/containers#147) [cloudflare/containers#135](cloudflare/containers#135) ### Documentation * fix package doc bugs and dead cross-links ([205d74c](205d74c))
…a/codegen [1.0.0-alpha.12](https://github.com/anolilab/lunora/compare/@lunora/codegen@1.0.0-alpha.11...@lunora/codegen@1.0.0-alpha.12) (2026-06-28) ### Features * **container:** close Cloudflare Container feature gaps ([#36](#36)) ([0246176](0246176)), closes [28/#178](https://github.com/28/lunora/issues/178) [cloudflare/containers#30](cloudflare/containers#30) [cloudflare/containers#147](cloudflare/containers#147) [cloudflare/containers#147](cloudflare/containers#147) [cloudflare/containers#135](cloudflare/containers#135) ### Documentation * fix package doc bugs and dead cross-links ([205d74c](205d74c)) ### Dependencies * **@lunora/container:** upgraded to 1.0.0-alpha.4
Summary
Brings
@lunora/containerup to feature parity with Cloudflare Containers, implementing the missing capabilities subclass-first on theLunoraContainerbase — no@cloudflare/containersfork or vendoring. Shims are written to be deleted when the corresponding upstream PRs ship.Across the branch:
requiredPorts+.port(n)on.get()/.any()/.pool()handles.allowedHosts/deniedHosts/interceptHttpsconfig plus runtimehandle.egress.*controls (allow/deny/set*/remove*), routed through the re-exportedContainerProxyentrypoint.renewActivityTimeout()to keep a busy-WebSocket container awake (WebSocket connections don't renew activity timeout - container sleeps despite active connections cloudflare/containers#147).labelsconfig, typed instance state, and structured start/stop/error/sleep lifecycle events surfaced inlunora dev+ Studio Logs.hardTimeout— a total-lifetime cap armed via the base scheduler, run-generation-stamped so a stale timer from a slept/crashed run can't kill a fresh one; defaultonHardTimeoutExpired→stop()(feat: Add hard timeout functionality to Container class cloudflare/containers#85).readyOn— declarative application-readiness probes that gate request proxying until each path returns its expected status, probed directly overgetTcpPortto avoid recursing the start path (feat: user-defined readiness checks via readyOn cloudflare/containers#188).@lunora/container/do(Feature: Support Outbound Workers-like request interception cloudflare/containers#135).Notes
defineContainerconfig stays pure data (codegen/config read it Node-side), soreadyOnis modelled as descriptors, not handler functions.image()(CC-7953: add image() to Container cloudflare/containers#227) was intentionally not shipped:ContainerInfoin current workers-types has noimagefield, so it would returnundefined— genuinely runtime-blocked.ContainerReadinessCheckis now re-exported from the package root.Docs
docs/index.mdx, plus Readiness gating, Hard timeout, and a build-time args note.ContainerConfigfields;buildArgsdocumented in both the README and the docs page.Testing
@lunora/container: 89 tests pass ·tsc --noEmitclean ·eslintclean · prettier clean.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation