Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 23 additions & 24 deletions .claude/skills/wdl-deploy/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,31 +53,30 @@ enable flags, `legacy_error_serialization`, and
(`wrangler.json`, then `wrangler.jsonc`, then `wrangler.toml`). Both JSON
filenames use Wrangler's JSONC syntax, including comments and trailing commas.
The control plane is canonical for unsupported runtime shapes such as
unsupported workerd compatibility flags and WDL-reserved injected module
names. The CLI still fails fast for cheap local cases such as Python Workers
modules, unmapped top-level or selected-env Wrangler runtime/deploy keys
(`[site]`, `pages_build_output_dir`, `observability`, `limits`,
`placement`, etc.), and ambiguous runtime `env` name collisions between
`[vars]`, explicit bindings, and the implicit `ASSETS` binding.
For an operator-enabled routed Worker, explicit `workers_dev = false` keeps its
pattern routes active while disabling the default platform-domain URL; it
requires at least one `route` / `routes` pattern and is not inferred.
The deploy summary prints every active route-pattern URL hint, preserving the
trailing `*` on prefix patterns, and includes the platform-domain URL only while
it is enabled. Cloudflare's separate `preview_urls` field is unsupported and
rejected by the CLI.
WDL-only `[[exports]]`, `[[platform_bindings]]`, `[[triggers.schedules]]`, and
unsupported workerd compatibility flags and WDL-reserved injected module names.
The CLI still fails fast for cheap local cases such as Python Workers modules,
unmapped top-level or selected-env Wrangler runtime/deploy keys (`[site]`,
`pages_build_output_dir`, `observability`, `limits`, `placement`, etc.), and
ambiguous runtime `env` name collisions between `[vars]`, explicit bindings, and
the implicit `ASSETS` binding. For an operator-enabled routed Worker, explicit
`workers_dev = false` keeps its pattern routes active while disabling the
default platform-domain URL; it requires at least one `route` / `routes` pattern
and is not inferred. The deploy summary prints every active route-pattern URL
hint, preserving the trailing `*` on prefix patterns, and includes the
platform-domain URL only while it is enabled. Cloudflare's separate
`preview_urls` field is unsupported and rejected by the CLI. WDL-only
`[[exports]]`, `[[platform_bindings]]`, `[[triggers.schedules]]`, and
`[[services]].ns` are parsed by the CLI and removed from Wrangler's temporary
bundle config; other fields retain their existing Wrangler passthrough
behavior. Wrangler's object-shaped declarative `exports` config is unsupported.
The dry-run child hides Wrangler's banner (and its normal update check) and
disables anonymous telemetry. Wrangler may still consult the configured npm
registry when reporting an unknown configuration field; project build hooks
retain their normal network access.
For `[[services]]` and `[[exports]]`, read `docs/deploy.md`: tenant JSRPC may
delegate service or Durable Object class stubs as opaque capabilities, but the
receiver cannot rewrite their host-authored caller properties. Keep delegated
stubs in memory; long-term irrevocable stub storage is unsupported.
bundle config; other fields retain their existing Wrangler passthrough behavior.
Wrangler's object-shaped declarative `exports` config is unsupported. The
dry-run child hides Wrangler's banner (and its normal update check) and disables
anonymous telemetry. Wrangler may still consult the configured npm registry when
reporting an unknown configuration field; project build hooks retain their
normal network access. For `[[services]]` and `[[exports]]`, read
`docs/deploy.md`: tenant JSRPC may delegate service or Durable Object class
stubs as opaque capabilities, but the receiver cannot rewrite their
host-authored caller properties. Keep delegated stubs in memory; long-term
irrevocable stub storage is unsupported.

Never recommend setting `CONTROL_CONNECT_HOST` outside local development: it
overrides the TCP target the admin token connects to (Host header + TLS SNI
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:

- run: npm ci
- run: npm audit --audit-level=moderate
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:

- run: npm ci
- run: npm audit --audit-level=moderate
- run: npm run format:check
- run: npm run lint
- run: npm run typecheck
- run: npm test
Expand Down
22 changes: 22 additions & 0 deletions .prettierrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"embeddedLanguageFormatting": "off",
"printWidth": 120,
"proseWrap": "always",
"quoteProps": "preserve",
"trailingComma": "es5",
"overrides": [
{
"files": "**/*.md",
"options": {
"printWidth": 80
}
},
{
"files": "**/*-zh.md",
"options": {
"printWidth": 80,
"proseWrap": "never"
}
}
]
}
65 changes: 34 additions & 31 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ newer target.

- `npm install`: install dependencies from `package-lock.json`.
- `npm link`: expose the local `wdl` binary for manual testing.
- `npm run format`: format supported source and documentation files.
- `npm run format:check`: verify formatting without modifying files.
- `npm run lint`: run ESLint over the CLI, docs helpers, and examples.
- `npm run typecheck`: run TypeScript's JavaScript-aware `tsc --noEmit` check.
- `npm test`: run Node's built-in test runner against
Expand All @@ -31,9 +33,9 @@ newer target.
configured — the CLI has no default endpoint.

Use Node.js 22 or newer for local work. The GitHub Actions workflow uses Node
22, runs `npm ci`, `npm audit --audit-level=moderate`, `npm run lint`,
`npm run typecheck`, `npm test`, and `npm pack --dry-run`, then checks workflow
syntax with actionlint.
22, runs `npm ci`, `npm audit --audit-level=moderate`, `npm run format:check`,
`npm run lint`, `npm run typecheck`, `npm test`, and `npm pack --dry-run`, then
checks workflow syntax with actionlint.

## Coding Style & Naming Conventions

Expand All @@ -47,14 +49,14 @@ dependency injection for testable command behavior, as seen in
types rather than `any`, and use `unknown` plus narrowing for values validated
at runtime.

Markdown wrapping is bilingual by design, normalized with Prettier
(`--embedded-language-formatting=off`; code blocks are hand-formatted) and kept
by editing habit rather than a linter: English prose hard-wraps at 80 columns
(`--prose-wrap always`), while Chinese prose never hard-wraps inside a sentence
(`--prose-wrap never`, one line per paragraph) because CommonMark renders a soft
break as a space and CJK text would pick up spurious mid-sentence spaces.
Tables, code blocks, and long URLs are exempt; there is no line-length lint,
since table rows would trip it.
Prettier formats supported files with a 120-column source width. Markdown
wrapping is bilingual by design (`--embedded-language-formatting=off`; code
blocks are hand-formatted): English prose hard-wraps at 80 columns
(`--prose-wrap always`), while Chinese `*-zh.md` prose never hard-wraps inside a
sentence (`--prose-wrap never`, one line per paragraph) because CommonMark
renders a soft break as a space and CJK text would pick up spurious mid-sentence
spaces. Tables, code blocks, and long URLs are exempt; run
`npm run format:check` to enforce the repository configuration.

## Testing Guidelines

Expand All @@ -75,26 +77,27 @@ defaults change. The per-feature docs are bilingual pairs — `docs/<name>.md`
(English) and `docs/<name>-zh.md` (Chinese) — and both languages are
authoritative: update the pair in the same change. Agent-facing references
(`templates/AGENTS.md`, the wdl-deploy skill, generated projects) point only at
the English set. Before packaging, re-run the audit, test, and `npm pack
--dry-run` checks from Build, Test, and Development Commands.
the English set. Before packaging, re-run the audit, test, and
`npm pack --dry-run` checks from Build, Test, and Development Commands.

## Release

Releases are tag-driven. `.github/workflows/release.yml` re-runs audit, lint,
typecheck, and tests, verifies the tag matches `version` in `package.json`, and
runs `npm pack --dry-run` — all before any publish, so a broken release fails the
tag's check job and never publishes. It then publishes `@wdl-dev/cli` to npmjs
(with provenance) and to GitHub Packages (authenticated with the workflow's own
`GITHUB_TOKEN`), and creates a GitHub Release for the tag: final releases take
their notes from the matching `CHANGELOG.md` section, pre-releases fall back to
generated notes and are marked Pre-release. Do not run `npm publish` by hand.
Releases are tag-driven. `.github/workflows/release.yml` re-runs formatting,
audit, lint, typecheck, and tests, verifies the tag matches `version` in
`package.json`, and runs `npm pack --dry-run` — all before any publish, so a
broken release fails the tag's check job and never publishes. It then publishes
`@wdl-dev/cli` to npmjs (with provenance) and to GitHub Packages (authenticated
with the workflow's own `GITHUB_TOKEN`), and creates a GitHub Release for the
tag: final releases take their notes from the matching `CHANGELOG.md` section,
pre-releases fall back to generated notes and are marked Pre-release. Do not run
`npm publish` by hand.

Published npm versions are immutable (no reuse; unpublish only within 72 hours),
but the check job gates every publish, so most releases tag the final version
directly. This project ships documented breaking removals in 1.x minors (called
out in the CHANGELOG) — do not hold or re-version a release for generic SemVer
reasons. Stage a pre-release only for the narrower risk an RC actually guards:
the *published artifact* differing from what the check job validated — packaging
the _published artifact_ differing from what the check job validated — packaging
changes (the `files` allowlist, entry points, the bundle/publish pipeline) or a
large release you want to smoke-test as a real `@next` install. For an RC, set
`version` to e.g. `2.0.0-rc.1`, write the CHANGELOG entry, commit, and tag
Expand Down Expand Up @@ -128,15 +131,15 @@ drops a `.env`-supplied endpoint when the effective token is not from the same
`.env`), then the global token store (`~/.config/wdl/credentials`, managed by
`wdl token`). The store is trusted (home directory, same-source token +
endpoint) and not subject to the guard; a project `.env` is not. The namespace
itself follows the same shape — `--ns > shell WDL_NS > project .env WDL_NS >
store default (base WDL_NS)` — so the store's default namespace is the lowest
selector, materialized into `env.WDL_NS` before the per-key gap-fill. Keep that
ordering and the guard intact when touching `loadCliControlEnv` or
`lib/token-store.js`; `--no-token-store` / `WDL_TOKEN_STORE=off` (via
`tokenStoreReader`, read from the process env, not a project `.env`) must keep
opting the store out of resolution entirely. Do not commit tenant tokens or
generated secrets; read credentials from the environment and keep example
configuration generic.
itself follows the same shape —
`--ns > shell WDL_NS > project .env WDL_NS > store default (base WDL_NS)` — so
the store's default namespace is the lowest selector, materialized into
`env.WDL_NS` before the per-key gap-fill. Keep that ordering and the guard
intact when touching `loadCliControlEnv` or `lib/token-store.js`;
`--no-token-store` / `WDL_TOKEN_STORE=off` (via `tokenStoreReader`, read from
the process env, not a project `.env`) must keep opting the store out of
resolution entirely. Do not commit tenant tokens or generated secrets; read
credentials from the environment and keep example configuration generic.

### Deploy runs project code as you

Expand Down
71 changes: 35 additions & 36 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@

### Added

- Routed Workers with at least one custom route can set `workers_dev = false`
to disable their platform-domain URL. Deploy summaries print the active
platform URL and route-pattern URL hints, omitting the disabled URL.
- Routed Workers with at least one custom route can set `workers_dev = false` to
disable their platform-domain URL. Deploy summaries print the active platform
URL and route-pattern URL hints, omitting the disabled URL.

## 1.5.1

### Security

- Bump the bundled `wrangler` to 4.114.0, which vendors a patched sharp
(0.35.2) and clears a high-severity libvips advisory (GHSA-f88m-g3jw-g9cj)
reachable only through the miniflare dev server, which the CLI never runs.
- Bump the bundled `wrangler` to 4.114.0, which vendors a patched sharp (0.35.2)
and clears a high-severity libvips advisory (GHSA-f88m-g3jw-g9cj) reachable
only through the miniflare dev server, which the CLI never runs.

## 1.5.0

Expand All @@ -41,11 +41,10 @@

### Fixed

- `wdl deploy` no longer passes WDL-only `[[exports]]`,
`[[platform_bindings]]`, `[[triggers.schedules]]`, or `[[services]].ns` to
Wrangler's temporary bundle config. This avoids the `exports` schema
collision in Wrangler 4.107+ while preserving the fields in the WDL deploy
manifest.
- `wdl deploy` no longer passes WDL-only `[[exports]]`, `[[platform_bindings]]`,
`[[triggers.schedules]]`, or `[[services]].ns` to Wrangler's temporary bundle
config. This avoids the `exports` schema collision in Wrangler 4.107+ while
preserving the fields in the WDL deploy manifest.

## 1.4.0

Expand All @@ -54,8 +53,8 @@
- `wdl deploy` now selects `wrangler.json`, then `wrangler.jsonc`, then
`wrangler.toml`, parses both JSON filenames as JSONC, and resolves Wrangler
from explicit/project/package/`PATH` sources (`npx` remains opt-in). It
rejects Python modules, unmapped runtime fields, and binding collisions
before upload; runtime/workerd bundle policy remains canonical in control.
rejects Python modules, unmapped runtime fields, and binding collisions before
upload; runtime/workerd bundle policy remains canonical in control.
- New projects use `compatibility_date = "2026-06-17"` unless a feature requires
newer behavior.
- Control requests now identify the CLI version, cap buffered JSON responses at
Expand All @@ -73,17 +72,17 @@

### Fixed

- Control connection failures, invalid 2xx JSON, unreadable project `.env` or
D1 input files, and unexpected arguments now use escaped CLI diagnostics
instead of raw Node errors or silently ignored input.
- Control connection failures, invalid 2xx JSON, unreadable project `.env` or D1
input files, and unexpected arguments now use escaped CLI diagnostics instead
of raw Node errors or silently ignored input.
- Deploy failures now retain control warnings and actionable secret,
environment-budget, and caller-secret guidance.
- D1 migrations reject symlinked SQL files, invalid `.assetsignore` patterns
identify the offending rule, and project `.env` parsing tolerates unrelated
dotenv extensions.
- Secret mutation failures explain environment-budget, contention, and
envelope errors, including when no mutation was written; token list safely
renders stored labels and endpoints.
- Secret mutation failures explain environment-budget, contention, and envelope
errors, including when no mutation was written; token list safely renders
stored labels and endpoints.

### Security

Expand All @@ -103,14 +102,14 @@

### Changed

- `wdl d1 execute` now requires exactly one SQL source (`--sql` or `--file`)
and rejects empty SQL locally before contacting control. Even `--sql ""`
conflicts with `--file`.
- `wdl d1 execute` now requires exactly one SQL source (`--sql` or `--file`) and
rejects empty SQL locally before contacting control. Even `--sql ""` conflicts
with `--file`.
- `wdl deploy` now rejects more malformed Wrangler config locally instead of
silently dropping invalid input: non-array `kv_namespaces`, malformed KV
entries, present-but-non-table `[assets]`, and several validated fields that
previously reached the manifest with loose types. Wrangler local-dev KV
fields `preview_id` and `remote` remain allowed but are ignored by deploy.
previously reached the manifest with loose types. Wrangler local-dev KV fields
`preview_id` and `remote` remain allowed but are ignored by deploy.

## 1.2.2

Expand All @@ -120,8 +119,8 @@
world-writable store directory: a 0600 file there can still be deleted,
replaced, or symlink-swapped by another user, so the write fails with a
`chmod 700` hint instead. POSIX only.
- `wdl r2 object head --json` parses custom metadata without prototype
pollution — an `x-amz-meta-__proto__` header stays an own key, and an empty
- `wdl r2 object head --json` parses custom metadata without prototype pollution
— an `x-amz-meta-__proto__` header stays an own key, and an empty
`x-amz-meta-` header is dropped.

### Changed
Expand All @@ -140,8 +139,8 @@
dropped.
- A local deploy prints a direct `http://<ns>.<domain>:8080/<worker>/` URL
instead of a `curl -H 'Host: ...'` hint, and a control plane reached via a
`.test` / `.local` host is recognized as local (previously only
localhost / 127.0.0.1).
`.test` / `.local` host is recognized as local (previously only localhost /
127.0.0.1).
- KV docs (GUIDE and `docs/kv.md`) describe the 512-byte key / list-prefix cap
the platform now enforces; they previously said it was not checked. Documented
that `wdl d1 execute --mode exec` takes no `--params`.
Expand Down Expand Up @@ -182,9 +181,9 @@
values already were. A malicious or compromised control plane could put
terminal control bytes (ESC / OSC / C1) in a JSON error property name and have
them written unescaped to stderr (OSC 52 clipboard writes, display spoofing).
- Control-plane responses now abort the connection when the body exceeds the
10 MiB cap, instead of rejecting the result while continuing to read the
stream — the cap bounds resource use, not just the returned value.
- Control-plane responses now abort the connection when the body exceeds the 10
MiB cap, instead of rejecting the result while continuing to read the stream —
the cap bounds resource use, not just the returned value.
- The trusted-publishing release job pins the npm CLI to an exact reviewed
version instead of installing `npm@latest`, so a compromised npm release can't
run in the job that holds the npm OIDC token and publish a tampered,
Expand Down Expand Up @@ -225,8 +224,8 @@

### Changed

- `wdl init`'s `--ns` is now optional. With `--ns`, the scaffolded `npm run
deploy` keeps `wdl deploy . --ns <ns>`; without it the script is
- `wdl init`'s `--ns` is now optional. With `--ns`, the scaffolded
`npm run deploy` keeps `wdl deploy . --ns <ns>`; without it the script is
`wdl deploy .` and the namespace is resolved at deploy time (`--ns` / `WDL_NS`
/ project `.env` / a `wdl token` default). `init` also no longer autoloads
control credentials, so a corrupt token store cannot block scaffolding.
Expand All @@ -244,9 +243,9 @@
- `wdl secret put` no longer echoes the typed secret on a TTY: input is read in
raw mode (hidden), and fails closed — it errors rather than echo if the
terminal cannot hide input.
- `.env` values containing literal backslash escape sequences (e.g. a token
with a backslash followed by `n`) now round-trip correctly instead of being
decoded as control characters.
- `.env` values containing literal backslash escape sequences (e.g. a token with
a backslash followed by `n`) now round-trip correctly instead of being decoded
as control characters.

### Security

Expand Down
4 changes: 3 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,11 @@ reaching the terminal**.

## Checks

All of these run in CI on every pull request and must pass:
Run `npm run format` to apply the repository formatting rules. The following
checks run in CI on every pull request and must pass:

```bash
npm run format:check
npm run lint
npm run typecheck
npm test
Expand Down
Loading