Skip to content

fix(core): trusted standard-format route handlers get the public single-arg RouteContext#2136

Open
dchaudhari7177 wants to merge 6 commits into
emdash-cms:mainfrom
dchaudhari7177:fix/trusted-route-single-arg
Open

fix(core): trusted standard-format route handlers get the public single-arg RouteContext#2136
dchaudhari7177 wants to merge 6 commits into
emdash-cms:mainfrom
dchaudhari7177:fix/trusted-route-single-arg

Conversation

@dchaudhari7177

Copy link
Copy Markdown
Contributor

What does this PR do?

adaptSandboxEntry (the in-process adapter behind plugins: []) always invoked route handlers with the sandboxed two-arg (routeCtx, pluginCtx) convention. A standard-format plugin authored against the public, exported, type-checked single-arg RouteContext API silently received routeCtx as its only argument (JS drops the extra parameter for a one-param function), so ctx.storage, ctx.email, ctx.kv and every other capability surface were undefined — any route touching more than input/request/requestMeta threw TypeError: Cannot read properties of undefined.

The fix keys off the format signal the codebase already documents (define-plugin.ts): definePlugin requires id, and sandbox-format default exports never carry one (identity comes from the manifest's slug + publisher). When the definition has an id, the wrapper passes the incoming RouteContext through unchanged (full PluginContext + input + real WHATWG Request + requestMeta, exactly what the public contract promises). Sandbox-format entries keep the existing two-arg flattened convention, byte for byte.

This covers both trusted paths: config plugins: [somePlugin()] and marketplace bundles built by plugin-cli (which bundle the plugin's own definePlugin entry, so the id survives).

Closes #2079

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Fable 5 (Claude Code)

Screenshots / test output

New tests: a definePlugin-shaped definition's handler receives the full RouteContext as its only argument (same object identity, real Request, storage reachable); a sandbox-format definition (no id) still gets the flattened two-arg convention. Full plugin unit suite: 622/622 pass (tests/unit/plugins, 30 files). Prettier clean.

…-arg RouteContext

adaptSandboxEntry always invoked route handlers with the sandboxed
two-arg (routeCtx, pluginCtx) convention. A standard-format plugin
authored against the exported single-arg RouteContext API silently got
routeCtx as its only argument (JS drops the extra parameter), so
ctx.storage / ctx.email / ctx.kv and every other capability surface
were undefined and any route touching them threw at runtime.

The definition's id is the format signal — definePlugin requires it and
sandbox-format default exports never carry one (identity comes from the
manifest), the same rule define-plugin.ts documents. Standard-format
handlers now receive the incoming RouteContext unchanged; sandbox-format
entries keep the two-arg flattened convention.

Closes emdash-cms#2079
Copilot AI review requested due to automatic review settings July 19, 2026 14:10
@changeset-bot

changeset-bot Bot commented Jul 19, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: a15b352

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 17 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/plugin-mcp-smoke Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added area/core size/M review/awaiting-author Reviewed; waiting on the author to respond cla: signed labels Jul 19, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 19, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@2136

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@2136

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@2136

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@2136

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@2136

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@2136

emdash

npm i https://pkg.pr.new/emdash@2136

create-emdash

npm i https://pkg.pr.new/create-emdash@2136

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@2136

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@2136

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@2136

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@2136

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@2136

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@2136

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@2136

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@2136

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@2136

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@2136

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@2136

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@2136

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@2136

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@2136

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@2136

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@2136

commit: a15b352

@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/awaiting-author Reviewed; waiting on the author to respond labels Jul 19, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Jul 20, 2026
@ascorbic ascorbic added the bot:review Trigger an emdashbot code review on this PR label Jul 21, 2026

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the right fix for the right problem. adaptSandboxEntry was written assuming every standard/trusted plugin default export used the sandboxed two-arg route convention, so definePlugin-authored plugins placed in plugins: [] silently received only the route-specific slice of RouteContext and blew up on ctx.storage, ctx.email, etc. The PR keys the dispatch off the same structural signal the codebase already uses (definePlugin requires id; sandbox default exports never carry one), which is consistent with define-plugin.ts and avoids changing the public API.

I checked:

  • The diff and full changed files in packages/core/src/plugins/adapt-sandbox-entry.ts and the unit tests.
  • Call-sites in packages/core/src/emdash-runtime.ts and packages/core/src/astro/integration/virtual-modules.ts — the adapter is used for both plugins: [] trusted installs and marketplace/debug-bypass trusted loads, so the same format-detection benefits both paths.
  • The plugin-cli build pipeline (packages/plugin-cli/src/build/pipeline.ts) — ProbedDefaultSchema is a looseObject over { hooks, routes }, so a definePlugin(...) shaped default export (which returns a ResolvedPlugin containing id) is not rejected at bundle time, and its id survives into the runtime artifact handled by adaptSandboxEntry.
  • packages/core/src/plugins/routes.ts — the runtime router always calls route.handler(routeContext) with a single RouteContext; the adapter’s public-format passthrough is exactly what the public contract promises.
  • AGENTS.md conventions: no SQL, no new content-table queries, no admin UI strings, no migrations, and a proper user-facing changeset under the published package name (emdash).

The added tests cover the regression directly: a definePlugin-shaped definition receives the full RouteContext as its only argument (same object identity, real Request, capabilities reachable), while a sandbox-format definition still gets the flattened two-arg convention. Existing behavior for sandbox-format entries is preserved byte-for-byte.

No blocking issues found.

@emdashbot emdashbot Bot removed the bot:review Trigger an emdashbot code review on this PR label Jul 21, 2026
@github-actions github-actions Bot added review/approved Approved; no new commits since and removed review/needs-rereview Author pushed changes since the last review labels Jul 21, 2026
@ascorbic
ascorbic enabled auto-merge (squash) July 21, 2026 06:26
github-actions Bot pushed a commit that referenced this pull request Jul 21, 2026
@ascorbic
ascorbic disabled auto-merge July 21, 2026 06:36
ascorbic and others added 2 commits July 21, 2026 17:40
The id check cast `definition` to `{ id?: unknown }`, which trips
oxlint's no-unsafe-type-assertion under --deny-warnings and failed Lint.
Use the `in` operator instead — it narrows without an assertion and
reads the same.
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review and removed review/approved Approved; no new commits since labels Jul 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core cla: signed review/needs-rereview Author pushed changes since the last review size/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Trusted-mode (plugins: []) route adapter calls standard-format handlers with the old two-arg signature, breaking the public single-arg RouteContext API

3 participants