From 9b2e9625de4388b520710fe23583ab3a07d98c42 Mon Sep 17 00:00:00 2001 From: bimsonz Date: Sat, 18 Jul 2026 16:41:28 +0100 Subject: [PATCH 1/5] Expose Astro's trailingSlash config to plugins via ctx.site Plugins that build absolute URLs (sitemap, canonical, hreflang) need to match the site's URL convention, but ctx.site only carried name/url/locale, so plugins had to hardcode a trailing slash. That's wrong for a site configured with trailingSlash: 'never' -- notably a headless front-end that serves bare URLs, where the plugin's sitemap/canonical then advertise URLs the site doesn't serve. Capture astroConfig.trailingSlash at astro:config:setup, carry it on virtual:emdash/config, and surface it as ctx.site.trailingSlash -- the same path the i18n config already rides. Optional on SiteInfo; createSiteInfo defaults it to 'ignore' (Astro's default) so existing construction is unaffected. --- packages/core/src/astro/integration/index.ts | 3 +++ packages/core/src/emdash-runtime.ts | 14 +++++++++----- packages/core/src/plugins/context.ts | 3 +++ packages/core/src/plugins/types.ts | 8 ++++++++ packages/core/src/virtual-modules.d.ts | 1 + .../tests/integration/plugins/capabilities.test.ts | 3 +++ 6 files changed, 27 insertions(+), 5 deletions(-) diff --git a/packages/core/src/astro/integration/index.ts b/packages/core/src/astro/integration/index.ts index 96798766e9..4a15b0464b 100644 --- a/packages/core/src/astro/integration/index.ts +++ b/packages/core/src/astro/integration/index.ts @@ -384,6 +384,9 @@ export function emdash(config: EmDashConfig = {}): AstroIntegration { // EmDashHead must not access Astro.csp when the host has disabled // Astro's built-in CSP runtime; Astro logs a warning for that access. serializableConfig.astroCspEnabled = Boolean(astroConfig.security.csp); + // Expose Astro's trailingSlash routing policy so plugins can build + // URLs (sitemap/canonical/hreflang) that match what the site serves. + serializableConfig.trailingSlash = astroConfig.trailingSlash; // Extract i18n config from Astro config // Astro locales can be strings OR { path, codes } objects — normalize to paths if (astroConfig.i18n) { diff --git a/packages/core/src/emdash-runtime.ts b/packages/core/src/emdash-runtime.ts index b550d446cb..719b1fd6f1 100644 --- a/packages/core/src/emdash-runtime.ts +++ b/packages/core/src/emdash-runtime.ts @@ -352,7 +352,7 @@ export interface EmDashRuntimeParts { db: Kysely; getDb?: () => Kysely; storage?: Storage; - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string }; + siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }; }; runtimeDeps: RuntimeDependencies; pipelineRef: { current: HookPipeline }; @@ -534,7 +534,7 @@ export class EmDashRuntime { db: Kysely; getDb?: () => Kysely; storage?: Storage; - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string }; + siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }; }; /** Dependencies needed for exclusive hook resolution */ private runtimeDeps: RuntimeDependencies; @@ -1106,7 +1106,7 @@ export class EmDashRuntime { const storage = EmDashRuntime.getStorage(deps); let pluginStates: Map = new Map(); - let siteInfo: { siteName?: string; siteUrl?: string; locale?: string } | undefined; + let siteInfo: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" } | undefined; // "Already set up" by default so a read failure (e.g. tables absent on a // pre-migration db) skips seeding rather than seeding a half-built db. let seedGate = { collectionCount: 1, setupDone: true }; @@ -1149,6 +1149,10 @@ export class EmDashRuntime { siteName: siteOpts.get("emdash:site_title") ?? undefined, siteUrl: siteOpts.get("emdash:site_url") ?? undefined, locale: siteOpts.get("emdash:locale") ?? undefined, + // trailingSlash is a build-time Astro routing decision, not a + // user-editable setting, so it comes from the Astro config + // (virtual:emdash/config), not the options table. + trailingSlash: virtualConfig?.trailingSlash, }; }; @@ -1831,7 +1835,7 @@ export class EmDashRuntime { deps: RuntimeDependencies, db: Kysely, mediaStorage?: Storage | null, - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string }, + siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }, ): Promise> { // Return cached plugins if already loaded if (sandboxedPluginCache.size > 0) { @@ -1946,7 +1950,7 @@ export class EmDashRuntime { storage: Storage, deps: RuntimeDependencies, cache: Map, - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string }, + siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }, ): Promise { // Ensure sandbox runner exists with media storage wired up. // (storage here is the media Storage adapter from the runtime.) diff --git a/packages/core/src/plugins/context.ts b/packages/core/src/plugins/context.ts index 8945a94748..233a6e16a5 100644 --- a/packages/core/src/plugins/context.ts +++ b/packages/core/src/plugins/context.ts @@ -882,6 +882,8 @@ export interface SiteInfoOptions { siteUrl?: string; /** Site locale from options table */ locale?: string; + /** Astro's `trailingSlash` config (from `virtual:emdash/config`). */ + trailingSlash?: "always" | "never" | "ignore"; } /** @@ -897,6 +899,7 @@ export function createSiteInfo(options: SiteInfoOptions): SiteInfo { name: options.siteName ?? "", url: (options.siteUrl ?? "").replace(TRAILING_SLASH_RE, ""), // strip trailing slash locale: options.locale ?? "en", + trailingSlash: options.trailingSlash ?? "ignore", // Astro's default }; } diff --git a/packages/core/src/plugins/types.ts b/packages/core/src/plugins/types.ts index 2ce3bbaefe..8154c9aec0 100644 --- a/packages/core/src/plugins/types.ts +++ b/packages/core/src/plugins/types.ts @@ -419,6 +419,14 @@ export interface SiteInfo { url: string; /** Site locale (from settings, defaults to "en") */ locale: string; + /** + * Astro's `trailingSlash` routing policy, from the host's Astro config. + * Plugins that build absolute URLs (sitemap, canonical, hreflang) should + * honor this so the URLs they emit match what the site serves. `createSiteInfo` + * always populates it (defaulting to `"ignore"`, Astro's default); it is + * optional on the type so pre-existing `SiteInfo` construction stays valid. + */ + trailingSlash?: "always" | "never" | "ignore"; } /** diff --git a/packages/core/src/virtual-modules.d.ts b/packages/core/src/virtual-modules.d.ts index 6f4ddd4a7a..9985e87f60 100644 --- a/packages/core/src/virtual-modules.d.ts +++ b/packages/core/src/virtual-modules.d.ts @@ -22,6 +22,7 @@ declare module "virtual:emdash/config" { i18n?: I18nConfig | null; toolbar?: "server" | "client" | false; astroCspEnabled?: boolean; + trailingSlash?: "always" | "never" | "ignore"; } const config: VirtualConfig; diff --git a/packages/core/tests/integration/plugins/capabilities.test.ts b/packages/core/tests/integration/plugins/capabilities.test.ts index 287493f196..0981c29a55 100644 --- a/packages/core/tests/integration/plugins/capabilities.test.ts +++ b/packages/core/tests/integration/plugins/capabilities.test.ts @@ -999,11 +999,13 @@ describe("Capability Enforcement Integration (v2)", () => { siteName: "My Site", siteUrl: "https://example.com/", locale: "fr", + trailingSlash: "never", }); expect(info.name).toBe("My Site"); expect(info.url).toBe("https://example.com"); // trailing slash stripped expect(info.locale).toBe("fr"); + expect(info.trailingSlash).toBe("never"); }); it("uses defaults for missing values", () => { @@ -1012,6 +1014,7 @@ describe("Capability Enforcement Integration (v2)", () => { expect(info.name).toBe(""); expect(info.url).toBe(""); expect(info.locale).toBe("en"); + expect(info.trailingSlash).toBe("ignore"); // Astro's default }); it("strips trailing slash from URL", () => { From 93ab7d8caecdf8496c5c971be2d7adda2bc72942 Mon Sep 17 00:00:00 2001 From: bimsonz Date: Sat, 18 Jul 2026 19:36:18 +0100 Subject: [PATCH 2/5] Type trailingSlash on the sandbox site payload + add changeset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Review follow-up (#2122): createSiteInfo already flows the full SiteInfo — including trailingSlash — to sandboxed plugins via the runner options, matching the documented 'same normalized site context used by trusted plugin hooks and routes' parity. Make that explicit by adding trailingSlash to the sandbox site-shape types (core + the workerd/cloudflare wrappers) so the wire format is typed, not silently widened. Add the changeset for the published emdash change. --- .changeset/expose-trailing-slash.md | 5 +++++ packages/cloudflare/src/sandbox/runner.ts | 6 +++--- packages/cloudflare/src/sandbox/wrapper.ts | 2 +- packages/core/src/plugins/sandbox/types.ts | 2 +- packages/workerd/src/sandbox/dev-runner.ts | 2 +- packages/workerd/src/sandbox/runner.ts | 2 +- packages/workerd/src/sandbox/wrapper.ts | 2 +- 7 files changed, 13 insertions(+), 8 deletions(-) create mode 100644 .changeset/expose-trailing-slash.md diff --git a/.changeset/expose-trailing-slash.md b/.changeset/expose-trailing-slash.md new file mode 100644 index 0000000000..3dc9ae0e08 --- /dev/null +++ b/.changeset/expose-trailing-slash.md @@ -0,0 +1,5 @@ +--- +"emdash": minor +--- + +Expose the host Astro project's `trailingSlash` config to plugins via `ctx.site.trailingSlash`, so plugins that build absolute URLs (sitemaps, canonical, hreflang) can match the site's routing policy. Available to in-process and sandboxed plugins alike. diff --git a/packages/cloudflare/src/sandbox/runner.ts b/packages/cloudflare/src/sandbox/runner.ts index d7c8f381ec..14a2ce882c 100644 --- a/packages/cloudflare/src/sandbox/runner.ts +++ b/packages/cloudflare/src/sandbox/runner.ts @@ -102,7 +102,7 @@ export class CloudflareSandboxRunner implements SandboxRunner { private plugins = new Map(); private options: SandboxOptions; private resolvedLimits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string }; + private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; constructor(options: SandboxOptions) { this.options = options; @@ -203,7 +203,7 @@ class CloudflareSandboxedPlugin implements SandboxedPluginInstance { private code: string; private wrapperCode: string | null = null; private limits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string }; + private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; constructor( manifest: PluginManifest, @@ -211,7 +211,7 @@ class CloudflareSandboxedPlugin implements SandboxedPluginInstance { loader: WorkerLoader, createBridge: (opts: { props: PluginBridgeProps }) => PluginBridgeBinding, limits: ResolvedLimits, - siteInfo?: { name: string; url: string; locale: string }, + siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }, ) { this.id = `${manifest.id}:${manifest.version}`; this.manifest = manifest; diff --git a/packages/cloudflare/src/sandbox/wrapper.ts b/packages/cloudflare/src/sandbox/wrapper.ts index fae6d00877..97338c4949 100644 --- a/packages/cloudflare/src/sandbox/wrapper.ts +++ b/packages/cloudflare/src/sandbox/wrapper.ts @@ -27,7 +27,7 @@ const COMMENT_CLOSE_RE = /\*\//g; */ export interface WrapperOptions { /** Site info to inject into the context (no RPC needed) */ - site?: { name: string; url: string; locale: string }; + site?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; } export function generatePluginWrapper(manifest: PluginManifest, options?: WrapperOptions): string { diff --git a/packages/core/src/plugins/sandbox/types.ts b/packages/core/src/plugins/sandbox/types.ts index e95545353d..e14710e5bf 100644 --- a/packages/core/src/plugins/sandbox/types.ts +++ b/packages/core/src/plugins/sandbox/types.ts @@ -72,7 +72,7 @@ export interface SandboxOptions { /** Default resource limits */ limits?: ResourceLimits; /** Site info for plugin context (injected into wrapper at generation time) */ - siteInfo?: { name: string; url: string; locale: string }; + siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; /** Email send callback, wired from the EmailPipeline by the runtime */ emailSend?: SandboxEmailSendCallback; /** diff --git a/packages/workerd/src/sandbox/dev-runner.ts b/packages/workerd/src/sandbox/dev-runner.ts index 3ecacedee6..c72fff91e8 100644 --- a/packages/workerd/src/sandbox/dev-runner.ts +++ b/packages/workerd/src/sandbox/dev-runner.ts @@ -51,7 +51,7 @@ const EMDASH_SHIM = "export const definePlugin = (d) => d;\n"; */ export class MiniflareDevRunner implements SandboxRunner { private options: SandboxOptions; - private siteInfo?: { name: string; url: string; locale: string }; + private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; private emailSendCallback: SandboxEmailSendCallback | null = null; /** Miniflare instance (lazily created) */ diff --git a/packages/workerd/src/sandbox/runner.ts b/packages/workerd/src/sandbox/runner.ts index f5e1a48bed..6404c9d622 100644 --- a/packages/workerd/src/sandbox/runner.ts +++ b/packages/workerd/src/sandbox/runner.ts @@ -292,7 +292,7 @@ function isTokenClaims(value: unknown): value is PluginTokenClaims { export class WorkerdSandboxRunner implements SandboxRunner { private options: SandboxOptions; private limits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string }; + private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; /** Loaded plugins indexed by pluginId (manifest.id:manifest.version) */ private plugins = new Map(); diff --git a/packages/workerd/src/sandbox/wrapper.ts b/packages/workerd/src/sandbox/wrapper.ts index ce2fd9b26d..f6db08195d 100644 --- a/packages/workerd/src/sandbox/wrapper.ts +++ b/packages/workerd/src/sandbox/wrapper.ts @@ -18,7 +18,7 @@ const NEWLINE_RE = /[\n\r]/g; const COMMENT_CLOSE_RE = /\*\//g; export interface WrapperOptions { - site?: { name: string; url: string; locale: string }; + site?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; /** URL of the Node backing service (e.g., http://127.0.0.1:18787) */ backingServiceUrl: string; /** Auth token the plugin sends on outbound bridge calls to Node */ From 864a58ab358595afabc11812d305dd3d00dcb413 Mon Sep 17 00:00:00 2001 From: bimsonz Date: Sun, 19 Jul 2026 00:48:34 +0100 Subject: [PATCH 3/5] changeset: declare @emdash-cms/cloudflare + @emdash-cms/sandbox-workerd The sandbox runners/wrappers forward trailingSlash into generated worker code, so both published packages change behavior and must be released alongside emdash core. --- .changeset/expose-trailing-slash.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.changeset/expose-trailing-slash.md b/.changeset/expose-trailing-slash.md index 3dc9ae0e08..ef03849042 100644 --- a/.changeset/expose-trailing-slash.md +++ b/.changeset/expose-trailing-slash.md @@ -1,5 +1,7 @@ --- "emdash": minor +"@emdash-cms/cloudflare": minor +"@emdash-cms/sandbox-workerd": minor --- Expose the host Astro project's `trailingSlash` config to plugins via `ctx.site.trailingSlash`, so plugins that build absolute URLs (sitemaps, canonical, hreflang) can match the site's routing policy. Available to in-process and sandboxed plugins alike. From 675f53135ca69f2f7f3d3720b6ecb98a7c4fb8ea Mon Sep 17 00:00:00 2001 From: bimsonz Date: Sun, 19 Jul 2026 13:55:24 +0100 Subject: [PATCH 4/5] test: expect trailingSlash in normalized siteInfo createSiteInfo now always populates trailingSlash (default "ignore"), so the strict siteInfo expectations must include it: sandbox-runner-options (toEqual x2), create.test.ts (objectContaining on the sandbox-runner siteInfo), and the plugin-route-site-info integration test (end-to-end). Verified by running the full core suite locally (4753 passing). --- packages/core/tests/integration/runtime/create.test.ts | 1 + .../tests/integration/runtime/plugin-route-site-info.test.ts | 1 + .../core/tests/unit/plugins/sandbox-runner-options.test.ts | 3 ++- 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/core/tests/integration/runtime/create.test.ts b/packages/core/tests/integration/runtime/create.test.ts index 9bea7ac7af..65b249174f 100644 --- a/packages/core/tests/integration/runtime/create.test.ts +++ b/packages/core/tests/integration/runtime/create.test.ts @@ -150,6 +150,7 @@ describe("EmDashRuntime.create — cold boot", () => { name: "Example Site", url: "https://example.com", locale: "nl", + trailingSlash: "ignore", }, }), ); diff --git a/packages/core/tests/integration/runtime/plugin-route-site-info.test.ts b/packages/core/tests/integration/runtime/plugin-route-site-info.test.ts index 112ffe9c0c..dee591136a 100644 --- a/packages/core/tests/integration/runtime/plugin-route-site-info.test.ts +++ b/packages/core/tests/integration/runtime/plugin-route-site-info.test.ts @@ -83,6 +83,7 @@ describe("EmDashRuntime.handlePluginApiRoute site context", () => { name: "Example Site", url: "https://example.com", locale: "nl", + trailingSlash: "ignore", }, url: "https://example.com/checkout/success", }, diff --git a/packages/core/tests/unit/plugins/sandbox-runner-options.test.ts b/packages/core/tests/unit/plugins/sandbox-runner-options.test.ts index b241468a1f..6720ae6ceb 100644 --- a/packages/core/tests/unit/plugins/sandbox-runner-options.test.ts +++ b/packages/core/tests/unit/plugins/sandbox-runner-options.test.ts @@ -20,6 +20,7 @@ describe("createSandboxRunnerOptions", () => { name: "Example Site", url: "https://example.com", locale: "nl", + trailingSlash: "ignore", }, }); }); @@ -33,6 +34,6 @@ describe("createSandboxRunnerOptions", () => { expect(options.db).toBe(db); expect(options.mediaStorage).toBe(mediaStorage); - expect(options.siteInfo).toEqual({ name: "", url: "", locale: "en" }); + expect(options.siteInfo).toEqual({ name: "", url: "", locale: "en", trailingSlash: "ignore" }); }); }); From b1d12283466229752aedf4a7a73fdf185bcd1797 Mon Sep 17 00:00:00 2001 From: "emdashbot[bot]" Date: Mon, 20 Jul 2026 19:18:32 +0000 Subject: [PATCH 5/5] style: format --- packages/cloudflare/src/sandbox/runner.ts | 21 ++++++++++-- packages/cloudflare/src/sandbox/wrapper.ts | 7 +++- packages/core/src/emdash-runtime.ts | 37 +++++++++++++++++++--- packages/core/src/plugins/sandbox/types.ts | 7 +++- packages/workerd/src/sandbox/dev-runner.ts | 7 +++- packages/workerd/src/sandbox/runner.ts | 7 +++- packages/workerd/src/sandbox/wrapper.ts | 7 +++- 7 files changed, 80 insertions(+), 13 deletions(-) diff --git a/packages/cloudflare/src/sandbox/runner.ts b/packages/cloudflare/src/sandbox/runner.ts index 14a2ce882c..8dd780c7dc 100644 --- a/packages/cloudflare/src/sandbox/runner.ts +++ b/packages/cloudflare/src/sandbox/runner.ts @@ -102,7 +102,12 @@ export class CloudflareSandboxRunner implements SandboxRunner { private plugins = new Map(); private options: SandboxOptions; private resolvedLimits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + private siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; constructor(options: SandboxOptions) { this.options = options; @@ -203,7 +208,12 @@ class CloudflareSandboxedPlugin implements SandboxedPluginInstance { private code: string; private wrapperCode: string | null = null; private limits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + private siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; constructor( manifest: PluginManifest, @@ -211,7 +221,12 @@ class CloudflareSandboxedPlugin implements SandboxedPluginInstance { loader: WorkerLoader, createBridge: (opts: { props: PluginBridgeProps }) => PluginBridgeBinding, limits: ResolvedLimits, - siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }, + siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }, ) { this.id = `${manifest.id}:${manifest.version}`; this.manifest = manifest; diff --git a/packages/cloudflare/src/sandbox/wrapper.ts b/packages/cloudflare/src/sandbox/wrapper.ts index 97338c4949..320c3752e7 100644 --- a/packages/cloudflare/src/sandbox/wrapper.ts +++ b/packages/cloudflare/src/sandbox/wrapper.ts @@ -27,7 +27,12 @@ const COMMENT_CLOSE_RE = /\*\//g; */ export interface WrapperOptions { /** Site info to inject into the context (no RPC needed) */ - site?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + site?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; } export function generatePluginWrapper(manifest: PluginManifest, options?: WrapperOptions): string { diff --git a/packages/core/src/emdash-runtime.ts b/packages/core/src/emdash-runtime.ts index 1d4e6bf908..91fb7dcaf3 100644 --- a/packages/core/src/emdash-runtime.ts +++ b/packages/core/src/emdash-runtime.ts @@ -352,7 +352,12 @@ export interface EmDashRuntimeParts { db: Kysely; getDb?: () => Kysely; storage?: Storage; - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }; + siteInfo?: { + siteName?: string; + siteUrl?: string; + locale?: string; + trailingSlash?: "always" | "never" | "ignore"; + }; }; runtimeDeps: RuntimeDependencies; pipelineRef: { current: HookPipeline }; @@ -534,7 +539,12 @@ export class EmDashRuntime { db: Kysely; getDb?: () => Kysely; storage?: Storage; - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }; + siteInfo?: { + siteName?: string; + siteUrl?: string; + locale?: string; + trailingSlash?: "always" | "never" | "ignore"; + }; }; /** Dependencies needed for exclusive hook resolution */ private runtimeDeps: RuntimeDependencies; @@ -1106,7 +1116,14 @@ export class EmDashRuntime { const storage = EmDashRuntime.getStorage(deps); let pluginStates: Map = new Map(); - let siteInfo: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" } | undefined; + let siteInfo: + | { + siteName?: string; + siteUrl?: string; + locale?: string; + trailingSlash?: "always" | "never" | "ignore"; + } + | undefined; // "Already set up" by default so a read failure (e.g. tables absent on a // pre-migration db) skips seeding rather than seeding a half-built db. let seedGate = { collectionCount: 1, setupDone: true }; @@ -1835,7 +1852,12 @@ export class EmDashRuntime { deps: RuntimeDependencies, db: Kysely, mediaStorage?: Storage | null, - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }, + siteInfo?: { + siteName?: string; + siteUrl?: string; + locale?: string; + trailingSlash?: "always" | "never" | "ignore"; + }, ): Promise> { // Return cached plugins if already loaded if (sandboxedPluginCache.size > 0) { @@ -1950,7 +1972,12 @@ export class EmDashRuntime { storage: Storage, deps: RuntimeDependencies, cache: Map, - siteInfo?: { siteName?: string; siteUrl?: string; locale?: string; trailingSlash?: "always" | "never" | "ignore" }, + siteInfo?: { + siteName?: string; + siteUrl?: string; + locale?: string; + trailingSlash?: "always" | "never" | "ignore"; + }, ): Promise { // Ensure sandbox runner exists with media storage wired up. // (storage here is the media Storage adapter from the runtime.) diff --git a/packages/core/src/plugins/sandbox/types.ts b/packages/core/src/plugins/sandbox/types.ts index e14710e5bf..3dfb344d59 100644 --- a/packages/core/src/plugins/sandbox/types.ts +++ b/packages/core/src/plugins/sandbox/types.ts @@ -72,7 +72,12 @@ export interface SandboxOptions { /** Default resource limits */ limits?: ResourceLimits; /** Site info for plugin context (injected into wrapper at generation time) */ - siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; /** Email send callback, wired from the EmailPipeline by the runtime */ emailSend?: SandboxEmailSendCallback; /** diff --git a/packages/workerd/src/sandbox/dev-runner.ts b/packages/workerd/src/sandbox/dev-runner.ts index c72fff91e8..fe1da7039b 100644 --- a/packages/workerd/src/sandbox/dev-runner.ts +++ b/packages/workerd/src/sandbox/dev-runner.ts @@ -51,7 +51,12 @@ const EMDASH_SHIM = "export const definePlugin = (d) => d;\n"; */ export class MiniflareDevRunner implements SandboxRunner { private options: SandboxOptions; - private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + private siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; private emailSendCallback: SandboxEmailSendCallback | null = null; /** Miniflare instance (lazily created) */ diff --git a/packages/workerd/src/sandbox/runner.ts b/packages/workerd/src/sandbox/runner.ts index 6404c9d622..7624485d3e 100644 --- a/packages/workerd/src/sandbox/runner.ts +++ b/packages/workerd/src/sandbox/runner.ts @@ -292,7 +292,12 @@ function isTokenClaims(value: unknown): value is PluginTokenClaims { export class WorkerdSandboxRunner implements SandboxRunner { private options: SandboxOptions; private limits: ResolvedLimits; - private siteInfo?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + private siteInfo?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; /** Loaded plugins indexed by pluginId (manifest.id:manifest.version) */ private plugins = new Map(); diff --git a/packages/workerd/src/sandbox/wrapper.ts b/packages/workerd/src/sandbox/wrapper.ts index f6db08195d..b45be3fde6 100644 --- a/packages/workerd/src/sandbox/wrapper.ts +++ b/packages/workerd/src/sandbox/wrapper.ts @@ -18,7 +18,12 @@ const NEWLINE_RE = /[\n\r]/g; const COMMENT_CLOSE_RE = /\*\//g; export interface WrapperOptions { - site?: { name: string; url: string; locale: string; trailingSlash?: "always" | "never" | "ignore" }; + site?: { + name: string; + url: string; + locale: string; + trailingSlash?: "always" | "never" | "ignore"; + }; /** URL of the Node backing service (e.g., http://127.0.0.1:18787) */ backingServiceUrl: string; /** Auth token the plugin sends on outbound bridge calls to Node */