diff --git a/packages/miniflare/src/index.ts b/packages/miniflare/src/index.ts index a6f330551f4f7..4a520679c8db4 100644 --- a/packages/miniflare/src/index.ts +++ b/packages/miniflare/src/index.ts @@ -43,7 +43,7 @@ import { } from "./plugins"; import { JsonErrorSchema, - SourceOptions, + NameSourceOptions, getUserServiceName, handlePrettyErrorRequest, reviveError, @@ -517,8 +517,8 @@ export class Miniflare { } } - get #workerSrcOpts(): SourceOptions[] { - return this.#workerOpts.map(({ core }) => core); + get #workerSrcOpts(): NameSourceOptions[] { + return this.#workerOpts.map(({ core }) => core); } #handleLoopback = async ( diff --git a/packages/miniflare/src/plugins/core/constants.ts b/packages/miniflare/src/plugins/core/constants.ts new file mode 100644 index 0000000000000..e73d69aec8af3 --- /dev/null +++ b/packages/miniflare/src/plugins/core/constants.ts @@ -0,0 +1,23 @@ +export const CORE_PLUGIN_NAME = "core"; + +// Service for HTTP socket entrypoint (for checking runtime ready, routing, etc) +export const SERVICE_ENTRY = `${CORE_PLUGIN_NAME}:entry`; +// Service prefix for all regular user workers +const SERVICE_USER_PREFIX = `${CORE_PLUGIN_NAME}:user`; +// Service prefix for `workerd`'s builtin services (network, external, disk) +const SERVICE_BUILTIN_PREFIX = `${CORE_PLUGIN_NAME}:builtin`; +// Service prefix for custom fetch functions defined in `serviceBindings` option +const SERVICE_CUSTOM_PREFIX = `${CORE_PLUGIN_NAME}:custom`; + +export function getUserServiceName(workerName = "") { + return `${SERVICE_USER_PREFIX}:${workerName}`; +} +export function getBuiltinServiceName( + workerIndex: number, + bindingName: string +) { + return `${SERVICE_BUILTIN_PREFIX}:${workerIndex}:${bindingName}`; +} +export function getCustomServiceName(workerIndex: number, bindingName: string) { + return `${SERVICE_CUSTOM_PREFIX}:${workerIndex}:${bindingName}`; +} diff --git a/packages/miniflare/src/plugins/core/errors/index.ts b/packages/miniflare/src/plugins/core/errors/index.ts index 8367d52058b32..ba1b03c20ccf6 100644 --- a/packages/miniflare/src/plugins/core/errors/index.ts +++ b/packages/miniflare/src/plugins/core/errors/index.ts @@ -4,6 +4,7 @@ import type { UrlAndMap } from "source-map-support"; import { z } from "zod"; import { Request, Response } from "../../../http"; import { Log } from "../../../shared"; +import { getUserServiceName } from "../constants"; import { SourceOptions, contentsToString, @@ -13,33 +14,31 @@ import { getSourceMapper } from "./sourcemap"; // Subset of core worker options that define Worker source code. // These are the possible cases, and corresponding reported source files in -// workerd stack traces. Note that all service-worker scripts will be called -// "worker.js" in `workerd`, so we can't differentiate between multiple workers. -// TODO: see if we can add a service-worker script path config option to handle -// case (i)[i] +// workerd stack traces. // // Single Worker: -// (a) { script: "" } -> "worker.js" -// (b) { script: "", modules: true } -> "" -// (c) { script: "", scriptPath: "" } -> "worker.js" -// (d) { script: "", scriptPath: "", modules: true } -> "" -// (e) { scriptPath: "" } -> "worker.js" -// (f) { scriptPath: "", modules: true } -> "" +// (a) { script: "" } -> "core:user:" +// (b) { script: "", modules: true } -> "" +// (c) { script: "", scriptPath: "" } -> "core:user:" +// (d) { script: "", scriptPath: "", modules: true } -> "" +// (e) { scriptPath: "" } -> "core:user:" +// (f) { scriptPath: "", modules: true } -> "" // (g) { modules: [ -// [i] { ..., path: "", contents: "" }, -> "" relative to cwd -// [ii] { ..., path: "" }, -> "" relative to cwd +// [i] { ..., path: "", contents: "" }, -> "" relative to cwd +// [ii] { ..., path: "" }, -> "" relative to cwd // ] } // (h) { modulesRoot: "", modules: [ -// [i] { ..., path: "", contents: "" }, -> "" relative to "" -// [ii] { ..., path: "" }, -> "" relative to "" +// [i] { ..., path: "", contents: "" }, -> "" relative to "" +// [ii] { ..., path: "" }, -> "" relative to "" // ] } // // Multiple Workers (array of `SourceOptions`): -// (i) [ (note cannot differentiate "worker.js"s) -// [i] { script: "" }, -> "worker.js" -// { script: "" }, -> "worker.js" -// { script: "", scriptPath: "" }, -> "worker.js" -// [ii] { script: "", modules: true }, -> "" +// (i) [ +// [i] { script: "" }, -> "core:user:" +// [ii] { name: "a", script: "" }, -> "core:user:a" +// [iii] { name: "b", script: "", scriptPath: "" }, -> "core:user:b" +// [iv] { name: "c", scriptPath: "" }, -> "core:user:c" +// [v] { script: "", modules: true }, -> "" // ] // @@ -59,11 +58,13 @@ function maybeGetDiskFile(filePath: string): SourceFile | undefined { } } +export type NameSourceOptions = SourceOptions & { name?: string }; + // Try to extract the path and contents of a `file` reported in a JavaScript // stack-trace. See the `SourceOptions` comment for examples of what these look // like. function maybeGetFile( - workerSrcOpts: SourceOptions[], + workerSrcOpts: NameSourceOptions[], file: string ): SourceFile | undefined { // Resolve file relative to current working directory @@ -111,7 +112,7 @@ function maybeGetFile( } } - // Cases: (b), (i)[ii] + // Cases: (b), (i)[v] // 3. If file looks like "", and the `n`th worker has a custom // `script`, use that. const workerIndex = maybeGetStringScriptPathIndex(file); @@ -122,22 +123,15 @@ function maybeGetFile( } } - // Cases: (a), (c), (e) - // 4. If there is a single worker defined with `modules` disabled, the - // file is "worker.js", then... - // - // Note: can't handle case (i)[i], as cannot distinguish between multiple - // "worker.js"s, hence the check for a single worker. We'd rather be - // conservative and return no contents (and therefore no source code in the - // error page) over incorrect ones. - if (workerSrcOpts.length === 1) { - const srcOpts = workerSrcOpts[0]; + // Cases: (a), (c), (e), (i)[i], (i)[ii], (i)[iii], (i)[iv] + // 4. If `file` is the name of a service, use that services' source. + for (const srcOpts of workerSrcOpts) { if ( - file === "worker.js" && + file === getUserServiceName(srcOpts.name) && (srcOpts.modules === undefined || srcOpts.modules === false) ) { if ("script" in srcOpts && srcOpts.script !== undefined) { - // Cases: (a), (c) + // Cases: (a), (c), (i)[i], (i)[ii], (i)[iii] // ...if a custom `script` is defined, use that, with the defined // `scriptPath` if any (Case (c)) return { @@ -148,7 +142,7 @@ function maybeGetFile( contents: srcOpts.script, }; } else if (srcOpts.scriptPath !== undefined) { - // Case: (e) + // Case: (e), (i)[iv] // ...otherwise, if a `scriptPath` is defined, use that return maybeGetDiskFile(path.resolve(srcOpts.scriptPath)); } @@ -160,7 +154,10 @@ function maybeGetFile( return maybeGetDiskFile(filePath); } -function getSourceMappedStack(workerSrcOpts: SourceOptions[], error: Error) { +function getSourceMappedStack( + workerSrcOpts: NameSourceOptions[], + error: Error +) { // This function needs to match the signature of the `retrieveSourceMap` // option from the "source-map-support" package. function retrieveSourceMap(file: string): UrlAndMap | null { @@ -220,7 +217,7 @@ const ALLOWED_ERROR_SUBCLASS_CONSTRUCTORS: StandardErrorConstructor[] = [ URIError, ]; export function reviveError( - workerSrcOpts: SourceOptions[], + workerSrcOpts: NameSourceOptions[], jsonError: JsonError ): Error { // At a high level, this function takes a JSON-serialisable representation of @@ -262,7 +259,7 @@ export function reviveError( export async function handlePrettyErrorRequest( log: Log, - workerSrcOpts: SourceOptions[], + workerSrcOpts: NameSourceOptions[], request: Request ): Promise { // Parse and validate the error we've been given from user code diff --git a/packages/miniflare/src/plugins/core/index.ts b/packages/miniflare/src/plugins/core/index.ts index 13f22e85e9654..0637cf2c6f6bf 100644 --- a/packages/miniflare/src/plugins/core/index.ts +++ b/packages/miniflare/src/plugins/core/index.ts @@ -29,6 +29,12 @@ import { WORKER_BINDING_SERVICE_LOOPBACK, parseRoutes, } from "../shared"; +import { + SERVICE_ENTRY, + getBuiltinServiceName, + getCustomServiceName, + getUserServiceName, +} from "./constants"; import { ModuleLocator, SourceOptions, @@ -80,25 +86,6 @@ export const CoreSharedOptionsSchema = z.object({ export const CORE_PLUGIN_NAME = "core"; -// Service for HTTP socket entrypoint (for checking runtime ready, routing, etc) -export const SERVICE_ENTRY = `${CORE_PLUGIN_NAME}:entry`; -// Service prefix for all regular user workers -const SERVICE_USER_PREFIX = `${CORE_PLUGIN_NAME}:user`; -// Service prefix for `workerd`'s builtin services (network, external, disk) -const SERVICE_BUILTIN_PREFIX = `${CORE_PLUGIN_NAME}:builtin`; -// Service prefix for custom fetch functions defined in `serviceBindings` option -const SERVICE_CUSTOM_PREFIX = `${CORE_PLUGIN_NAME}:custom`; - -export function getUserServiceName(workerName = "") { - return `${SERVICE_USER_PREFIX}:${workerName}`; -} -function getBuiltinServiceName(workerIndex: number, bindingName: string) { - return `${SERVICE_BUILTIN_PREFIX}:${workerIndex}:${bindingName}`; -} -function getCustomServiceName(workerIndex: number, bindingName: string) { - return `${SERVICE_CUSTOM_PREFIX}:${workerIndex}:${bindingName}`; -} - const LIVE_RELOAD_SCRIPT_TEMPLATE = ( port: number ) => `