diff --git a/.changeset/consolidate-miniflare-persist-options.md b/.changeset/consolidate-miniflare-persist-options.md
new file mode 100644
index 00000000000..b822197f473
--- /dev/null
+++ b/.changeset/consolidate-miniflare-persist-options.md
@@ -0,0 +1,20 @@
+---
+"miniflare": major
+---
+
+Consolidate persistence and temporary directory options
+
+The per-resource persistence options (`kvPersist`, `r2Persist`, `d1Persist`, `cachePersist`, `durableObjectsPersist`, `workflowsPersist`, `secretsStorePersist`, `analyticsEngineDatasetsPersist`, `streamPersist`, `imagesPersist`, and `helloWorldPersist`) have been removed. The `Miniflare.unsafeGetPersistPaths()` method, which provided the per-resource persistence paths, has also been removed as they can now be stably inferred from the base path.
+
+For consistency and clarity, `defaultPersistRoot` and `defaultProjectTmpPath` have been renamed to `resourcePersistencePath` and `resourceTmpPath`, respectively.
+
+For example:
+
+```js
+new Miniflare({
+ resourcePersistencePath: ".wrangler/state/v3",
+ resourceTmpPath: ".wrangler/tmp",
+});
+```
+
+When `resourcePersistencePath` is set, each resource persists to a subdirectory named after its plugin (e.g. `.wrangler/state/v3/kv`). When it is omitted, resources are ephemeral and their data is cleared on dispose.
diff --git a/.changeset/drop-cache-warn-usage.md b/.changeset/drop-cache-warn-usage.md
new file mode 100644
index 00000000000..fad1959bc42
--- /dev/null
+++ b/.changeset/drop-cache-warn-usage.md
@@ -0,0 +1,7 @@
+---
+"miniflare": major
+---
+
+Remove the `cacheWarnUsage` option
+
+The `cacheWarnUsage` Worker option, which logged a warning when cache operations were used, has been removed.
diff --git a/.changeset/drop-fetch-mock.md b/.changeset/drop-fetch-mock.md
new file mode 100644
index 00000000000..899ea332b12
--- /dev/null
+++ b/.changeset/drop-fetch-mock.md
@@ -0,0 +1,5 @@
+---
+"miniflare": major
+---
+
+Remove the `fetchMock` option and `createFetchMock` export
diff --git a/.changeset/drop-https-key-cert-path.md b/.changeset/drop-https-key-cert-path.md
new file mode 100644
index 00000000000..89854346f76
--- /dev/null
+++ b/.changeset/drop-https-key-cert-path.md
@@ -0,0 +1,7 @@
+---
+"miniflare": major
+---
+
+Remove the `httpsKeyPath` and `httpsCertPath` options
+
+The `httpsKeyPath` and `httpsCertPath` options have been removed. To use a custom certificate, read the files and pass their contents via the existing `httpsKey` and `httpsCert` options.
diff --git a/.changeset/drop-live-reload-endpoint.md b/.changeset/drop-live-reload-endpoint.md
new file mode 100644
index 00000000000..2112cc607c9
--- /dev/null
+++ b/.changeset/drop-live-reload-endpoint.md
@@ -0,0 +1,7 @@
+---
+"miniflare": major
+---
+
+Drop `/cdn-cgi/mf/reload` live reload endpoint and `liveReload` option
+
+The built-in live reload mechanism has been removed from Miniflare. This included a WebSocket endpoint at `/cdn-cgi/mf/reload`, the `liveReload` option, and the automatic injection of a live reload ``;
-
export const SCRIPT_CUSTOM_FETCH_SERVICE = `addEventListener("fetch", (event) => {
const request = new Request(event.request);
request.headers.set("${CoreHeaders.CUSTOM_FETCH_SERVICE}", ${CoreBindings.TEXT_CUSTOM_SERVICE});
@@ -597,18 +523,6 @@ function getDevControlBindings(
return Array.from(bindings.values());
}
-const WRAPPED_MODULE_PREFIX = "miniflare-internal:wrapped:";
-function workerNameToWrappedModule(workerName: string): string {
- return WRAPPED_MODULE_PREFIX + workerName;
-}
-export function maybeWrappedModuleToWorkerName(
- name: string
-): string | undefined {
- if (name.startsWith(WRAPPED_MODULE_PREFIX)) {
- return name.substring(WRAPPED_MODULE_PREFIX.length);
- }
-}
-
function getOutboundInterceptorName(workerIndex: number) {
return `outbound:${workerIndex}`;
}
@@ -683,29 +597,6 @@ export const CORE_PLUGIN: Plugin<
})
);
}
- if (options.wrappedBindings !== undefined) {
- bindings.push(
- ...Object.entries(options.wrappedBindings).map(([name, designator]) => {
- // Normalise designator
- const isObject = typeof designator === "object";
- const scriptName = isObject ? designator.scriptName : designator;
- const entrypoint = isObject ? designator.entrypoint : undefined;
- const bindings = isObject ? designator.bindings : undefined;
-
- // Build binding
- const moduleName = workerNameToWrappedModule(scriptName);
- const innerBindings =
- bindings === undefined ? [] : buildBindings(bindings);
- // `scriptName`'s bindings will be added to `innerBindings` when
- // assembling the config
- return {
- name,
- wrapped: { moduleName, entrypoint, innerBindings },
- };
- })
- );
- }
-
if (options.unsafeEvalBinding !== undefined) {
bindings.push({
name: options.unsafeEvalBinding,
@@ -761,15 +652,6 @@ export const CORE_PLUGIN: Plugin<
])
);
}
- if (options.wrappedBindings !== undefined) {
- bindingEntries.push(
- ...Object.keys(options.wrappedBindings).map((name) => [
- name,
- new ProxyNodeBinding(),
- ])
- );
- }
-
return Object.fromEntries(await Promise.all(bindingEntries));
},
async getServices({
@@ -778,7 +660,6 @@ export const CORE_PLUGIN: Plugin<
sharedOptions,
workerBindings,
workerIndex,
- wrappedBindingNames,
durableObjectClassNames,
additionalModules,
loopbackHost,
@@ -851,119 +732,75 @@ export const CORE_PLUGIN: Plugin<
options.compatibilityDate ?? FALLBACK_COMPATIBILITY_DATE
);
- const isWrappedBinding = wrappedBindingNames.has(name);
-
const services: Service[] = [];
const extensions: Extension[] = [];
- if (isWrappedBinding) {
- const stringName = JSON.stringify(name);
- function invalidWrapped(reason: string): never {
- const message = `Cannot use ${stringName} for wrapped binding because ${reason}`;
- throw new MiniflareCoreError("ERR_INVALID_WRAPPED", message);
- }
- if (workerIndex === 0) {
- invalidWrapped(
- `it's the entrypoint.\nEnsure ${stringName} isn't the first entry in the \`workers\` array.`
- );
- }
- if (!("modules" in workerScript)) {
- invalidWrapped(
- `it's a service worker.\nEnsure ${stringName} sets \`modules\` to \`true\` or an array of modules`
- );
- }
- if (workerScript.modules.length !== 1) {
- invalidWrapped(
- `it isn't a single module.\nEnsure ${stringName} doesn't include unbundled \`import\`s.`
- );
- }
- const firstModule = workerScript.modules[0];
- if (!("esModule" in firstModule)) {
- invalidWrapped("it isn't a single ES module");
- }
- if (options.compatibilityDate !== undefined) {
- invalidWrapped(
- "it defines a compatibility date.\nWrapped bindings use the compatibility date of the worker with the binding."
- );
- }
- if (options.compatibilityFlags?.length) {
- invalidWrapped(
- "it defines compatibility flags.\nWrapped bindings use the compatibility flags of the worker with the binding."
- );
- }
- if (options.outboundService !== undefined) {
- invalidWrapped(
- "it defines an outbound service.\nWrapped bindings use the outbound service of the worker with the binding."
- );
- }
- // We validate this "worker" isn't bound to for services/Durable Objects
- // in `getWrappedBindingNames()`.
-
- extensions.push({
- modules: [
- {
- name: workerNameToWrappedModule(name),
- esModule: firstModule.esModule,
- internal: true,
- },
- ],
- });
- } else {
- services.push({
- name: serviceName,
- worker: {
- ...workerScript,
- compatibilityDate,
- compatibilityFlags: options.compatibilityFlags,
- bindings: workerBindings,
- durableObjectNamespaces:
- classNamesEntries.map(
- ([
+ services.push({
+ name: serviceName,
+ worker: {
+ ...workerScript,
+ compatibilityDate,
+ compatibilityFlags: options.compatibilityFlags,
+ bindings: workerBindings,
+ durableObjectNamespaces:
+ classNamesEntries.map(
+ ([
+ className,
+ {
+ enableSql,
+ unsafeUniqueKey,
+ unsafePreventEviction: preventEviction,
+ container,
+ },
+ ]) => {
+ const uniqueKey = getDurableObjectUniqueKey(
className,
- {
- enableSql,
- unsafeUniqueKey,
- unsafePreventEviction: preventEviction,
- container,
- },
- ]) => {
- const uniqueKey = getDurableObjectUniqueKey(
- className,
- options.name,
- unsafeUniqueKey
- );
-
- return uniqueKey === undefined
- ? {
- className,
- enableSql,
- ephemeralLocal: kVoid,
- preventEviction,
- container,
- }
- : {
- className,
- enableSql,
- uniqueKey,
- preventEviction,
- container,
- };
- }
- ),
- durableObjectStorage:
- classNamesEntries.length === 0
- ? undefined
- : options.unsafeEphemeralDurableObjects
- ? { inMemory: kVoid }
- : { localDisk: DURABLE_OBJECTS_STORAGE_SERVICE_NAME },
- globalOutbound: { name: getOutboundInterceptorName(workerIndex) },
- cacheApiOutbound: { name: getCacheServiceName(workerIndex) },
- moduleFallback:
- options.unsafeUseModuleFallbackService &&
- sharedOptions.unsafeModuleFallbackService !== undefined
- ? `${loopbackHost}:${loopbackPort}`
- : undefined,
- tails: options.tails?.map((service) => {
+ options.name,
+ unsafeUniqueKey
+ );
+
+ return uniqueKey === undefined
+ ? {
+ className,
+ enableSql,
+ ephemeralLocal: kVoid,
+ preventEviction,
+ container,
+ }
+ : {
+ className,
+ enableSql,
+ uniqueKey,
+ preventEviction,
+ container,
+ };
+ }
+ ),
+ durableObjectStorage:
+ classNamesEntries.length === 0
+ ? undefined
+ : options.unsafeEphemeralDurableObjects
+ ? { inMemory: kVoid }
+ : { localDisk: DURABLE_OBJECTS_STORAGE_SERVICE_NAME },
+ globalOutbound: { name: getOutboundInterceptorName(workerIndex) },
+ cacheApiOutbound: { name: getCacheServiceName(workerIndex) },
+ moduleFallback:
+ options.unsafeUseModuleFallbackService &&
+ sharedOptions.unsafeModuleFallbackService !== undefined
+ ? `${loopbackHost}:${loopbackPort}`
+ : undefined,
+ tails: options.tails?.map((service) => {
+ return getCustomServiceDesignator(
+ /* referrer */ options.name,
+ workerIndex,
+ CustomServiceKind.UNKNOWN,
+ name,
+ service,
+ options.hasAssetsAndIsVitest
+ );
+ }),
+ streamingTails: options.streamingTails?.map(
+ (service) => {
return getCustomServiceDesignator(
/* referrer */ options.name,
workerIndex,
@@ -972,23 +809,11 @@ export const CORE_PLUGIN: Plugin<
service,
options.hasAssetsAndIsVitest
);
- }),
- streamingTails: options.streamingTails?.map(
- (service) => {
- return getCustomServiceDesignator(
- /* referrer */ options.name,
- workerIndex,
- CustomServiceKind.UNKNOWN,
- name,
- service,
- options.hasAssetsAndIsVitest
- );
- }
- ),
- containerEngine: getContainerEngine(options.containerEngine),
- },
- });
- }
+ }
+ ),
+ containerEngine: getContainerEngine(sharedOptions.containerEngine),
+ },
+ });
// Define custom `fetch` services if set
if (options.serviceBindings !== undefined) {
@@ -1072,7 +897,6 @@ export interface GlobalServicesOptions {
sharedOptions: z.infer;
allWorkerRoutes: Map;
fallbackWorkerName: string | undefined;
- loopbackPort: number;
tmpPath: string;
log: Log;
/** All user workerd-native bindings, used for Miniflare's magic proxy and the local explorer worker */
@@ -1088,7 +912,6 @@ export function getGlobalServices({
sharedOptions,
allWorkerRoutes,
fallbackWorkerName,
- loopbackPort,
tmpPath,
log,
proxyBindings,
@@ -1203,14 +1026,6 @@ export function getGlobalServices({
data: encoder.encode(sharedOptions.unsafeProxySharedSecret),
});
}
- if (sharedOptions.liveReload) {
- const liveReloadScript = LIVE_RELOAD_SCRIPT_TEMPLATE(loopbackPort);
- serviceEntryBindings.push({
- name: CoreBindings.DATA_LIVE_RELOAD_SCRIPT,
- data: encoder.encode(liveReloadScript),
- });
- }
-
const services: Service[] = [
{
name: SERVICE_LOOPBACK,
diff --git a/packages/miniflare/src/plugins/core/services.ts b/packages/miniflare/src/plugins/core/services.ts
index 3fa0fa14aa0..5fd9ee8069c 100644
--- a/packages/miniflare/src/plugins/core/services.ts
+++ b/packages/miniflare/src/plugins/core/services.ts
@@ -18,13 +18,13 @@ export const kCurrentWorker = Symbol.for("miniflare.kCurrentWorker");
export const HttpOptionsHeaderSchema = z.object({
name: z.string(), // name should be required
- value: z.ostring(), // If omitted, the header will be removed
+ value: z.string().optional(), // If omitted, the header will be removed
});
const HttpOptionsSchema = z
.object({
- style: z.nativeEnum(HttpOptions_Style).optional(),
- forwardedProtoHeader: z.ostring(),
- cfBlobHeader: z.ostring(),
+ style: z.enum(HttpOptions_Style).optional(),
+ forwardedProtoHeader: z.string().optional(),
+ cfBlobHeader: z.string().optional(),
injectRequestHeaders: HttpOptionsHeaderSchema.array().optional(),
injectResponseHeaders: HttpOptionsHeaderSchema.array().optional(),
})
@@ -34,17 +34,17 @@ const HttpOptionsSchema = z
}));
const TlsOptionsKeypairSchema = z.object({
- privateKey: z.ostring(),
- certificateChain: z.ostring(),
+ privateKey: z.string().optional(),
+ certificateChain: z.string().optional(),
});
const TlsOptionsSchema = z.object({
keypair: TlsOptionsKeypairSchema.optional(),
- requireClientCerts: z.oboolean(),
- trustBrowserCas: z.oboolean(),
+ requireClientCerts: z.boolean().optional(),
+ trustBrowserCas: z.boolean().optional(),
trustedCertificates: z.string().array().optional(),
- minVersion: z.nativeEnum(TlsOptions_Version).optional(),
- cipherList: z.ostring(),
+ minVersion: z.enum(TlsOptions_Version).optional(),
+ cipherList: z.string().optional(),
});
const NetworkSchema = z.object({
@@ -62,7 +62,7 @@ export const ExternalServerSchema = z.intersection(
z.object({
options: HttpOptionsSchema.optional(),
tlsOptions: TlsOptionsSchema.optional(),
- certificateHost: z.ostring(),
+ certificateHost: z.string().optional(),
})
),
}),
@@ -76,7 +76,7 @@ export const ExternalServerSchema = z.intersection(
const DiskDirectorySchema = z.object({
path: z.string(), // path should be required
- writable: z.oboolean(),
+ writable: z.boolean().optional(),
});
const CustomNodeServiceSchema = z.custom<
@@ -93,11 +93,14 @@ export const CustomFetchServiceSchema = z.custom<
export const ServiceDesignatorSchema = z.union([
z.string(),
- z.literal(kCurrentWorker),
+ z.custom((v) => v === kCurrentWorker),
z.object({
- name: z.union([z.string(), z.literal(kCurrentWorker)]),
- entrypoint: z.ostring(),
- props: z.record(z.unknown()).optional(),
+ name: z.union([
+ z.string(),
+ z.custom((v) => v === kCurrentWorker),
+ ]),
+ entrypoint: z.string().optional(),
+ props: z.record(z.string(), z.unknown()).optional(),
remoteProxyConnectionString: z
.custom()
.optional(),
diff --git a/packages/miniflare/src/plugins/d1/index.ts b/packages/miniflare/src/plugins/d1/index.ts
index 007f73d68eb..b4bb8c53f75 100644
--- a/packages/miniflare/src/plugins/d1/index.ts
+++ b/packages/miniflare/src/plugins/d1/index.ts
@@ -8,11 +8,9 @@ import {
getMiniflareObjectBindings,
getPersistPath,
getUserBindingServiceName,
- migrateDatabase,
namespaceEntries,
namespaceKeys,
objectEntryWorker,
- PersistenceSchema,
ProxyNodeBinding,
remoteProxyClientWorker,
SERVICE_LOOPBACK,
@@ -28,6 +26,7 @@ export const D1OptionsSchema = z.object({
d1Databases: z
.union([
z.record(
+ z.string(),
z.union([
z.string(),
z.object({
@@ -42,10 +41,6 @@ export const D1OptionsSchema = z.object({
])
.optional(),
});
-export const D1SharedOptionsSchema = z.object({
- d1Persist: PersistenceSchema,
-});
-
export const D1_PLUGIN_NAME = "d1";
const D1_STORAGE_SERVICE_NAME = `${D1_PLUGIN_NAME}:storage`;
const D1_DATABASE_SERVICE_PREFIX = `${D1_PLUGIN_NAME}:db`;
@@ -57,12 +52,8 @@ const D1_DATABASE_OBJECT: Worker_Binding_DurableObjectNamespaceDesignator = {
className: D1_DATABASE_OBJECT_CLASS_NAME,
};
-export const D1_PLUGIN: Plugin<
- typeof D1OptionsSchema,
- typeof D1SharedOptionsSchema
-> = {
+export const D1_PLUGIN: Plugin = {
options: D1OptionsSchema,
- sharedOptions: D1SharedOptionsSchema,
bindingTypeDescription: "D1 database",
getBindings(options) {
const databases = namespaceEntries(options.d1Databases);
@@ -112,15 +103,7 @@ export const D1_PLUGIN: Plugin<
databases.map((name) => [name, new ProxyNodeBinding()])
);
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- log,
- unsafeStickyBlobs,
- }) {
- const persist = sharedOptions.d1Persist;
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const databases = namespaceEntries(options.d1Databases);
const services: Service[] = [];
@@ -148,8 +131,7 @@ export const D1_PLUGIN: Plugin<
const persistPath = getPersistPath(
D1_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- persist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
@@ -186,23 +168,13 @@ export const D1_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
};
services.push(storageService, objectService);
-
- for (const [, database] of databases) {
- if (database.remoteProxyConnectionString) {
- continue;
- }
- await migrateDatabase(log, uniqueKey, persistPath, database.id);
- }
}
return services;
},
- getPersistPath({ d1Persist }, tmpPath) {
- return getPersistPath(D1_PLUGIN_NAME, tmpPath, undefined, d1Persist);
- },
};
diff --git a/packages/miniflare/src/plugins/dispatch-namespace/index.ts b/packages/miniflare/src/plugins/dispatch-namespace/index.ts
index 11e4c776312..61fb08715bb 100644
--- a/packages/miniflare/src/plugins/dispatch-namespace/index.ts
+++ b/packages/miniflare/src/plugins/dispatch-namespace/index.ts
@@ -12,6 +12,7 @@ import type { Plugin, RemoteProxyConnectionString } from "../shared";
export const DispatchNamespaceOptionsSchema = z.object({
dispatchNamespaces: z
.record(
+ z.string(),
z.object({
namespace: z.string(),
remoteProxyConnectionString: z
diff --git a/packages/miniflare/src/plugins/do/index.ts b/packages/miniflare/src/plugins/do/index.ts
index e97ae2d580c..8efbdeadcb6 100644
--- a/packages/miniflare/src/plugins/do/index.ts
+++ b/packages/miniflare/src/plugins/do/index.ts
@@ -4,7 +4,6 @@ import { getUserServiceName } from "../core";
import {
getPersistPath,
kUnsafeEphemeralUniqueKey,
- PersistenceSchema,
ProxyNodeBinding,
} from "../shared";
import type { Worker_Binding } from "../../runtime";
@@ -29,7 +28,12 @@ const DurableObject = z.object({
// another `workerd` process, to ensure the IDs created by the stub
// object can be used by the real object too.
unsafeUniqueKey: z
- .union([z.string(), z.literal(kUnsafeEphemeralUniqueKey)])
+ .union([
+ z.string(),
+ z.custom(
+ (v) => v === kUnsafeEphemeralUniqueKey
+ ),
+ ])
.optional(),
// Prevents the Durable Object being evicted.
unsafePreventEviction: z.boolean().optional(),
@@ -40,14 +44,13 @@ const DurableObject = z.object({
});
export const DurableObjectsOptionsSchema = z.object({
- durableObjects: z.record(z.union([z.string(), DurableObject])).optional(),
+ durableObjects: z
+ .record(z.string(), z.union([z.string(), DurableObject]))
+ .optional(),
// Not all DOs are configured as bindings! Include these in a different key
// These might just be configured via migrations, but should still be allocated storage for e.g. ctx.exports support
additionalUnboundDurableObjects: z.array(DurableObject).optional(),
});
-export const DurableObjectsSharedOptionsSchema = z.object({
- durableObjectsPersist: PersistenceSchema,
-});
export function normaliseDurableObject(
designator: NonNullable<
@@ -108,11 +111,9 @@ export const DURABLE_OBJECTS_PLUGIN_NAME = "do";
export const DURABLE_OBJECTS_STORAGE_SERVICE_NAME = `${DURABLE_OBJECTS_PLUGIN_NAME}:storage`;
export const DURABLE_OBJECTS_PLUGIN: Plugin<
- typeof DurableObjectsOptionsSchema,
- typeof DurableObjectsSharedOptionsSchema
+ typeof DurableObjectsOptionsSchema
> = {
options: DurableObjectsOptionsSchema,
- sharedOptions: DurableObjectsSharedOptionsSchema,
bindingTypeDescription: "Durable Object namespace",
getBindings(options) {
return Object.entries(options.durableObjects ?? {}).map(
@@ -132,9 +133,8 @@ export const DURABLE_OBJECTS_PLUGIN: Plugin<
);
},
async getServices({
- sharedOptions,
tmpPath,
- defaultPersistRoot,
+ resourcePersistencePath,
durableObjectClassNames,
unsafeEphemeralDurableObjects,
}) {
@@ -157,8 +157,7 @@ export const DURABLE_OBJECTS_PLUGIN: Plugin<
const storagePath = getPersistPath(
DURABLE_OBJECTS_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.durableObjectsPersist
+ resourcePersistencePath
);
// `workerd` requires the `disk.path` to exist. Setting `recursive: true`
// is like `mkdir -p`: it won't fail if the directory already exists, and it
@@ -174,12 +173,4 @@ export const DURABLE_OBJECTS_PLUGIN: Plugin<
},
];
},
- getPersistPath({ durableObjectsPersist }, tmpPath) {
- return getPersistPath(
- DURABLE_OBJECTS_PLUGIN_NAME,
- tmpPath,
- undefined,
- durableObjectsPersist
- );
- },
};
diff --git a/packages/miniflare/src/plugins/email/index.ts b/packages/miniflare/src/plugins/email/index.ts
index d46d5c8024f..dfaedfaf3c3 100644
--- a/packages/miniflare/src/plugins/email/index.ts
+++ b/packages/miniflare/src/plugins/email/index.ts
@@ -57,27 +57,27 @@ function buildJsonBindings(bindings: Record): Worker_Binding[] {
}
function getEmailProjectParentDirectory(
- defaultProjectTmpPath: string | undefined
+ resourceTmpPath: string | undefined
): string | undefined {
- if (defaultProjectTmpPath === undefined) {
+ if (resourceTmpPath === undefined) {
return undefined;
}
- return path.join(defaultProjectTmpPath, EMAIL_PLUGIN_NAME);
+ return path.join(resourceTmpPath, EMAIL_PLUGIN_NAME);
}
/**
* Returns the session directory for email files.
- * Path: `/email/`
+ * Path: `/email/`
* Example: `/path/to/project/.wrangler/tmp/email/dev-abc123`
* When an email is logged, it is stored under this directory using a type indicator
* and a unique ID.
* Path: `//.`
*/
function getEmailProjectSessionDirectory(
- defaultProjectTmpPath: string | undefined,
+ resourceTmpPath: string | undefined,
tmpPath: string
): string | undefined {
- const parentDir = getEmailProjectParentDirectory(defaultProjectTmpPath);
+ const parentDir = getEmailProjectParentDirectory(resourceTmpPath);
if (parentDir === undefined) {
return undefined;
}
@@ -85,17 +85,14 @@ function getEmailProjectSessionDirectory(
}
export function getEmailPathsToClean(
- defaultProjectTmpPath: string | undefined,
+ resourceTmpPath: string | undefined,
tmpPath: string
): { sessionDir: string; parentDir: string } | undefined {
- if (defaultProjectTmpPath === undefined) {
+ if (resourceTmpPath === undefined) {
return undefined;
}
- const sessionDir = getEmailProjectSessionDirectory(
- defaultProjectTmpPath,
- tmpPath
- );
- const parentDir = getEmailProjectParentDirectory(defaultProjectTmpPath);
+ const sessionDir = getEmailProjectSessionDirectory(resourceTmpPath, tmpPath);
+ const parentDir = getEmailProjectParentDirectory(resourceTmpPath);
if (sessionDir === undefined || parentDir === undefined) {
return undefined;
}
@@ -148,7 +145,7 @@ export const EMAIL_PLUGIN: Plugin = {
await mkdir(emailSystemDirectory, { recursive: true });
// Map binding disk services to names and paths, for concise access when storing emails as files.
- // When defaultProjectTmpPath is unset, only create system service to avoid duplicates
+ // When resourceTmpPath is unset, only create system service to avoid duplicates
const diskServices: Array<{
location: "system" | "project";
bindingName: string;
@@ -163,9 +160,9 @@ export const EMAIL_PLUGIN: Plugin = {
},
];
- if (args.defaultProjectTmpPath) {
+ if (args.resourceTmpPath) {
const emailProjectSessionDirectory = getEmailProjectSessionDirectory(
- args.defaultProjectTmpPath,
+ args.resourceTmpPath,
args.tmpPath
);
if (emailProjectSessionDirectory !== undefined) {
diff --git a/packages/miniflare/src/plugins/flagship/index.ts b/packages/miniflare/src/plugins/flagship/index.ts
index 8c30d56aa3c..16f0f9029bb 100644
--- a/packages/miniflare/src/plugins/flagship/index.ts
+++ b/packages/miniflare/src/plugins/flagship/index.ts
@@ -15,7 +15,7 @@ const FlagshipSchema = z.object({
});
export const FlagshipOptionsSchema = z.object({
- flagship: z.record(FlagshipSchema).optional(),
+ flagship: z.record(z.string(), FlagshipSchema).optional(),
});
export const FLAGSHIP_PLUGIN_NAME = "flagship";
diff --git a/packages/miniflare/src/plugins/hello-world/index.ts b/packages/miniflare/src/plugins/hello-world/index.ts
index 8b008321975..25317de453a 100644
--- a/packages/miniflare/src/plugins/hello-world/index.ts
+++ b/packages/miniflare/src/plugins/hello-world/index.ts
@@ -6,7 +6,6 @@ import { SharedBindings } from "../../workers";
import {
getMiniflareObjectBindings,
getPersistPath,
- PersistenceSchema,
ProxyNodeBinding,
SERVICE_LOOPBACK,
} from "../shared";
@@ -18,6 +17,7 @@ export const HELLO_WORLD_PLUGIN_NAME = "hello-world";
export const HelloWorldOptionsSchema = z.object({
helloWorld: z
.record(
+ z.string(),
z.object({
enable_timer: z.boolean().optional(),
})
@@ -25,16 +25,8 @@ export const HelloWorldOptionsSchema = z.object({
.optional(),
});
-export const HelloWorldSharedOptionsSchema = z.object({
- helloWorldPersist: PersistenceSchema,
-});
-
-export const HELLO_WORLD_PLUGIN: Plugin<
- typeof HelloWorldOptionsSchema,
- typeof HelloWorldSharedOptionsSchema
-> = {
+export const HELLO_WORLD_PLUGIN: Plugin = {
options: HelloWorldOptionsSchema,
- sharedOptions: HelloWorldSharedOptionsSchema,
bindingTypeDescription: "Hello World",
async getBindings(options) {
if (!options.helloWorld) {
@@ -65,13 +57,7 @@ export const HELLO_WORLD_PLUGIN: Plugin<
])
);
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- unsafeStickyBlobs,
- }) {
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const configs = options.helloWorld ? Object.values(options.helloWorld) : [];
if (configs.length === 0) {
@@ -81,8 +67,7 @@ export const HELLO_WORLD_PLUGIN: Plugin<
const persistPath = getPersistPath(
HELLO_WORLD_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.helloWorldPersist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
@@ -119,7 +104,7 @@ export const HELLO_WORLD_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
} satisfies Service;
@@ -151,12 +136,4 @@ export const HELLO_WORLD_PLUGIN: Plugin<
return [...services, storageService, objectService];
},
- getPersistPath(sharedOptions, tmpPath) {
- return getPersistPath(
- HELLO_WORLD_PLUGIN_NAME,
- tmpPath,
- undefined,
- sharedOptions.helloWorldPersist
- );
- },
};
diff --git a/packages/miniflare/src/plugins/hyperdrive/index.ts b/packages/miniflare/src/plugins/hyperdrive/index.ts
index 3530245d164..f27fb59a461 100644
--- a/packages/miniflare/src/plugins/hyperdrive/index.ts
+++ b/packages/miniflare/src/plugins/hyperdrive/index.ts
@@ -23,46 +23,46 @@ function getPort(url: URL) {
}
export const HyperdriveSchema = z
- .union([z.string().url(), z.instanceof(URL)])
+ .union([z.url(), z.instanceof(URL)])
.transform((url, ctx) => {
if (typeof url === "string") url = new URL(url);
if (url.protocol === "") {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"You must specify the database protocol - e.g. 'postgresql'/'mysql'.",
});
} else if (!hasPostgresProtocol(url) && !hasMysqlProtocol(url)) {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"Only PostgreSQL-compatible or MySQL-compatible databases are currently supported.",
});
}
if (url.host === "") {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"You must provide a hostname or IP address in your connection string - e.g. 'user:password@database-hostname.example.com:5432/databasename",
});
}
if (url.pathname === "") {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"You must provide a database name as the path component - e.g. /postgres",
});
}
if (url.username === "") {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"You must provide a username - e.g. 'user:password@database.example.com:port/databasename'",
});
}
if (url.password === "") {
ctx.addIssue({
- code: z.ZodIssueCode.custom,
+ code: "custom",
message:
"You must provide a password - e.g. 'user:password@database.example.com:port/databasename' ",
});
diff --git a/packages/miniflare/src/plugins/images/index.ts b/packages/miniflare/src/plugins/images/index.ts
index 7df446df282..aabff361b5d 100644
--- a/packages/miniflare/src/plugins/images/index.ts
+++ b/packages/miniflare/src/plugins/images/index.ts
@@ -10,7 +10,6 @@ import {
getPersistPath,
getUserBindingServiceName,
objectEntryWorker,
- PersistenceSchema,
ProxyNodeBinding,
remoteProxyClientWorker,
SERVICE_LOOPBACK,
@@ -30,19 +29,11 @@ export const ImagesOptionsSchema = z.object({
images: ImagesSchema.optional(),
});
-export const ImagesSharedOptionsSchema = z.object({
- imagesPersist: PersistenceSchema,
-});
-
export const IMAGES_PLUGIN_NAME = "images";
const IMAGES_REMOTE_SERVICE_NAME = `${IMAGES_PLUGIN_NAME}:remote`;
-export const IMAGES_PLUGIN: Plugin<
- typeof ImagesOptionsSchema,
- typeof ImagesSharedOptionsSchema
-> = {
+export const IMAGES_PLUGIN: Plugin = {
options: ImagesOptionsSchema,
- sharedOptions: ImagesSharedOptionsSchema,
bindingTypeDescription: "Images",
async getBindings(options) {
if (!options.images) {
@@ -85,13 +76,7 @@ export const IMAGES_PLUGIN: Plugin<
[options.images.binding]: new ProxyNodeBinding(),
};
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- unsafeStickyBlobs,
- }) {
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
if (!options.images) {
return [];
}
@@ -113,8 +98,7 @@ export const IMAGES_PLUGIN: Plugin<
const persistPath = getPersistPath(
IMAGES_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.imagesPersist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
@@ -151,7 +135,7 @@ export const IMAGES_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
} satisfies Service;
@@ -189,12 +173,4 @@ export const IMAGES_PLUGIN: Plugin<
return [storageService, objectService, kvNamespaceService, imagesService];
},
- getPersistPath({ imagesPersist }, tmpPath) {
- return getPersistPath(
- IMAGES_PLUGIN_NAME,
- tmpPath,
- undefined,
- imagesPersist
- );
- },
};
diff --git a/packages/miniflare/src/plugins/index.ts b/packages/miniflare/src/plugins/index.ts
index f60738d9d2a..3dca3daa67b 100644
--- a/packages/miniflare/src/plugins/index.ts
+++ b/packages/miniflare/src/plugins/index.ts
@@ -194,7 +194,6 @@ export {
CoreOptionsSchema,
CoreSharedOptionsSchema,
compileModuleRules,
- createFetchMock,
getGlobalServices,
ModuleRuleTypeSchema,
ModuleRuleSchema,
diff --git a/packages/miniflare/src/plugins/kv/index.ts b/packages/miniflare/src/plugins/kv/index.ts
index ff79ca4100c..6da8d3a1da6 100644
--- a/packages/miniflare/src/plugins/kv/index.ts
+++ b/packages/miniflare/src/plugins/kv/index.ts
@@ -7,11 +7,9 @@ import {
buildRemoteProxyProps,
getMiniflareObjectBindings,
getPersistPath,
- migrateDatabase,
namespaceEntries,
namespaceKeys,
objectEntryWorker,
- PersistenceSchema,
ProxyNodeBinding,
remoteProxyClientWorker,
SERVICE_LOOPBACK,
@@ -34,6 +32,7 @@ export const KVOptionsSchema = z.object({
kvNamespaces: z
.union([
z.record(
+ z.string(),
z.union([
z.string(),
z.object({
@@ -53,10 +52,6 @@ export const KVOptionsSchema = z.object({
siteInclude: z.string().array().optional(),
siteExclude: z.string().array().optional(),
});
-export const KVSharedOptionsSchema = z.object({
- kvPersist: PersistenceSchema,
-});
-
const SERVICE_NAMESPACE_PREFIX = `${KV_PLUGIN_NAME}:ns`;
// A single entry service shared by every *local* namespace. Each namespace's id
// is supplied per-binding via `ctx.props`, so one service serves all of them.
@@ -76,12 +71,8 @@ function isWorkersSitesEnabled(
return options.sitePath !== undefined;
}
-export const KV_PLUGIN: Plugin<
- typeof KVOptionsSchema,
- typeof KVSharedOptionsSchema
-> = {
+export const KV_PLUGIN: Plugin = {
options: KVOptionsSchema,
- sharedOptions: KVSharedOptionsSchema,
bindingTypeDescription: "KV namespace",
async getBindings(options) {
const namespaces = namespaceEntries(options.kvNamespaces);
@@ -135,15 +126,7 @@ export const KV_PLUGIN: Plugin<
return bindings;
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- log,
- unsafeStickyBlobs,
- }) {
- const persist = sharedOptions.kvPersist;
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const namespaces = namespaceEntries(options.kvNamespaces);
const services: Service[] = [];
@@ -175,8 +158,7 @@ export const KV_PLUGIN: Plugin<
const persistPath = getPersistPath(
KV_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- persist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
const storageService: Service = {
@@ -209,23 +191,11 @@ export const KV_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
};
services.push(storageService, objectService);
-
- // Before the switch to Durable Object simulators, Miniflare stored
- // databases alongside blobs in a namespace specific directory. To avoid
- // another breaking change to the persistence location, migrate SQLite
- // databases from the old location to the new location. Blobs are still
- // stored in the same location.
- for (const [, namespace] of namespaces) {
- if (namespace.remoteProxyConnectionString) {
- continue;
- }
- await migrateDatabase(log, uniqueKey, persistPath, namespace.id);
- }
}
if (isWorkersSitesEnabled(options)) {
@@ -234,10 +204,6 @@ export const KV_PLUGIN: Plugin<
return services;
},
-
- getPersistPath({ kvPersist }, tmpPath) {
- return getPersistPath(KV_PLUGIN_NAME, tmpPath, undefined, kvPersist);
- },
};
export { KV_PLUGIN_NAME };
diff --git a/packages/miniflare/src/plugins/mtls/index.ts b/packages/miniflare/src/plugins/mtls/index.ts
index 5977b4dde79..1e5857e9bb9 100644
--- a/packages/miniflare/src/plugins/mtls/index.ts
+++ b/packages/miniflare/src/plugins/mtls/index.ts
@@ -14,7 +14,7 @@ const MtlsSchema = z.object({
});
export const MtlsOptionsSchema = z.object({
- mtlsCertificates: z.record(MtlsSchema).optional(),
+ mtlsCertificates: z.record(z.string(), MtlsSchema).optional(),
});
export const MTLS_PLUGIN_NAME = "mtls";
diff --git a/packages/miniflare/src/plugins/pipelines/index.ts b/packages/miniflare/src/plugins/pipelines/index.ts
index c65fc7f5e3d..aa481d494ab 100644
--- a/packages/miniflare/src/plugins/pipelines/index.ts
+++ b/packages/miniflare/src/plugins/pipelines/index.ts
@@ -13,6 +13,7 @@ export const PipelineOptionsSchema = z.object({
pipelines: z
.union([
z.record(
+ z.string(),
z.union([
z.string(),
z.object({
diff --git a/packages/miniflare/src/plugins/queues/index.ts b/packages/miniflare/src/plugins/queues/index.ts
index c86552f18c1..959f46d3452 100644
--- a/packages/miniflare/src/plugins/queues/index.ts
+++ b/packages/miniflare/src/plugins/queues/index.ts
@@ -29,20 +29,22 @@ export const QueuesOptionsSchema = z.object({
queueProducers: z
.union([
z.record(
- QueueProducerOptionsSchema.merge(
- z.object({
- remoteProxyConnectionString: z
- .custom()
- .optional(),
- })
- )
+ z.string(),
+ QueueProducerOptionsSchema.extend({
+ remoteProxyConnectionString: z
+ .custom()
+ .optional(),
+ })
),
z.string().array(),
- z.record(z.string()),
+ z.record(z.string(), z.string()),
])
.optional(),
queueConsumers: z
- .union([z.record(QueueConsumerOptionsSchema), z.string().array()])
+ .union([
+ z.record(z.string(), QueueConsumerOptionsSchema),
+ z.string().array(),
+ ])
.optional(),
});
@@ -75,7 +77,6 @@ export const QUEUES_PLUGIN: Plugin = {
queueProducers: allQueueProducers,
queueConsumers: allQueueConsumers,
devRegistryEnabled,
- unsafeStickyBlobs,
}) {
const produced = bindingEntries(options.queueProducers).map(([, id]) => id);
// Consumed queues get a broker service even without a local producer so
@@ -117,7 +118,7 @@ export const QUEUES_PLUGIN: Plugin = {
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
{
name: SharedBindings.DURABLE_OBJECT_NAMESPACE_OBJECT,
durableObjectNamespace: {
diff --git a/packages/miniflare/src/plugins/r2/index.ts b/packages/miniflare/src/plugins/r2/index.ts
index 59543f5d409..20e0b69190a 100644
--- a/packages/miniflare/src/plugins/r2/index.ts
+++ b/packages/miniflare/src/plugins/r2/index.ts
@@ -8,11 +8,9 @@ import {
getMiniflareObjectBindings,
getPersistPath,
getUserBindingServiceName,
- migrateDatabase,
namespaceEntries,
namespaceKeys,
objectEntryWorker,
- PersistenceSchema,
ProxyNodeBinding,
remoteProxyClientWorker,
SERVICE_LOOPBACK,
@@ -28,6 +26,7 @@ export const R2OptionsSchema = z.object({
r2Buckets: z
.union([
z.record(
+ z.string(),
z.union([
z.string(),
z.object({
@@ -42,10 +41,6 @@ export const R2OptionsSchema = z.object({
])
.optional(),
});
-export const R2SharedOptionsSchema = z.object({
- r2Persist: PersistenceSchema,
-});
-
export const R2_PLUGIN_NAME = "r2";
const R2_STORAGE_SERVICE_NAME = `${R2_PLUGIN_NAME}:storage`;
const R2_BUCKET_SERVICE_PREFIX = `${R2_PLUGIN_NAME}:bucket`;
@@ -89,12 +84,8 @@ export function getR2PublicService(
};
}
-export const R2_PLUGIN: Plugin<
- typeof R2OptionsSchema,
- typeof R2SharedOptionsSchema
-> = {
+export const R2_PLUGIN: Plugin = {
options: R2OptionsSchema,
- sharedOptions: R2SharedOptionsSchema,
bindingTypeDescription: "R2 bucket",
getBindings(options) {
const buckets = namespaceEntries(options.r2Buckets);
@@ -122,15 +113,7 @@ export const R2_PLUGIN: Plugin<
buckets.map((name) => [name, new ProxyNodeBinding()])
);
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- log,
- unsafeStickyBlobs,
- }) {
- const persist = sharedOptions.r2Persist;
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const buckets = namespaceEntries(options.r2Buckets);
const services: Service[] = [];
@@ -158,8 +141,7 @@ export const R2_PLUGIN: Plugin<
const persistPath = getPersistPath(
R2_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- persist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
const storageService: Service = {
@@ -195,23 +177,13 @@ export const R2_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
};
services.push(storageService, objectService);
-
- for (const [, bucket] of buckets) {
- if (bucket.remoteProxyConnectionString) {
- continue;
- }
- await migrateDatabase(log, uniqueKey, persistPath, bucket.id);
- }
}
return services;
},
- getPersistPath({ r2Persist }, tmpPath) {
- return getPersistPath(R2_PLUGIN_NAME, tmpPath, undefined, r2Persist);
- },
};
diff --git a/packages/miniflare/src/plugins/ratelimit/index.ts b/packages/miniflare/src/plugins/ratelimit/index.ts
index e7a81765d64..c42ee653f25 100644
--- a/packages/miniflare/src/plugins/ratelimit/index.ts
+++ b/packages/miniflare/src/plugins/ratelimit/index.ts
@@ -32,11 +32,11 @@ export const RatelimitConfigSchema = z.object({
limit: z.number().gt(0),
// may relax this to be any number in the future
- period: z.nativeEnum(PeriodType).optional().default(PeriodType.MINUTE),
+ period: z.enum(PeriodType).optional().default(PeriodType.MINUTE),
}),
});
export const RatelimitOptionsSchema = z.object({
- ratelimits: z.record(RatelimitConfigSchema).optional(),
+ ratelimits: z.record(z.string(), RatelimitConfigSchema).optional(),
});
export const RATELIMIT_PLUGIN_NAME = "ratelimit";
@@ -99,7 +99,7 @@ export const RATELIMIT_PLUGIN: Plugin = {
])
);
},
- async getServices({ options, unsafeStickyBlobs }) {
+ async getServices({ options }) {
if (!options.ratelimits) {
return [];
}
@@ -145,7 +145,7 @@ export const RATELIMIT_PLUGIN: Plugin = {
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
});
diff --git a/packages/miniflare/src/plugins/secret-store/index.ts b/packages/miniflare/src/plugins/secret-store/index.ts
index 609c6e8a811..1fbf4b5b900 100644
--- a/packages/miniflare/src/plugins/secret-store/index.ts
+++ b/packages/miniflare/src/plugins/secret-store/index.ts
@@ -9,7 +9,6 @@ import {
getPersistPath,
getUserBindingServiceName,
objectEntryWorker,
- PersistenceSchema,
ProxyNodeBinding,
SERVICE_LOOPBACK,
} from "../shared";
@@ -17,6 +16,7 @@ import type { Service, Worker_Binding } from "../../runtime";
import type { Plugin } from "../shared";
const SecretsStoreSecretsSchema = z.record(
+ z.string(),
z.object({
store_id: z.string(),
secret_name: z.string(),
@@ -27,18 +27,12 @@ export const SecretsStoreSecretsOptionsSchema = z.object({
secretsStoreSecrets: SecretsStoreSecretsSchema.optional(),
});
-export const SecretsStoreSecretsSharedOptionsSchema = z.object({
- secretsStorePersist: PersistenceSchema,
-});
-
export const SECRET_STORE_PLUGIN_NAME = "secrets-store";
export const SECRET_STORE_PLUGIN: Plugin<
- typeof SecretsStoreSecretsOptionsSchema,
- typeof SecretsStoreSecretsSharedOptionsSchema
+ typeof SecretsStoreSecretsOptionsSchema
> = {
options: SecretsStoreSecretsOptionsSchema,
- sharedOptions: SecretsStoreSecretsSharedOptionsSchema,
bindingTypeDescription: "Secrets Store secret",
async getBindings(options) {
if (!options.secretsStoreSecrets) {
@@ -72,13 +66,7 @@ export const SECRET_STORE_PLUGIN: Plugin<
])
);
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- unsafeStickyBlobs,
- }) {
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const configs = options.secretsStoreSecrets
? Object.values(options.secretsStoreSecrets)
: [];
@@ -90,8 +78,7 @@ export const SECRET_STORE_PLUGIN: Plugin<
const persistPath = getPersistPath(
SECRET_STORE_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.secretsStorePersist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
@@ -129,7 +116,7 @@ export const SECRET_STORE_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
} satisfies Service;
@@ -177,12 +164,4 @@ export const SECRET_STORE_PLUGIN: Plugin<
return [...services, storageService, objectService];
},
- getPersistPath({ secretsStorePersist }, tmpPath) {
- return getPersistPath(
- SECRET_STORE_PLUGIN_NAME,
- tmpPath,
- undefined,
- secretsStorePersist
- );
- },
};
diff --git a/packages/miniflare/src/plugins/shared/constants.ts b/packages/miniflare/src/plugins/shared/constants.ts
index 02c5c2ef601..6661669d5f8 100644
--- a/packages/miniflare/src/plugins/shared/constants.ts
+++ b/packages/miniflare/src/plugins/shared/constants.ts
@@ -37,21 +37,12 @@ const WORKER_BINDING_ENABLE_CONTROL_ENDPOINTS: Worker_Binding = {
name: SharedBindings.MAYBE_JSON_ENABLE_CONTROL_ENDPOINTS,
json: "true",
};
-const WORKER_BINDING_ENABLE_STICKY_BLOBS: Worker_Binding = {
- name: SharedBindings.MAYBE_JSON_ENABLE_STICKY_BLOBS,
- json: "true",
-};
let enableControlEndpoints = false;
-export function getMiniflareObjectBindings(
- unsafeStickyBlobs: boolean
-): Worker_Binding[] {
+export function getMiniflareObjectBindings(): Worker_Binding[] {
const result: Worker_Binding[] = [];
if (enableControlEndpoints) {
result.push(WORKER_BINDING_ENABLE_CONTROL_ENDPOINTS);
}
- if (unsafeStickyBlobs) {
- result.push(WORKER_BINDING_ENABLE_STICKY_BLOBS);
- }
return result;
}
/** @internal */
diff --git a/packages/miniflare/src/plugins/shared/index.ts b/packages/miniflare/src/plugins/shared/index.ts
index baf5928461c..4d61caa0a55 100644
--- a/packages/miniflare/src/plugins/shared/index.ts
+++ b/packages/miniflare/src/plugins/shared/index.ts
@@ -1,11 +1,7 @@
-import crypto, { createHash } from "node:crypto";
-import { existsSync } from "node:fs";
-import fs from "node:fs/promises";
+import { createHash } from "node:crypto";
import path from "node:path";
-import { fileURLToPath, pathToFileURL } from "node:url";
-import { z } from "zod";
-import { MiniflareCoreError, PathSchema } from "../../shared";
-import { sanitisePath } from "../../workers";
+import { pathToFileURL } from "node:url";
+import { MiniflareCoreError } from "../../shared";
import type {
Extension,
Service,
@@ -21,20 +17,7 @@ import type {
import type { DOContainerOptions } from "../do";
import type { HyperdriveProxyController } from "../hyperdrive/hyperdrive-proxy";
import type { UnsafeUniqueKey } from "./constants";
-
-export const DEFAULT_PERSIST_ROOT = ".mf";
-
-export const PersistenceSchema = z
- // Zod checks union types in order, both `z.string().url()` and `PathSchema`
- // will result in a `string`, but `PathSchema` gets resolved relative to the
- // closest `rootPath`.
- .union([z.boolean(), z.string().url(), PathSchema])
- .optional();
-export type Persistence = z.infer;
-
-// Set of "worker" names that are being used as wrapped bindings and shouldn't
-// be added a regular worker services. These workers shouldn't be routable.
-export type WrappedBindingNames = Set;
+import type { z } from "zod";
// Maps workflow binding names to their workflow options
export interface WorkflowOption {
@@ -77,16 +60,14 @@ export interface PluginServicesOptions<
workerIndex: number;
additionalModules: Worker_Module[];
tmpPath: string;
- defaultPersistRoot: string | undefined;
- defaultProjectTmpPath: string | undefined;
+ resourcePersistencePath: string | undefined;
+ resourceTmpPath: string | undefined;
workerNames: string[];
loopbackHost: string;
loopbackPort: number;
publicUrl: string | undefined;
- unsafeStickyBlobs: boolean;
// ~~Leaky abstractions~~ "Plugin specific options" :)
- wrappedBindingNames: WrappedBindingNames;
durableObjectClassNames: DurableObjectClassNames;
unsafeEphemeralDurableObjects: boolean;
queueProducers: QueueProducers;
@@ -120,10 +101,6 @@ export interface PluginBase<
getServices(
options: PluginServicesOptions
): Awaitable;
- getPersistPath?(
- sharedOptions: OptionalZodTypeOf,
- tmpPath: string
- ): string;
getExtensions?(options: {
options: z.infer[];
}): Awaitable;
@@ -143,7 +120,7 @@ export type Plugin<
*/
export async function loadExternalPlugins(
packageName: string
-): Promise>> {
+): Promise>> {
let pluginModule;
try {
const pluginPath = require.resolve(packageName);
@@ -223,7 +200,7 @@ export function namespaceEntries(
}
}
-export function maybeParseURL(url: Persistence): URL | undefined {
+export function maybeParseURL(url: string | undefined): URL | undefined {
if (typeof url !== "string" || path.isAbsolute(url)) return;
try {
return new URL(url);
@@ -233,48 +210,18 @@ export function maybeParseURL(url: Persistence): URL | undefined {
export function getPersistPath(
pluginName: string,
tmpPath: string,
- defaultPersistRoot: string | undefined,
- persist: Persistence
+ resourcePersistencePath: string | undefined
): string {
- // If persistence is disabled, use "memory" storage. Note we're still
- // returning a path on the file-system here. Miniflare 2's in-memory storage
- // persisted between options reloads. However, we restart the `workerd`
- // process on each reload which would destroy any in-memory data. We'd like to
- // keep Miniflare 2's behaviour, so persist to a temporary path which we
- // destroy on `dispose()`.
- const memoryishPath = path.join(tmpPath, pluginName);
-
- let result: string;
- if (persist === false) {
- result = memoryishPath;
- } else if (persist === undefined) {
- // If `persist` is undefined, use either the default path or fallback to the tmpPath
- result =
- defaultPersistRoot === undefined
- ? memoryishPath
- : path.join(defaultPersistRoot, pluginName);
- } else {
- // Try parse `persist` as a URL
- const url = maybeParseURL(persist);
- if (url !== undefined) {
- if (url.protocol === "memory:") {
- result = memoryishPath;
- } else if (url.protocol === "file:") {
- result = fileURLToPath(url);
- } else {
- throw new MiniflareCoreError(
- "ERR_PERSIST_UNSUPPORTED",
- `Unsupported "${url.protocol}" persistence protocol for storage: ${url.href}`
- );
- }
- } else {
- // Otherwise, fallback to file storage
- result =
- persist === true
- ? path.join(defaultPersistRoot ?? DEFAULT_PERSIST_ROOT, pluginName)
- : persist;
- }
- }
+ // If persistence is disabled (no resource persistence path), use "memory"
+ // storage. Note we're still returning a path on the file-system here.
+ // Miniflare 2's in-memory storage persisted between options reloads. However,
+ // we restart the `workerd` process on each reload which would destroy any
+ // in-memory data. We'd like to keep Miniflare 2's behaviour, so persist to a
+ // temporary path which we destroy on `dispose()`.
+ const result =
+ resourcePersistencePath === undefined
+ ? path.join(tmpPath, pluginName)
+ : path.join(resourcePersistencePath, pluginName);
// Normalize to forward slashes for workerd's disk service compatibility on
// Windows. workerd is a Unix-oriented C++ program and its disk service does
@@ -284,62 +231,6 @@ export function getPersistPath(
return result.replaceAll("\\", "/");
}
-// https://github.com/cloudflare/workerd/blob/81d97010e44f848bb95d0083e2677bca8d1658b7/src/workerd/server/workerd-api.c%2B%2B#L436
-function durableObjectNamespaceIdFromName(uniqueKey: string, name: string) {
- const key = crypto.createHash("sha256").update(uniqueKey).digest();
- const nameHmac = crypto
- .createHmac("sha256", key)
- .update(name)
- .digest()
- .subarray(0, 16);
- const hmac = crypto
- .createHmac("sha256", key)
- .update(nameHmac)
- .digest()
- .subarray(0, 16);
- return Buffer.concat([nameHmac, hmac]).toString("hex");
-}
-
-export async function migrateDatabase(
- log: Log,
- uniqueKey: string,
- persistPath: string,
- namespace: string
-) {
- // Check if database exists at previous location
- const sanitisedNamespace = sanitisePath(namespace);
- const previousDir = path.join(persistPath, sanitisedNamespace);
- const previousPath = path.join(previousDir, "db.sqlite");
- const previousWalPath = path.join(previousDir, "db.sqlite-wal");
- if (!existsSync(previousPath)) return;
-
- // Move database to new location, if database isn't already there
- const id = durableObjectNamespaceIdFromName(uniqueKey, namespace);
- const newDir = path.join(persistPath, uniqueKey);
- const newPath = path.join(newDir, `${id}.sqlite`);
- const newWalPath = path.join(newDir, `${id}.sqlite-wal`);
- if (existsSync(newPath)) {
- log.debug(
- `Not migrating ${previousPath} to ${newPath} as it already exists`
- );
- return;
- }
-
- log.debug(`Migrating ${previousPath} to ${newPath}...`);
- await fs.mkdir(newDir, { recursive: true });
-
- try {
- await fs.copyFile(previousPath, newPath);
- if (existsSync(previousWalPath)) {
- await fs.copyFile(previousWalPath, newWalPath);
- }
- await fs.unlink(previousPath);
- await fs.unlink(previousWalPath);
- } catch (e) {
- log.warn(`Error migrating ${previousPath} to ${newPath}: ${e}`);
- }
-}
-
/**
* Service names for remote bindings should be unique depending on the remote proxy connection
* string (since in theory different remote bindings can have different remote proxy connections),
diff --git a/packages/miniflare/src/plugins/stream/index.ts b/packages/miniflare/src/plugins/stream/index.ts
index fe75ffbc367..3ef0fc25809 100644
--- a/packages/miniflare/src/plugins/stream/index.ts
+++ b/packages/miniflare/src/plugins/stream/index.ts
@@ -8,7 +8,6 @@ import {
getMiniflareObjectBindings,
getPersistPath,
getUserBindingServiceName,
- PersistenceSchema,
ProxyNodeBinding,
remoteProxyClientWorker,
WORKER_BINDING_SERVICE_LOOPBACK,
@@ -27,10 +26,6 @@ export const StreamOptionsSchema = z.object({
stream: StreamSchema.optional(),
});
-export const StreamSharedOptionsSchema = z.object({
- streamPersist: PersistenceSchema,
-});
-
export const STREAM_PLUGIN_NAME = "stream";
const STREAM_REMOTE_SERVICE_NAME = `${STREAM_PLUGIN_NAME}:remote`;
const STREAM_STORAGE_SERVICE_NAME = `${STREAM_PLUGIN_NAME}:storage`;
@@ -39,12 +34,8 @@ export const STREAM_OBJECT_CLASS_NAME = "StreamObject";
export const STREAM_COMPAT_DATE = "2026-03-23";
-export const STREAM_PLUGIN: Plugin<
- typeof StreamOptionsSchema,
- typeof StreamSharedOptionsSchema
-> = {
+export const STREAM_PLUGIN: Plugin = {
options: StreamOptionsSchema,
- sharedOptions: StreamSharedOptionsSchema,
bindingTypeDescription: "Stream",
async getBindings(options) {
if (!options.stream) {
@@ -77,13 +68,7 @@ export const STREAM_PLUGIN: Plugin<
[options.stream.binding]: new ProxyNodeBinding(),
};
},
- async getServices({
- options,
- sharedOptions,
- tmpPath,
- defaultPersistRoot,
- unsafeStickyBlobs,
- }) {
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
if (!options.stream) {
return [];
}
@@ -100,8 +85,7 @@ export const STREAM_PLUGIN: Plugin<
const persistPath = getPersistPath(
STREAM_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.streamPersist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
@@ -136,7 +120,7 @@ export const STREAM_PLUGIN: Plugin<
name: SharedBindings.MAYBE_SERVICE_BLOBS,
service: { name: STREAM_STORAGE_SERVICE_NAME },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
// Allow the DO to send outbound HTTP requests (fetching watermark images)
globalOutbound: { name: "internet" },
@@ -177,12 +161,4 @@ export const STREAM_PLUGIN: Plugin<
return [storageService, objectService, bindingService];
},
- getPersistPath({ streamPersist }, tmpPath) {
- return getPersistPath(
- STREAM_PLUGIN_NAME,
- tmpPath,
- undefined,
- streamPersist
- );
- },
};
diff --git a/packages/miniflare/src/plugins/vectorize/index.ts b/packages/miniflare/src/plugins/vectorize/index.ts
index b9e4f585281..f3cf83657ca 100644
--- a/packages/miniflare/src/plugins/vectorize/index.ts
+++ b/packages/miniflare/src/plugins/vectorize/index.ts
@@ -14,7 +14,7 @@ const VectorizeSchema = z.object({
});
export const VectorizeOptionsSchema = z.object({
- vectorize: z.record(VectorizeSchema).optional(),
+ vectorize: z.record(z.string(), VectorizeSchema).optional(),
});
export const VECTORIZE_PLUGIN_NAME = "vectorize";
diff --git a/packages/miniflare/src/plugins/vpc-networks/index.ts b/packages/miniflare/src/plugins/vpc-networks/index.ts
index bc363f2bba3..40eae34f892 100644
--- a/packages/miniflare/src/plugins/vpc-networks/index.ts
+++ b/packages/miniflare/src/plugins/vpc-networks/index.ts
@@ -22,7 +22,7 @@ const VpcNetworksSchema = z.union([
]);
export const VpcNetworksOptionsSchema = z.object({
- vpcNetworks: z.record(VpcNetworksSchema).optional(),
+ vpcNetworks: z.record(z.string(), VpcNetworksSchema).optional(),
});
export const VPC_NETWORKS_PLUGIN_NAME = "vpc-networks";
diff --git a/packages/miniflare/src/plugins/vpc-services/index.ts b/packages/miniflare/src/plugins/vpc-services/index.ts
index 597155d4f6e..a87daf5b578 100644
--- a/packages/miniflare/src/plugins/vpc-services/index.ts
+++ b/packages/miniflare/src/plugins/vpc-services/index.ts
@@ -14,7 +14,7 @@ const VpcServicesSchema = z.object({
});
export const VpcServicesOptionsSchema = z.object({
- vpcServices: z.record(VpcServicesSchema).optional(),
+ vpcServices: z.record(z.string(), VpcServicesSchema).optional(),
});
export const VPC_SERVICES_PLUGIN_NAME = "vpc-services";
diff --git a/packages/miniflare/src/plugins/websearch/index.ts b/packages/miniflare/src/plugins/websearch/index.ts
index 70e29380cf9..db4ab15160c 100644
--- a/packages/miniflare/src/plugins/websearch/index.ts
+++ b/packages/miniflare/src/plugins/websearch/index.ts
@@ -13,7 +13,7 @@ const WebsearchEntrySchema = z.object({
});
export const WebsearchOptionsSchema = z.object({
- websearch: z.record(WebsearchEntrySchema).optional(),
+ websearch: z.record(z.string(), WebsearchEntrySchema).optional(),
});
export const WEBSEARCH_PLUGIN_NAME = "websearch";
diff --git a/packages/miniflare/src/plugins/worker-loader/index.ts b/packages/miniflare/src/plugins/worker-loader/index.ts
index 519a8aa76d0..cc56be412a6 100644
--- a/packages/miniflare/src/plugins/worker-loader/index.ts
+++ b/packages/miniflare/src/plugins/worker-loader/index.ts
@@ -4,7 +4,7 @@ import type { Plugin } from "../shared";
export const WorkerLoaderConfigSchema = z.object({});
export const WorkerLoaderOptionsSchema = z.object({
- workerLoaders: z.record(WorkerLoaderConfigSchema).optional(),
+ workerLoaders: z.record(z.string(), WorkerLoaderConfigSchema).optional(),
});
export const WORKER_LOADER_PLUGIN_NAME = "worker-loader";
diff --git a/packages/miniflare/src/plugins/workflows/index.ts b/packages/miniflare/src/plugins/workflows/index.ts
index a89b5219121..8d5d7cc803a 100644
--- a/packages/miniflare/src/plugins/workflows/index.ts
+++ b/packages/miniflare/src/plugins/workflows/index.ts
@@ -6,7 +6,6 @@ import { getUserServiceName } from "../core";
import {
getPersistPath,
getUserBindingServiceName,
- PersistenceSchema,
ProxyNodeBinding,
SERVICE_DEV_REGISTRY_PROXY,
} from "../shared";
@@ -16,6 +15,7 @@ import type { Plugin, RemoteProxyConnectionString } from "../shared";
export const WorkflowsOptionsSchema = z.object({
workflows: z
.record(
+ z.string(),
z.object({
name: z.string(),
className: z.string(),
@@ -36,19 +36,11 @@ export const WorkflowsOptionsSchema = z.object({
)
.optional(),
});
-export const WorkflowsSharedOptionsSchema = z.object({
- workflowsPersist: PersistenceSchema,
-});
-
export const WORKFLOWS_PLUGIN_NAME = "workflows";
export const WORKFLOWS_STORAGE_SERVICE_NAME = `${WORKFLOWS_PLUGIN_NAME}:storage`;
-export const WORKFLOWS_PLUGIN: Plugin<
- typeof WorkflowsOptionsSchema,
- typeof WorkflowsSharedOptionsSchema
-> = {
+export const WORKFLOWS_PLUGIN: Plugin = {
options: WorkflowsOptionsSchema,
- sharedOptions: WorkflowsSharedOptionsSchema,
bindingTypeDescription: "Workflow",
async getBindings(options: z.infer) {
return Object.entries(options.workflows ?? {}).map(
@@ -97,12 +89,11 @@ export const WORKFLOWS_PLUGIN: Plugin<
];
},
- async getServices({ options, sharedOptions, tmpPath, defaultPersistRoot }) {
+ async getServices({ options, tmpPath, resourcePersistencePath }) {
const persistPath = getPersistPath(
WORKFLOWS_PLUGIN_NAME,
tmpPath,
- defaultPersistRoot,
- sharedOptions.workflowsPersist
+ resourcePersistencePath
);
await fs.mkdir(persistPath, { recursive: true });
// each workflow should get its own storage service
@@ -204,13 +195,4 @@ export const WORKFLOWS_PLUGIN: Plugin<
return [...storageServices, ...services];
},
-
- getPersistPath({ workflowsPersist }, tmpPath) {
- return getPersistPath(
- WORKFLOWS_PLUGIN_NAME,
- tmpPath,
- undefined,
- workflowsPersist
- );
- },
};
diff --git a/packages/miniflare/src/runtime/index.ts b/packages/miniflare/src/runtime/index.ts
index 7bd790cd3ec..0c35443be2d 100644
--- a/packages/miniflare/src/runtime/index.ts
+++ b/packages/miniflare/src/runtime/index.ts
@@ -6,9 +6,7 @@ import path from "node:path";
import rl from "node:readline";
import { Readable, Transform } from "node:stream";
import { $ as $colors, red } from "kleur/colors";
-import workerdPath, {
- compatibilityDate as workerdCompatibilityDate,
-} from "workerd";
+import workerdPath from "workerd";
import { z } from "zod";
import { SERVICE_LOOPBACK, SOCKET_ENTRY } from "../plugins";
import { MiniflareCoreError } from "../shared";
@@ -42,7 +40,6 @@ export interface RuntimeOptions {
inspectorAddress?: string;
debugPortAddress?: string;
verbose?: boolean;
- handleRuntimeStdio?: (stdout: Readable, stderr: Readable) => void;
handleStructuredLogs?: StructuredLogsHandler;
// Extra environment variables to set on the spawned `workerd` subprocess.
// Merged on top of `process.env` and Miniflare's own defaults
@@ -90,20 +87,27 @@ function waitForExit(process: childProcess.ChildProcess): Promise {
});
}
-function pipeOutput(stdout: Readable, stderr: Readable) {
- // TODO: may want to proxy these and prettify ✨
- // We can't just pipe() to `process.stdout/stderr` here, as Ink (used by
- // wrangler), only patches the `console.*` methods:
- // https://github.com/vadimdemedes/ink/blob/5d24ed8ada593a6c36ea5416f452158461e33ba5/readme.md#patchconsole
- // Writing directly to `process.stdout/stderr` would result in graphical
- // glitches.
- // eslint-disable-next-line no-console -- Intentional console.log to forward workerd stdout through Ink-patched console
- rl.createInterface(stdout).on("line", (data) => console.log(data));
- // eslint-disable-next-line no-console -- Intentional console.error to forward workerd stderr through Ink-patched console
- rl.createInterface(stderr).on("line", (data) => console.error(red(data)));
- // stdout.pipe(process.stdout);
- // stderr.pipe(process.stderr);
-}
+// When no `handleStructuredLogs` handler is provided, workerd's structured logs
+// are forwarded to the console by default. `warn`/`error` logs go to stderr
+// (in red), everything else to stdout, matching how the raw workerd streams
+// used to be forwarded. We use `console.*` rather than writing to
+// `process.stdout/stderr` directly, as Ink (used by Wrangler) only patches the
+// `console.*` methods:
+// https://github.com/vadimdemedes/ink/blob/5d24ed8ada593a6c36ea5416f452158461e33ba5/readme.md#patchconsole
+// Writing directly to `process.stdout/stderr` would result in graphical
+// glitches.
+const defaultStructuredLogsHandler: StructuredLogsHandler = ({
+ level,
+ message,
+}) => {
+ if (level === "error" || level === "warn") {
+ // eslint-disable-next-line no-console -- forward workerd output through Ink-patched console
+ console.error(red(message));
+ } else {
+ // eslint-disable-next-line no-console -- forward workerd output through Ink-patched console
+ console.log(message);
+ }
+};
function getRuntimeCommand() {
return process.env.MINIFLARE_WORKERD_PATH ?? workerdPath;
@@ -260,28 +264,17 @@ export class Runtime {
const processExitPromise = waitForExit(runtimeProcess);
this.#processExitPromise = processExitPromise;
- const handleRuntimeStdio =
- options.handleRuntimeStdio ??
- (options.handleStructuredLogs
- ? // If `handleStructuredLogs` is provided then by default Miniflare should not pipe through the stream's output
- () => {}
- : pipeOutput);
-
- handleRuntimeStdio(
- runtimeProcess.stdout.pipe(startupLogBuffer.stdoutStream),
- runtimeProcess.stderr.pipe(startupLogBuffer.stderrStream)
+ const stdoutStream = runtimeProcess.stdout.pipe(
+ startupLogBuffer.stdoutStream
+ );
+ const stderrStream = runtimeProcess.stderr.pipe(
+ startupLogBuffer.stderrStream
);
- if (options.handleStructuredLogs) {
- handleStructuredLogsFromStream(
- startupLogBuffer.stdoutStream,
- options.handleStructuredLogs
- );
- handleStructuredLogsFromStream(
- startupLogBuffer.stderrStream,
- options.handleStructuredLogs
- );
- }
+ const structuredLogsHandler =
+ options.handleStructuredLogs ?? defaultStructuredLogsHandler;
+ handleStructuredLogsFromStream(stdoutStream, structuredLogsHandler);
+ handleStructuredLogsFromStream(stderrStream, structuredLogsHandler);
const controlPipe = runtimeProcess.stdio[3];
assert(controlPipe instanceof Readable);
@@ -385,21 +378,3 @@ export class Runtime {
}
export * from "./config";
-
-/**
- * Gets a safe compatibility date from workerd. If the workerd compatibility
- * date is in the future, returns today's date instead. This handles the case
- * where workerd releases set their compatibility date up to 7 days in the future.
- */
-function getSafeCompatibilityDate(): string {
- const today = new Date().toISOString().slice(0, 10);
- if (workerdCompatibilityDate > today) {
- return today;
- }
- return workerdCompatibilityDate;
-}
-
-/**
- * @deprecated Use today's date as the compatibility date instead: `new Date().toISOString().slice(0, 10)`
- */
-export const supportedCompatibilityDate = getSafeCompatibilityDate();
diff --git a/packages/miniflare/src/shared/colour.ts b/packages/miniflare/src/shared/colour.ts
deleted file mode 100644
index 6b87d80372f..00000000000
--- a/packages/miniflare/src/shared/colour.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { $ as $colors } from "kleur/colors";
-
-const originalEnabled = $colors.enabled;
-
-// `kleur` is marked as a dev dependency, so will get bundled. We'd still like
-// to be able to control whether it's enabled in tests though. Therefore, export
-// a function that toggles the enabled state of our bundled version.
-export function _forceColour(enabled = originalEnabled) {
- $colors.enabled = enabled;
-}
diff --git a/packages/miniflare/src/shared/error.ts b/packages/miniflare/src/shared/error.ts
index add9fc4fcfa..91aeee011ed 100644
--- a/packages/miniflare/src/shared/error.ts
+++ b/packages/miniflare/src/shared/error.ts
@@ -13,15 +13,12 @@ export const USER_ERROR_CODES = new Set([
"ERR_DIFFERENT_STORAGE_BACKEND", // Multiple Durable Object bindings declared for same class with different storage backends
"ERR_DIFFERENT_UNIQUE_KEYS", // Multiple Durable Object bindings declared for same class with different unsafe unique keys
"ERR_DIFFERENT_PREVENT_EVICTION", // Multiple Durable Object bindings declared for same class with different unsafe prevent eviction values
- "ERR_MULTIPLE_OUTBOUNDS", // Both `outboundService` and `fetchMock` specified
- "ERR_INVALID_WRAPPED", // Worker not allowed to be used as wrapped binding
"ERR_MISSING_INSPECTOR_PROXY_PORT", // An inspector proxy has been requested but no inspector port to use has been specified
"ERR_MISSING_EXPLORER_UI", // Local Explorer enabled but assets not found at expected path
] as const);
export const SYSTEM_ERROR_CODES = new Set([
"ERR_RUNTIME_FAILURE", // Runtime failed to start
- "ERR_CYCLIC", // Generate cyclic workerd config
"ERR_PLUGIN_LOADING_FAILED",
] as const);
diff --git a/packages/miniflare/src/shared/index.ts b/packages/miniflare/src/shared/index.ts
index 253f2e50a53..b1c29594045 100644
--- a/packages/miniflare/src/shared/index.ts
+++ b/packages/miniflare/src/shared/index.ts
@@ -1,4 +1,3 @@
-export * from "./colour";
export * from "./error";
export * from "./event";
export * from "./log";
diff --git a/packages/miniflare/src/shared/types.ts b/packages/miniflare/src/shared/types.ts
index 4597d1483b7..83ea86c5e2d 100644
--- a/packages/miniflare/src/shared/types.ts
+++ b/packages/miniflare/src/shared/types.ts
@@ -1,16 +1,14 @@
import assert from "node:assert";
import path from "node:path";
import { z } from "zod";
-import type { ParseParams } from "zod";
-
-export function zAwaitable(
+export function zAwaitable(
type: T
): z.ZodUnion<[T, z.ZodPromise]> {
return type.or(z.promise(type));
}
-export type OptionalZodTypeOf =
- T extends z.ZodTypeAny ? z.TypeOf : undefined;
+export type OptionalZodTypeOf =
+ T extends z.ZodType ? z.output : undefined;
// https://github.com/colinhacks/zod/blob/59768246aa57133184b2cf3f7c2a1ba5c3ab08c3/README.md?plain=1#L1302-L1317
export const LiteralSchema = z.union([
@@ -22,19 +20,30 @@ export const LiteralSchema = z.union([
export type Literal = z.infer;
export type Json = Literal | { [key: string]: Json } | Json[];
export const JsonSchema: z.ZodType = z.lazy(() =>
- z.union([LiteralSchema, z.array(JsonSchema), z.record(JsonSchema)])
+ z.union([
+ LiteralSchema,
+ z.array(JsonSchema),
+ z.record(z.string(), JsonSchema),
+ ])
);
let rootPath: string | undefined;
-export function parseWithRootPath(
+export function parseWithRootPath(
newRootPath: string,
schema: Z,
data: unknown,
- params?: Partial
+ options?: { path?: (string | number)[] }
): z.infer {
rootPath = newRootPath;
try {
- return schema.parse(data, params);
+ return schema.parse(data);
+ } catch (e) {
+ if (options?.path && e instanceof z.ZodError) {
+ for (const issue of e.issues) {
+ issue.path.unshift(...options.path);
+ }
+ }
+ throw e;
} finally {
rootPath = undefined;
}
diff --git a/packages/miniflare/src/workers/assets/rpc-proxy.worker.ts b/packages/miniflare/src/workers/assets/rpc-proxy.worker.ts
index 8ed03b60098..e2d36a77442 100644
--- a/packages/miniflare/src/workers/assets/rpc-proxy.worker.ts
+++ b/packages/miniflare/src/workers/assets/rpc-proxy.worker.ts
@@ -30,7 +30,7 @@ export default class RPCProxyWorker extends WorkerEntrypoint {
// any scheduled logic; it just dispatches a real scheduled event to the user
// worker via the Fetcher built-in, then propagates the user worker's noRetry
// decision back onto this controller so the outcome surfaces correctly to
- // the caller (e.g. the entry worker's `/cdn-cgi/handler/scheduled` handler).
+ // the caller (e.g. the entry worker's `/cdn-cgi/local/scheduled` handler).
async scheduled(controller: ScheduledController) {
const result = await this.env.USER_WORKER.scheduled?.({
cron: controller.cron,
diff --git a/packages/miniflare/src/workers/cache/cache-entry.worker.ts b/packages/miniflare/src/workers/cache/cache-entry.worker.ts
index 8112c5ef9a8..f5627ed06c6 100644
--- a/packages/miniflare/src/workers/cache/cache-entry.worker.ts
+++ b/packages/miniflare/src/workers/cache/cache-entry.worker.ts
@@ -1,11 +1,9 @@
import { SharedBindings } from "miniflare:shared";
-import { CacheBindings, CacheHeaders } from "./constants";
-import type { CacheObjectCf } from "./constants";
+import { CacheHeaders } from "./constants";
import type { MiniflareDurableObjectCf } from "miniflare:shared";
interface Env {
[SharedBindings.DURABLE_OBJECT_NAMESPACE_OBJECT]: DurableObjectNamespace;
- [CacheBindings.MAYBE_JSON_CACHE_WARN_USAGE]?: boolean;
}
export default >{
@@ -16,11 +14,10 @@ export default >{
const objectNamespace = env[SharedBindings.DURABLE_OBJECT_NAMESPACE_OBJECT];
const id = objectNamespace.idFromName(name);
const stub = objectNamespace.get(id);
- const cf: MiniflareDurableObjectCf & CacheObjectCf = {
+ const cf: MiniflareDurableObjectCf = {
...request.cf,
miniflare: {
name,
- cacheWarnUsage: env[CacheBindings.MAYBE_JSON_CACHE_WARN_USAGE],
},
};
return await stub.fetch(request, { cf: cf as Record });
diff --git a/packages/miniflare/src/workers/cache/cache.worker.ts b/packages/miniflare/src/workers/cache/cache.worker.ts
index 95f97978dcb..431d3ece295 100644
--- a/packages/miniflare/src/workers/cache/cache.worker.ts
+++ b/packages/miniflare/src/workers/cache/cache.worker.ts
@@ -6,7 +6,6 @@ import {
DELETE,
GET,
KeyValueStorage,
- LogLevel,
MiniflareDurableObject,
parseRanges,
PURGE,
@@ -19,7 +18,6 @@ import {
RangeNotSatisfiable,
StorageFailure,
} from "./errors.worker";
-import type { CacheObjectCf } from "./constants";
import type {
InclusiveRange,
MiniflareDurableObjectCf,
@@ -36,7 +34,7 @@ interface CacheMetadata {
type CacheRouteHandler = RouteHandler<
unknown,
- RequestInitCfProperties & MiniflareDurableObjectCf & CacheObjectCf
+ RequestInitCfProperties & MiniflareDurableObjectCf
>;
function getCacheKey(req: Request) {
@@ -265,17 +263,6 @@ class SizingStream extends TransformStream {
}
export class CacheObject extends MiniflareDurableObject {
- #warnedUsage = false;
- async #maybeWarnUsage(request: Request) {
- if (!this.#warnedUsage && request.cf?.miniflare?.cacheWarnUsage === true) {
- this.#warnedUsage = true;
- await this.logWithLevel(
- LogLevel.WARN,
- "Cache operations will have no impact if you deploy to a workers.dev subdomain!"
- );
- }
- }
-
#storage?: KeyValueStorage;
get storage() {
// `KeyValueStorage` can only be constructed once `this.blob` is initialised
@@ -284,7 +271,6 @@ export class CacheObject extends MiniflareDurableObject {
@GET()
match: CacheRouteHandler = async (req) => {
- await this.#maybeWarnUsage(req);
const cacheKey = getCacheKey(req);
// Never cache Workers Sites requests, so we always return on-disk files
@@ -330,7 +316,6 @@ export class CacheObject extends MiniflareDurableObject {
@PUT()
put: CacheRouteHandler = async (req) => {
- await this.#maybeWarnUsage(req);
const cacheKey = getCacheKey(req);
// Never cache Workers Sites requests, so we always return on-disk files
@@ -384,7 +369,6 @@ export class CacheObject extends MiniflareDurableObject {
@PURGE()
delete: CacheRouteHandler = async (req) => {
- await this.#maybeWarnUsage(req);
const cacheKey = getCacheKey(req);
const deleted = await this.storage.delete(cacheKey);
diff --git a/packages/miniflare/src/workers/cache/constants.ts b/packages/miniflare/src/workers/cache/constants.ts
index 1c807598888..1a712143748 100644
--- a/packages/miniflare/src/workers/cache/constants.ts
+++ b/packages/miniflare/src/workers/cache/constants.ts
@@ -2,11 +2,3 @@ export const CacheHeaders = {
NAMESPACE: "cf-cache-namespace",
STATUS: "cf-cache-status",
} as const;
-
-export const CacheBindings = {
- MAYBE_JSON_CACHE_WARN_USAGE: "MINIFLARE_CACHE_WARN_USAGE",
-} as const;
-
-export interface CacheObjectCf {
- miniflare?: { cacheWarnUsage?: boolean };
-}
diff --git a/packages/miniflare/src/workers/core/constants.ts b/packages/miniflare/src/workers/core/constants.ts
index 79a397917c9..a72c648eccb 100644
--- a/packages/miniflare/src/workers/core/constants.ts
+++ b/packages/miniflare/src/workers/core/constants.ts
@@ -1,26 +1,23 @@
/**
- * Reserved `/cdn-cgi/` paths for internal Miniflare endpoints.
- * These paths are reserved by Cloudflare's network and won't conflict with user routes.
+ * Reserved paths for internal Miniflare endpoints.
+ *
+ * Paths under `/cdn-cgi/local/` are reserved by Cloudflare's network
+ * and won't conflict with user routes. Paths under `/__cf_local/` live
+ * outside `/cdn-cgi/` so they remain reachable over tunnels.
*/
export const CorePaths = {
/** Magic proxy used by getPlatformProxy */
- PLATFORM_PROXY: "/cdn-cgi/platform-proxy",
+ PLATFORM_PROXY: "/cdn-cgi/local/platform-proxy",
/** Trigger scheduled event handlers */
- SCHEDULED: "/cdn-cgi/handler/scheduled",
+ SCHEDULED: "/cdn-cgi/local/scheduled",
/** Trigger email event handlers */
- EMAIL: "/cdn-cgi/handler/email",
- /** Handler path prefix for validation */
- HANDLER_PREFIX: "/cdn-cgi/handler/",
- /** Live reload WebSocket endpoint */
- LIVE_RELOAD: "/cdn-cgi/mf/reload",
+ EMAIL: "/cdn-cgi/local/email",
/** Local explorer UI and API */
- EXPLORER: "/cdn-cgi/explorer",
- /** Legacy way to trigger scheduled event handlers */
- LEGACY_SCHEDULED: "/cdn-cgi/mf/scheduled",
- /** Stream video serving endpoint */
- STREAM_VIDEO: "/cdn-cgi/mf/stream",
- /** Local image delivery endpoint for serving hosted images */
- IMAGE_DELIVERY: "/cdn-cgi/mf/imagedelivery",
+ EXPLORER: "/cdn-cgi/local/explorer",
+ /** Stream video serving endpoint (outside /cdn-cgi/ for tunnel access) */
+ STREAM_VIDEO: "/__cf_local/stream",
+ /** Local image delivery endpoint (outside /cdn-cgi/ for tunnel access) */
+ IMAGE_DELIVERY: "/__cf_local/imagedelivery",
/** Public R2 bucket object serving endpoint */
R2_PUBLIC: "/cdn-cgi/local/r2/public",
} as const;
@@ -75,7 +72,6 @@ export const CoreBindings = {
JSON_CF_BLOB: "CF_BLOB",
JSON_ROUTES: "MINIFLARE_ROUTES",
JSON_LOG_LEVEL: "MINIFLARE_LOG_LEVEL",
- DATA_LIVE_RELOAD_SCRIPT: "MINIFLARE_LIVE_RELOAD_SCRIPT",
DURABLE_OBJECT_NAMESPACE_PROXY: "MINIFLARE_PROXY",
DATA_PROXY_SECRET: "MINIFLARE_PROXY_SECRET",
DATA_PROXY_SHARED_SECRET: "MINIFLARE_PROXY_SHARED_SECRET",
diff --git a/packages/miniflare/src/workers/core/dev-registry-proxy.worker.ts b/packages/miniflare/src/workers/core/dev-registry-proxy.worker.ts
index 392f46bd9e3..8fc05d10d87 100644
--- a/packages/miniflare/src/workers/core/dev-registry-proxy.worker.ts
+++ b/packages/miniflare/src/workers/core/dev-registry-proxy.worker.ts
@@ -1,5 +1,6 @@
import { WorkerEntrypoint } from "cloudflare:workers";
import { getQueueServiceName, HEADER_QUEUE_NAME } from "../queues/constants";
+import { CorePaths } from "./constants";
import {
findQueueConsumer,
resolveTarget,
@@ -145,7 +146,7 @@ export class ExternalServiceProxy extends WorkerEntrypoint {
params.set("time", String(controller.scheduledTime));
}
const response = await this._entryFetcher.fetch(
- new Request(`http://localhost/cdn-cgi/handler/scheduled?${params}`, {
+ new Request(`http://localhost${CorePaths.SCHEDULED}?${params}`, {
headers: { "MF-Route-Override": this.ctx.props.service },
})
);
diff --git a/packages/miniflare/src/workers/core/entry.worker.ts b/packages/miniflare/src/workers/core/entry.worker.ts
index bcea7b82079..673100f8ce6 100644
--- a/packages/miniflare/src/workers/core/entry.worker.ts
+++ b/packages/miniflare/src/workers/core/entry.worker.ts
@@ -26,7 +26,6 @@ type Env = {
[CoreBindings.JSON_CF_BLOB]: IncomingRequestCfProperties;
[CoreBindings.JSON_ROUTES]: WorkerRoute[];
[CoreBindings.JSON_LOG_LEVEL]: LogLevel;
- [CoreBindings.DATA_LIVE_RELOAD_SCRIPT]?: ArrayBuffer;
[CoreBindings.DURABLE_OBJECT_NAMESPACE_PROXY]: DurableObjectNamespace;
[CoreBindings.DATA_PROXY_SHARED_SECRET]?: ArrayBuffer;
[CoreBindings.TRIGGER_HANDLERS]: boolean;
@@ -294,45 +293,6 @@ function maybePrettifyError(request: Request, response: Response, env: Env) {
);
}
-function maybeInjectLiveReload(
- response: Response,
- env: Env,
- ctx: ExecutionContext
-) {
- const liveReloadScript = env[CoreBindings.DATA_LIVE_RELOAD_SCRIPT];
- if (
- liveReloadScript === undefined ||
- !response.headers.get("Content-Type")?.toLowerCase().includes("text/html")
- ) {
- return response;
- }
-
- const headers = new Headers(response.headers);
- const contentLength = parseInt(headers.get("content-length") ?? "NaN");
- if (!isNaN(contentLength)) {
- headers.set(
- "content-length",
- String(contentLength + liveReloadScript.byteLength)
- );
- }
-
- const { readable, writable } = new IdentityTransformStream();
- ctx.waitUntil(
- (async () => {
- await response.body?.pipeTo(writable, { preventClose: true });
- const writer = writable.getWriter();
- await writer.write(liveReloadScript);
- await writer.close();
- })()
- );
-
- return new Response(readable, {
- status: response.status,
- statusText: response.statusText,
- headers,
- });
-}
-
const acceptEncodingElement =
/^(?[a-z]+|\*)(?:\s*;\s*q=(?\d+(?:.\d+)?))?$/;
interface AcceptedEncoding {
@@ -572,24 +532,7 @@ export default >{
return await imagesDelivery.fetch(request);
}
if (env[CoreBindings.TRIGGER_HANDLERS]) {
- if (
- url.pathname === CorePaths.SCHEDULED ||
- /* legacy URL path */ url.pathname === CorePaths.LEGACY_SCHEDULED
- ) {
- if (url.pathname === CorePaths.LEGACY_SCHEDULED) {
- ctx.waitUntil(
- env[CoreBindings.SERVICE_LOOPBACK].fetch(
- "http://localhost/core/log",
- {
- method: "POST",
- headers: {
- [SharedHeaders.LOG_LEVEL]: LogLevel.WARN.toString(),
- },
- body: `Triggering scheduled handlers via a request to \`${CorePaths.LEGACY_SCHEDULED}\` is deprecated, and will be removed in a future version of Miniflare. Instead, send a request to \`${CorePaths.SCHEDULED}\``,
- }
- )
- );
- }
+ if (url.pathname === CorePaths.SCHEDULED) {
return await handleScheduled(url.searchParams, service);
}
@@ -602,13 +545,6 @@ export default >{
ctx
);
}
-
- if (url.pathname.startsWith(CorePaths.HANDLER_PREFIX)) {
- return new Response(
- `"${url.pathname}" is not a valid handler. Did you mean to use "${CorePaths.SCHEDULED}" or "${CorePaths.EMAIL}"?`,
- { status: 404 }
- );
- }
}
const streamService = env[CoreBindings.SERVICE_STREAM];
@@ -633,7 +569,6 @@ export default >{
if (!disablePrettyErrorPage) {
response = await maybePrettifyError(request, response, env);
}
- response = maybeInjectLiveReload(response, env, ctx);
response = ensureAcceptableEncoding(clientAcceptEncoding, response);
if (env[CoreBindings.LOG_REQUESTS]) {
response = maybeLogRequest(request, response, env, ctx, startTime);
diff --git a/packages/miniflare/src/workers/images/images.worker.ts b/packages/miniflare/src/workers/images/images.worker.ts
index 1f47dfaba81..5715b2af34e 100644
--- a/packages/miniflare/src/workers/images/images.worker.ts
+++ b/packages/miniflare/src/workers/images/images.worker.ts
@@ -251,7 +251,7 @@ export default class ImagesService extends WorkerEntrypoint {
async fetch(request: Request): Promise {
const url = new URL(request.url);
- // Serve image bytes at /cdn-cgi/mf/imagedelivery//
+ // Serve image bytes at /__cf_local/imagedelivery//
if (url.pathname.startsWith(`${CorePaths.IMAGE_DELIVERY}/`)) {
const parts = url.pathname
.slice(CorePaths.IMAGE_DELIVERY.length + 1)
diff --git a/packages/miniflare/src/workers/local-explorer/common.ts b/packages/miniflare/src/workers/local-explorer/common.ts
index 7ce298c8215..dae29010a53 100644
--- a/packages/miniflare/src/workers/local-explorer/common.ts
+++ b/packages/miniflare/src/workers/local-explorer/common.ts
@@ -20,12 +20,14 @@ export type AppContext = Context;
*
* If the whole query param is optional, you need to unwrap it before passing to this function.
*/
-export function validateQuery(schema: T) {
+export function validateQuery(schema: T) {
return validator("query", async (value, c) => {
- let result: z.SafeParseReturnType, z.output>;
+ let result:
+ | { success: true; data: z.output }
+ | { success: false; error: z.ZodError };
try {
const coerced = coerceValue(schema, value);
- result = await schema.safeParseAsync(coerced);
+ result = (await schema.safeParseAsync(coerced)) as typeof result;
} catch (error) {
if (error instanceof z.ZodError) {
return validationHook({ success: false, error }, c);
@@ -42,7 +44,7 @@ export function validateQuery(schema: T) {
/**
* validates request body according to openapi schema
*/
-export function validateRequestBody(schema: T) {
+export function validateRequestBody(schema: T) {
return validator("json", async (value, c) => {
const result = await schema.safeParseAsync(value);
if (!result.success) {
@@ -63,14 +65,14 @@ export function validateRequestBody(schema: T) {
* 3. Arrays/Objects: We need to recursively coerce nested values
*/
export function coerceValue(
- schema: z.ZodTypeAny,
+ schema: z.ZodType,
value: unknown,
path: (string | number)[] = []
): unknown {
// Unwrap optional/default to get inner type
if (schema instanceof z.ZodOptional || schema instanceof z.ZodDefault) {
if (value === undefined) return value;
- return coerceValue(schema._def.innerType, value, path);
+ return coerceValue(schema._zod.def.innerType as z.ZodType, value, path);
}
if (schema instanceof z.ZodNumber && typeof value === "string") {
@@ -78,9 +80,8 @@ export function coerceValue(
if (isNaN(num)) {
throw new z.ZodError([
{
- code: z.ZodIssueCode.invalid_type,
+ code: "invalid_type",
expected: "number",
- received: "string",
path,
message: `Expected query param to be number but received "${value}"`,
},
@@ -94,9 +95,8 @@ export function coerceValue(
if (value === "false") return false;
throw new z.ZodError([
{
- code: z.ZodIssueCode.invalid_type,
+ code: "invalid_type",
expected: "boolean",
- received: "string",
path,
message: `Expected query param to be 'true' or 'false' but received "${value}"`,
},
@@ -105,7 +105,7 @@ export function coerceValue(
if (schema instanceof z.ZodArray && Array.isArray(value)) {
return value.map((item, index) =>
- coerceValue(schema.element, item, [...path, index])
+ coerceValue(schema.element as z.ZodType, item, [...path, index])
);
}
@@ -118,7 +118,7 @@ export function coerceValue(
for (const [key, propSchema] of Object.entries(schema.shape)) {
if (key in value) {
result[key] = coerceValue(
- propSchema as z.ZodTypeAny,
+ propSchema as z.ZodType,
(value as Record)[key],
[...path, key]
);
@@ -136,7 +136,7 @@ export function validationHook(
result: { success: false; error: z.ZodError },
c: Context
): Response {
- const errors = result.error.errors.map((e) => {
+ const errors = result.error.issues.map((e) => {
const message =
e.path.length > 0 ? `${e.path.join(".")}: ${e.message}` : e.message;
diff --git a/packages/miniflare/src/workers/local-explorer/generated/types.gen.ts b/packages/miniflare/src/workers/local-explorer/generated/types.gen.ts
index 215f2aab522..7d76dd79135 100644
--- a/packages/miniflare/src/workers/local-explorer/generated/types.gen.ts
+++ b/packages/miniflare/src/workers/local-explorer/generated/types.gen.ts
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts
export type ClientOptions = {
- baseUrl: `${string}://${string}/cdn-cgi/explorer/api` | (string & {});
+ baseUrl: `${string}://${string}/cdn-cgi/local/explorer/api` | (string & {});
};
export type R2V4Response = {
diff --git a/packages/miniflare/src/workers/local-explorer/generated/zod.gen.ts b/packages/miniflare/src/workers/local-explorer/generated/zod.gen.ts
index a9a0769ff60..5d30d46326a 100644
--- a/packages/miniflare/src/workers/local-explorer/generated/zod.gen.ts
+++ b/packages/miniflare/src/workers/local-explorer/generated/zod.gen.ts
@@ -1,12 +1,12 @@
// This file is auto-generated by @hey-api/openapi-ts
-import { z } from "zod";
+import * as z from "zod";
export const zR2Messages = z.array(z.string());
export const zR2Errors = z.array(
z.object({
- code: z.number().int().gte(1000),
+ code: z.int().gte(1000),
message: z.string(),
})
);
@@ -14,7 +14,7 @@ export const zR2Errors = z.array(
export const zR2V4Response = z.object({
errors: zR2Errors,
messages: zR2Messages,
- result: z.record(z.unknown()),
+ result: z.record(z.string(), z.unknown()),
success: z.literal(true),
});
@@ -42,7 +42,7 @@ export const zR2Bucket = z.object({
name: zR2BucketName.optional(),
});
-export const zR2ResultInfo = z.record(z.unknown());
+export const zR2ResultInfo = z.record(z.string(), z.unknown());
export const zR2V4ResponseList = zR2V4Response.and(
z.object({
@@ -68,7 +68,7 @@ export const zWorkersSchemasId = z.string();
export const zWorkersMessages = z.array(
z.object({
- code: z.number().int().gte(1000),
+ code: z.int().gte(1000),
documentation_url: z.string().optional(),
message: z.string(),
source: z
@@ -136,7 +136,9 @@ export const zD1RawResultResponse = z.object({
columns: z.array(z.string()).optional(),
rows: z
.array(
- z.array(z.union([z.number(), z.string(), z.record(z.unknown())]))
+ z.array(
+ z.union([z.number(), z.string(), z.record(z.string(), z.unknown())])
+ )
)
.optional(),
})
@@ -178,7 +180,7 @@ export const zD1DatabaseIdentifier = z.string().readonly();
export const zD1Messages = z.array(
z.object({
- code: z.number().int().gte(1000),
+ code: z.int().gte(1000),
message: z.string(),
})
);
@@ -215,14 +217,14 @@ export const zD1ApiResponseCommon = z.object({
success: z.literal(true),
});
-export const zWorkersKvAny: z.ZodTypeAny = z
+export const zWorkersKvAny = z
.union([
z.string(),
z.number(),
- z.number().int(),
+ z.int(),
z.boolean(),
- z.record(z.unknown()),
- z.array(z.lazy(() => zWorkersKvAny)),
+ z.record(z.string(), z.unknown()),
+ z.array(z.lazy((): any => zWorkersKvAny)),
])
.nullable();
@@ -234,6 +236,7 @@ export const zWorkersKvExpiration = z.number();
export const zWorkersKvBulkGetResultWithMetadata = z.object({
values: z
.record(
+ z.string(),
z
.object({
expiration: zWorkersKvExpiration.optional(),
@@ -248,8 +251,14 @@ export const zWorkersKvBulkGetResultWithMetadata = z.object({
export const zWorkersKvBulkGetResult = z.object({
values: z
.record(
+ z.string(),
z
- .union([z.string(), z.number(), z.boolean(), z.record(z.unknown())])
+ .union([
+ z.string(),
+ z.number(),
+ z.boolean(),
+ z.record(z.string(), z.unknown()),
+ ])
.nullable()
)
.optional(),
@@ -262,7 +271,7 @@ export const zWorkersKvKeyNameBulk = z.string().max(512);
export const zWorkersKvMessages = z.array(
z.object({
- code: z.number().int().gte(1000),
+ code: z.int().gte(1000),
message: z.string(),
})
);
@@ -276,7 +285,7 @@ export const zWorkersKvApiResponseCommon = z.object({
export const zWorkersKvApiResponseCommonNoResult =
zWorkersKvApiResponseCommon.and(
z.object({
- result: z.record(z.unknown()).nullish(),
+ result: z.record(z.string(), z.unknown()).nullish(),
})
);
@@ -321,7 +330,7 @@ export const zWorkersKvNamespaceIdentifier = z.string().max(32).readonly();
export const zWorkersKvApiResponseCommonFailure = z.object({
errors: zWorkersKvMessages,
messages: zWorkersKvMessages,
- result: z.record(z.unknown()).nullable(),
+ result: z.record(z.string(), z.unknown()).nullable(),
success: z.literal(false),
});
@@ -348,10 +357,10 @@ export const zWorkersKvApiResponseCollection = zWorkersKvApiResponseCommon.and(
export const zR2Object = z.object({
key: z.string().optional(),
etag: z.string().optional(),
- size: z.number().int().optional(),
- last_modified: z.string().datetime().optional(),
- http_metadata: z.record(z.string()).optional(),
- custom_metadata: z.record(z.string()).optional(),
+ size: z.int().optional(),
+ last_modified: z.iso.datetime().optional(),
+ http_metadata: z.record(z.string(), z.string()).optional(),
+ custom_metadata: z.record(z.string(), z.string()).optional(),
});
export const zR2ListObjectsResultInfo = z.object({
@@ -364,15 +373,15 @@ export const zR2HeadObjectResult = z.object({
key: z.string().optional(),
etag: z.string().optional(),
last_modified: z.string().optional(),
- size: z.number().int().optional(),
- http_metadata: z.record(z.string()).optional(),
- custom_metadata: z.record(z.string()).optional(),
+ size: z.int().optional(),
+ http_metadata: z.record(z.string(), z.string()).optional(),
+ custom_metadata: z.record(z.string(), z.string()).optional(),
});
export const zR2PutObjectResult = z.object({
key: z.string().optional(),
etag: z.string().optional(),
- size: z.number().int().optional(),
+ size: z.int().optional(),
version: z.string().optional(),
});
@@ -511,7 +520,7 @@ export const zWorkflowsInstanceDetails = z.object({
.optional(),
});
-export const zR2ResultInfoWritable = z.record(z.unknown());
+export const zR2ResultInfoWritable = z.record(z.string(), z.unknown());
export const zWorkersNamespaceWritable = z.object({
class: z.string().optional(),
@@ -526,14 +535,14 @@ export const zD1DatabaseResponseWritable = z.object({
version: zD1DatabaseVersion.optional(),
});
-export const zWorkersKvAnyWritable: z.ZodTypeAny = z
+export const zWorkersKvAnyWritable = z
.union([
z.string(),
z.number(),
- z.number().int(),
+ z.int(),
z.boolean(),
- z.record(z.unknown()),
- z.array(z.lazy(() => zWorkersKvAnyWritable)),
+ z.record(z.string(), z.unknown()),
+ z.array(z.lazy((): any => zWorkersKvAnyWritable)),
])
.nullable();
@@ -816,7 +825,7 @@ export const zR2BucketListObjectsData = z.object({
prefix: z.string().optional(),
delimiter: z.string().optional(),
cursor: z.string().optional(),
- per_page: z.number().int().optional().default(1000),
+ per_page: z.int().optional().default(1000),
})
.optional(),
});
@@ -1086,7 +1095,7 @@ export const zWorkflowsChangeInstanceStatusData = z.object({
from: z
.object({
name: z.string(),
- count: z.number().int().gte(1).optional(),
+ count: z.int().gte(1).optional(),
type: z.enum(["do", "sleep", "waitForEvent"]).optional(),
})
.optional(),
diff --git a/packages/miniflare/src/workers/local-explorer/openapi.local.json b/packages/miniflare/src/workers/local-explorer/openapi.local.json
index 47357fcdbb0..310013567b4 100644
--- a/packages/miniflare/src/workers/local-explorer/openapi.local.json
+++ b/packages/miniflare/src/workers/local-explorer/openapi.local.json
@@ -8,7 +8,7 @@
"servers": [
{
"description": "Local Explorer",
- "url": "/cdn-cgi/explorer/api"
+ "url": "/cdn-cgi/local/explorer/api"
}
],
"paths": {
diff --git a/packages/miniflare/src/workers/local-explorer/route-names.ts b/packages/miniflare/src/workers/local-explorer/route-names.ts
index f6afe51f55b..c5a21a7805f 100644
--- a/packages/miniflare/src/workers/local-explorer/route-names.ts
+++ b/packages/miniflare/src/workers/local-explorer/route-names.ts
@@ -36,8 +36,8 @@ const ROUTE_PATTERNS: [RegExp, string][] = [
* Strips IDs and converts to dot notation.
*/
export function getRouteName(path: string): string {
- // Remove /cdn-cgi/explorer/api prefix
- const apiPath = path.replace(/^\/cdn-cgi\/explorer\/api/, "");
+ // Remove /cdn-cgi/local/explorer/api prefix
+ const apiPath = path.replace(/^\/cdn-cgi\/local\/explorer\/api/, "");
for (const [pattern, name] of ROUTE_PATTERNS) {
if (pattern.test(apiPath)) {
diff --git a/packages/miniflare/src/workers/queues/schemas.ts b/packages/miniflare/src/workers/queues/schemas.ts
index d714a273ce1..458d8f8f8de 100644
--- a/packages/miniflare/src/workers/queues/schemas.ts
+++ b/packages/miniflare/src/workers/queues/schemas.ts
@@ -19,26 +19,17 @@ export const QueueProducerSchema = /* @__PURE__ */ z.intersection(
);
export type QueueProducer = z.infer;
export const QueueProducersSchema =
- /* @__PURE__ */ z.record(QueueProducerSchema);
+ /* @__PURE__ */ z.record(z.string(), QueueProducerSchema);
-export const QueueConsumerOptionsSchema = /* @__PURE__ */ z
- .object({
- // https://developers.cloudflare.com/queues/platform/configuration/#consumer
- // https://developers.cloudflare.com/queues/platform/limits/
- maxBatchSize: z.number().min(0).max(100).optional(),
- maxBatchTimeout: z.number().min(0).max(60).optional(), // seconds
- maxRetires: z.number().min(0).max(100).optional(), // deprecated
- maxRetries: z.number().min(0).max(100).optional(),
- deadLetterQueue: z.ostring(),
- retryDelay: QueueMessageDelaySchema,
- })
- .transform((queue) => {
- if (queue.maxRetires !== undefined) {
- queue.maxRetries = queue.maxRetires;
- }
-
- return queue as Omit;
- });
+export const QueueConsumerOptionsSchema = /* @__PURE__ */ z.object({
+ // https://developers.cloudflare.com/queues/platform/configuration/#consumer
+ // https://developers.cloudflare.com/queues/platform/limits/
+ maxBatchSize: z.number().min(0).max(100).optional(),
+ maxBatchTimeout: z.number().min(0).max(60).optional(), // seconds
+ maxRetries: z.number().min(0).max(100).optional(),
+ deadLetterQueue: z.string().optional(),
+ retryDelay: QueueMessageDelaySchema,
+});
export const QueueConsumerSchema = /* @__PURE__ */ z.intersection(
QueueConsumerOptionsSchema,
z.object({ workerName: z.string() })
@@ -49,7 +40,7 @@ export type QueueConsumer = z.infer;
// queues. Support for multiple consumers of a single queue is not planned
// anytime soon.
export const QueueConsumersSchema =
- /* @__PURE__ */ z.record(QueueConsumerSchema);
+ /* @__PURE__ */ z.record(z.string(), QueueConsumerSchema);
export const QueueContentTypeSchema = /* @__PURE__ */ z
.enum(["text", "json", "bytes", "v8"])
@@ -64,8 +55,8 @@ export const QueueIncomingMessageSchema = /* @__PURE__ */ z.object({
body: Base64DataSchema,
// When enqueuing messages on dead-letter queues, we want to reuse the same ID
// and timestamp
- id: z.ostring(),
- timestamp: z.onumber(),
+ id: z.string().optional(),
+ timestamp: z.number().optional(),
});
export type QueueIncomingMessage = z.infer;
export type QueueOutgoingMessage = z.input;
diff --git a/packages/miniflare/src/workers/r2/schemas.worker.ts b/packages/miniflare/src/workers/r2/schemas.worker.ts
index 2cd2c8177f0..3ce0d31c9e1 100644
--- a/packages/miniflare/src/workers/r2/schemas.worker.ts
+++ b/packages/miniflare/src/workers/r2/schemas.worker.ts
@@ -113,7 +113,7 @@ export const R2ConditionalSchema = z.object({
// Performs the operation if the object was uploaded AFTER the given date
uploadedAfter: DateSchema.optional(), // "If-Modified-Since"
// Truncates dates to seconds before performing comparisons
- secondsGranularity: z.oboolean(),
+ secondsGranularity: z.boolean().optional(),
});
export type R2Conditional = z.infer;
@@ -142,11 +142,11 @@ export const R2PublishedPartSchema = z.object({
export type R2PublishedPart = z.infer;
export const R2HttpFieldsSchema = z.object({
- contentType: z.ostring(),
- contentLanguage: z.ostring(),
- contentDisposition: z.ostring(),
- contentEncoding: z.ostring(),
- cacheControl: z.ostring(),
+ contentType: z.string().optional(),
+ contentLanguage: z.string().optional(),
+ contentDisposition: z.string().optional(),
+ contentEncoding: z.string().optional(),
+ cacheControl: z.string().optional(),
cacheExpiry: z.coerce.number().optional(),
});
export type R2HttpFields = z.infer;
@@ -163,7 +163,7 @@ export const R2GetRequestSchema = z.object({
// of bytes from the object should be returned. Refer to
// https://developers.cloudflare.com/r2/runtime-apis/#ranged-reads.
range: R2RangeSchema.optional(),
- rangeHeader: z.ostring(),
+ rangeHeader: z.string().optional(),
// Specifies that the object should only be returned given satisfaction of
// certain conditions in the R2Conditional. Refer to R2Conditional above.
onlyIf: R2ConditionalSchema.optional(),
@@ -231,11 +231,11 @@ export const R2AbortMultipartUploadRequestSchema = z.object({
export const R2ListRequestSchema = z.object({
method: z.literal("list"),
- limit: z.onumber(),
- prefix: z.ostring(),
- cursor: z.ostring(),
- delimiter: z.ostring(),
- startAfter: z.ostring(),
+ limit: z.number().optional(),
+ prefix: z.string().optional(),
+ cursor: z.string().optional(),
+ delimiter: z.string().optional(),
+ startAfter: z.string().optional(),
include: z
.union([z.literal(0), z.literal(1)])
.transform((value) => (value === 0 ? "httpMetadata" : "customMetadata"))
diff --git a/packages/miniflare/src/workers/shared/blob.worker.ts b/packages/miniflare/src/workers/shared/blob.worker.ts
index 2e9743e63ca..c1abb57f888 100644
--- a/packages/miniflare/src/workers/shared/blob.worker.ts
+++ b/packages/miniflare/src/workers/shared/blob.worker.ts
@@ -182,17 +182,16 @@ export class BlobStore {
readonly #fetcher: Fetcher;
readonly #baseURL: string;
- readonly #stickyBlobs: boolean;
- constructor(fetcher: Fetcher, namespace: string, stickyBlobs: boolean) {
+ constructor(fetcher: Fetcher, namespace: string) {
namespace = encodeURIComponent(sanitisePath(namespace));
this.#fetcher = fetcher;
// `baseURL`'s `pathname` (`/${namespace}/blobs/`) is relative to the
- // `*Persist` (e.g. `kvPersist`) option if defined. For example, if
- // `kvPersist` is `/path/to/kv`, the `blobs` directory for a KV namespace
- // with ID `TEST_NAMESPACE` would be `/path/to/kv/TEST_NAMESPACE/blobs`.
+ // plugin's persistence directory under `resourcePersistencePath`. For
+ // example, if the KV persistence directory is `/path/to/kv`, the `blobs`
+ // directory for a KV namespace with ID `TEST_NAMESPACE` would be
+ // `/path/to/kv/TEST_NAMESPACE/blobs`.
this.#baseURL = `http://placeholder/${namespace}/blobs/`;
- this.#stickyBlobs = stickyBlobs;
}
private idURL(id: BlobId) {
@@ -239,8 +238,6 @@ export class BlobStore {
}
async delete(id: BlobId): Promise {
- // If sticky blobs are enabled, don't delete any blobs
- if (this.#stickyBlobs) return;
// Get path for this ID and delete, ignoring if outside root or not found
const idURL = this.idURL(id);
if (idURL === null) return;
diff --git a/packages/miniflare/src/workers/shared/constants.ts b/packages/miniflare/src/workers/shared/constants.ts
index f30006a2319..ef7e61a4331 100644
--- a/packages/miniflare/src/workers/shared/constants.ts
+++ b/packages/miniflare/src/workers/shared/constants.ts
@@ -8,7 +8,6 @@ export const SharedBindings = {
MAYBE_SERVICE_BLOBS: "MINIFLARE_BLOBS",
MAYBE_SERVICE_LOOPBACK: "MINIFLARE_LOOPBACK",
MAYBE_JSON_ENABLE_CONTROL_ENDPOINTS: "MINIFLARE_ENABLE_CONTROL_ENDPOINTS",
- MAYBE_JSON_ENABLE_STICKY_BLOBS: "MINIFLARE_STICKY_BLOBS",
} as const;
export enum LogLevel {
diff --git a/packages/miniflare/src/workers/shared/object.worker.ts b/packages/miniflare/src/workers/shared/object.worker.ts
index c7c637a79e8..54273e243fb 100644
--- a/packages/miniflare/src/workers/shared/object.worker.ts
+++ b/packages/miniflare/src/workers/shared/object.worker.ts
@@ -24,11 +24,6 @@ export interface MiniflareDurableObjectEnv {
// for testing. Note these endpoints allow anyone with access to the Miniflare
// dev server to run arbitrary SQL queries and read arbitrary blobs.
[SharedBindings.MAYBE_JSON_ENABLE_CONTROL_ENDPOINTS]?: boolean;
- // If set to `true`, Miniflare won't delete blobs when deleting/overriding
- // existing keys. This is a requirement for "stacked storage": when popping
- // from the storage stack, we need to guarantee the blobs created in and
- // before that storage stack frame still exist.
- [SharedBindings.MAYBE_JSON_ENABLE_STICKY_BLOBS]?: boolean;
}
export interface MiniflareDurableObjectCfControlOp {
@@ -78,13 +73,11 @@ export abstract class MiniflareDurableObject<
get blob(): BlobStore {
if (this.#blob !== undefined) return this.#blob;
const maybeBlobsService = this.env[SharedBindings.MAYBE_SERVICE_BLOBS];
- const stickyBlobs =
- !!this.env[SharedBindings.MAYBE_JSON_ENABLE_STICKY_BLOBS];
assert(
maybeBlobsService !== undefined,
`Expected ${SharedBindings.MAYBE_SERVICE_BLOBS} service binding`
);
- this.#blob = new BlobStore(maybeBlobsService, this.name, stickyBlobs);
+ this.#blob = new BlobStore(maybeBlobsService, this.name);
return this.#blob;
}
diff --git a/packages/miniflare/src/workers/stream/binding.worker.ts b/packages/miniflare/src/workers/stream/binding.worker.ts
index aea82466120..739f8292e3f 100644
--- a/packages/miniflare/src/workers/stream/binding.worker.ts
+++ b/packages/miniflare/src/workers/stream/binding.worker.ts
@@ -32,7 +32,7 @@ function rowsToDownloadResponse(
export class StreamBinding extends WorkerEntrypoint {
async fetch(request: Request): Promise {
const url = new URL(request.url);
- const match = url.pathname.match(/^\/cdn-cgi\/mf\/stream\/([^/]+)\/watch$/);
+ const match = url.pathname.match(/^\/__cf_local\/stream\/([^/]+)\/watch$/);
if (!match) {
return new Response("Not found", { status: 404 });
}
diff --git a/packages/miniflare/src/workers/stream/object.worker.ts b/packages/miniflare/src/workers/stream/object.worker.ts
index 7e9b2647675..4a230187b87 100644
--- a/packages/miniflare/src/workers/stream/object.worker.ts
+++ b/packages/miniflare/src/workers/stream/object.worker.ts
@@ -21,7 +21,6 @@ const BLOB_NAMESPACE = "stream-data";
interface Env {
MINIFLARE_BLOBS?: Fetcher;
- MINIFLARE_STICKY_BLOBS?: boolean;
[SharedBindings.MAYBE_JSON_ENABLE_CONTROL_ENDPOINTS]?: boolean;
}
@@ -42,12 +41,7 @@ export class StreamObject extends DurableObject {
db.exec(SQL_SCHEMA);
this.#db = db;
this.#stmts = sqlStmts(db, () => this.#now());
- const stickyBlobs = !!env.MINIFLARE_STICKY_BLOBS;
- this.#blob = new BlobStore(
- env.MINIFLARE_BLOBS as Fetcher,
- BLOB_NAMESPACE,
- stickyBlobs
- );
+ this.#blob = new BlobStore(env.MINIFLARE_BLOBS as Fetcher, BLOB_NAMESPACE);
}
async createVideo(
diff --git a/packages/miniflare/src/workers/stream/schemas.ts b/packages/miniflare/src/workers/stream/schemas.ts
index 61f09831a7b..e4fb90b76aa 100644
--- a/packages/miniflare/src/workers/stream/schemas.ts
+++ b/packages/miniflare/src/workers/stream/schemas.ts
@@ -1,3 +1,5 @@
+import { CorePaths } from "../core/constants";
+
export const SQL_SCHEMA = `
CREATE TABLE IF NOT EXISTS _mf_stream_videos (
id TEXT PRIMARY KEY,
@@ -130,7 +132,7 @@ export type DownloadRow = {
export function rowToStreamVideo(row: VideoRow, entryUrl: URL): StreamVideo {
const placeholderUrl = `https://customer-placeholder.cloudflarestream.com/${row.id}`;
- const videoUrl = `${entryUrl.origin}/cdn-cgi/mf/stream/${row.id}/watch`;
+ const videoUrl = `${entryUrl.origin}${CorePaths.STREAM_VIDEO}/${row.id}/watch`;
return {
id: row.id,
creator: row.creator,
diff --git a/packages/miniflare/test/dev-registry.spec.ts b/packages/miniflare/test/dev-registry.spec.ts
index c0976f51e72..5f7714e07f6 100644
--- a/packages/miniflare/test/dev-registry.spec.ts
+++ b/packages/miniflare/test/dev-registry.spec.ts
@@ -1193,7 +1193,7 @@ describe.sequential("DevRegistry", () => {
serviceBindings: {
remote: "remote-worker",
},
- handleRuntimeStdio: () => {},
+ handleStructuredLogs: () => {},
compatibilityFlags: ["experimental"],
modules: true,
script: `
@@ -1231,7 +1231,7 @@ describe.sequential("DevRegistry", () => {
},
compatibilityFlags: ["experimental"],
modules: true,
- handleRuntimeStdio: () => {},
+ handleStructuredLogs: () => {},
script: `
export default {
async fetch(request, env) {
@@ -1306,7 +1306,7 @@ describe.sequential("DevRegistry", () => {
props: { tailKey: "from-tail-binding" },
},
],
- handleRuntimeStdio: () => {},
+ handleStructuredLogs: () => {},
compatibilityFlags: ["experimental"],
modules: true,
script: `
diff --git a/packages/miniflare/test/fixtures/echo-plugin/index.ts b/packages/miniflare/test/fixtures/echo-plugin/index.ts
new file mode 100644
index 00000000000..4f4db1e8ca2
--- /dev/null
+++ b/packages/miniflare/test/fixtures/echo-plugin/index.ts
@@ -0,0 +1,77 @@
+import { ProxyNodeBinding } from "miniflare";
+import { z } from "miniflare:zod";
+import type { Plugin, Worker_Binding } from "miniflare";
+
+// Module implementing the wrapped binding. It exposes an `asyncIdentity` method
+// that echoes back its arguments, allowing tests to exercise the proxy client's
+// serialisation of `ReadableStream`/`Blob`/`File` arguments across the
+// Node.js <-> workerd boundary.
+//
+// The `.pipeThrough(new TransformStream())` is required: without it we'd see
+// `TypeError: Inter-TransformStream ReadableStream.pipeTo() is not implemented`
+// when echoing a `ReadableStream` back. `IdentityTransformStream` doesn't work
+// here.
+const ECHO_MODULE_NAME = "cloudflare-internal:echo-plugin:module";
+const ECHO_MODULE = /* javascript */ `
+class Identity {
+ async asyncIdentity(...args) {
+ const i = args.findIndex((arg) => arg instanceof ReadableStream);
+ if (i !== -1) args[i] = args[i].pipeThrough(new TransformStream());
+ return args;
+ }
+}
+export default function () {
+ return new Identity();
+}
+`;
+
+export const EchoBindingOptionSchema = z.array(
+ z.object({
+ name: z.string(),
+ type: z.string(),
+ plugin: z.object({
+ package: z.string(),
+ name: z.string(),
+ }),
+ options: z.record(z.string(), z.unknown()),
+ })
+);
+
+export const plugins = {
+ "echo-plugin": {
+ options: EchoBindingOptionSchema,
+ getBindings(options) {
+ return options.map((binding) => ({
+ name: binding.name,
+ wrapped: {
+ moduleName: ECHO_MODULE_NAME,
+ innerBindings: [],
+ },
+ }));
+ },
+ getNodeBindings(options) {
+ return Object.fromEntries(
+ options.map((binding) => [binding.name, new ProxyNodeBinding()])
+ );
+ },
+ getServices() {
+ return [];
+ },
+ getExtensions({ options }) {
+ if (!options.some((bindings) => bindings.length > 0)) {
+ return [];
+ }
+ return [
+ {
+ modules: [
+ {
+ name: ECHO_MODULE_NAME,
+ esModule: ECHO_MODULE,
+ internal: true,
+ },
+ ],
+ },
+ ];
+ },
+ } satisfies Plugin,
+};
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/README.md b/packages/miniflare/test/fixtures/migrations/3.20230821.0/README.md
deleted file mode 100644
index b7122c4a4a1..00000000000
--- a/packages/miniflare/test/fixtures/migrations/3.20230821.0/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-The contents of this directory were generated with the following script using
-`miniflare@3.20230821.0`:
-
-```js
-import path from "node:path";
-import url from "node:url";
-import { Miniflare } from "miniflare";
-
-const __filename = url.fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
-
-const mf = new Miniflare({
- script: "",
- modules: true,
-
- kvPersist: path.join(__dirname, "kv"),
- kvNamespaces: ["NAMESPACE"],
-
- r2Persist: path.join(__dirname, "r2"),
- r2Buckets: ["BUCKET"],
-
- d1Persist: path.join(__dirname, "d1"),
- d1Databases: ["DATABASE"],
-});
-
-const kvNamespace = await mf.getKVNamespace("NAMESPACE");
-await kvNamespace.put("key", "value");
-
-const r2Bucket = await mf.getR2Bucket("BUCKET");
-await r2Bucket.put("key", "value");
-
-const d1Database = await mf.getD1Database("DATABASE");
-await d1Database.exec(
- "CREATE TABLE entries (key TEXT PRIMARY KEY, value TEXT);"
-);
-await d1Database
- .prepare("INSERT INTO entries (key, value) VALUES (?1, ?2)")
- .bind("a", "1")
- .run();
-
-await mf.dispose();
-```
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/d1/DATABASE/db.sqlite b/packages/miniflare/test/fixtures/migrations/3.20230821.0/d1/DATABASE/db.sqlite
deleted file mode 100644
index cb44f6282aa..00000000000
Binary files a/packages/miniflare/test/fixtures/migrations/3.20230821.0/d1/DATABASE/db.sqlite and /dev/null differ
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/blobs/c708857cd996a02e1c61a4d5af45519baf21ce5c1a39c5baea36f40e912e81e6000637434f5e7544 b/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/blobs/c708857cd996a02e1c61a4d5af45519baf21ce5c1a39c5baea36f40e912e81e6000637434f5e7544
deleted file mode 100644
index 2890eead23d..00000000000
--- a/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/blobs/c708857cd996a02e1c61a4d5af45519baf21ce5c1a39c5baea36f40e912e81e6000637434f5e7544
+++ /dev/null
@@ -1 +0,0 @@
-value
\ No newline at end of file
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/db.sqlite b/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/db.sqlite
deleted file mode 100644
index cee2a8e1971..00000000000
Binary files a/packages/miniflare/test/fixtures/migrations/3.20230821.0/kv/NAMESPACE/db.sqlite and /dev/null differ
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/blobs/776759739fb970e463921f00822d78ae8455152966feb09ac09ec3cc4b27dcff000637435043236c b/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/blobs/776759739fb970e463921f00822d78ae8455152966feb09ac09ec3cc4b27dcff000637435043236c
deleted file mode 100644
index 2890eead23d..00000000000
--- a/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/blobs/776759739fb970e463921f00822d78ae8455152966feb09ac09ec3cc4b27dcff000637435043236c
+++ /dev/null
@@ -1 +0,0 @@
-value
\ No newline at end of file
diff --git a/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/db.sqlite b/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/db.sqlite
deleted file mode 100644
index a54e7e97ad8..00000000000
Binary files a/packages/miniflare/test/fixtures/migrations/3.20230821.0/r2/BUCKET/db.sqlite and /dev/null differ
diff --git a/packages/miniflare/test/fixtures/unsafe-plugin/index.ts b/packages/miniflare/test/fixtures/unsafe-plugin/index.ts
index d405d9b18e3..39f9cdf0c79 100644
--- a/packages/miniflare/test/fixtures/unsafe-plugin/index.ts
+++ b/packages/miniflare/test/fixtures/unsafe-plugin/index.ts
@@ -98,7 +98,7 @@ export const plugins = {
Object.keys(options).map((name) => [name, new ProxyNodeBinding()])
);
},
- getServices({ options, unsafeStickyBlobs }) {
+ getServices({ options }) {
if (options.length === 0) {
return [];
}
@@ -155,7 +155,7 @@ export const plugins = {
name: SharedBindings.MAYBE_SERVICE_LOOPBACK,
service: { name: SERVICE_LOOPBACK },
},
- ...getMiniflareObjectBindings(unsafeStickyBlobs),
+ ...getMiniflareObjectBindings(),
],
},
},
diff --git a/packages/miniflare/test/index.spec.ts b/packages/miniflare/test/index.spec.ts
index 22372d107a8..d947e3bd163 100644
--- a/packages/miniflare/test/index.spec.ts
+++ b/packages/miniflare/test/index.spec.ts
@@ -10,19 +10,15 @@ import net from "node:net";
import os from "node:os";
import path from "node:path";
import { json, text } from "node:stream/consumers";
-import url from "node:url";
import util from "node:util";
+import { _forceColour } from "@cloudflare/workers-utils";
import {
- _forceColour,
_transformsForContentEncodingAndContentType,
- createFetchMock,
DeferredPromise,
fetch,
kCurrentWorker,
Miniflare,
MiniflareCoreError,
- parseWithRootPath,
- PLUGINS,
Response,
viewToBuffer,
} from "miniflare";
@@ -51,7 +47,6 @@ import type {
MiniflareOptions,
ReplaceWorkersTypes,
Worker_Module,
- WorkerOptions,
} from "miniflare";
import type { AddressInfo } from "node:net";
import type { Writable } from "node:stream";
@@ -123,7 +118,7 @@ test("Miniflare: validates options", async ({ expect, onTestFinished }) => {
`Unexpected options passed to \`new Miniflare()\` constructor:
{
name: 42,
- ^ Expected string, received number
+ ^ Invalid input: expected string, received number
...,
}`
);
@@ -141,7 +136,7 @@ test("Miniflare: validates options", async ({ expect, onTestFinished }) => {
expect(error?.message).toEqual(
`Unexpected options passed to \`new Miniflare()\` constructor:
'addEventListener(...)'
-^ Expected object, received string`
+^ Invalid input: expected object, received string`
);
});
@@ -208,13 +203,9 @@ test("Miniflare: ready returns copy of entry URL", async ({ expect }) => {
});
test("Miniflare: setOptions: can update host/port", async ({ expect }) => {
- // Extract loopback port from injected live reload script
- const loopbackPortRegexp = /\/\/ Miniflare Live Reload.+url\.port = (\d+)/s;
-
const opts: MiniflareOptions = {
port: 0,
inspectorPort: 0,
- liveReload: true,
script: `addEventListener("fetch", (event) => {
event.respondWith(new Response("👋
", {
headers: { "Content-Type": "text/html;charset=utf-8" }
@@ -227,9 +218,7 @@ test("Miniflare: setOptions: can update host/port", async ({ expect }) => {
async function getState() {
const url = await mf.ready;
const inspectorUrl = await mf.getInspectorURL();
- const res = await mf.dispatchFetch("http://localhost");
- const loopbackPort = loopbackPortRegexp.exec(await res.text())?.[1];
- return { url, inspectorUrl, loopbackPort };
+ return { url, inspectorUrl };
}
const state1 = await getState();
@@ -243,19 +232,12 @@ test("Miniflare: setOptions: can update host/port", async ({ expect }) => {
expect(state1.inspectorUrl.port).not.toBe("0");
expect(state1.inspectorUrl.port).toBe(state2.inspectorUrl.port);
- // Make sure updating the host restarted the loopback server
- expect(state1.loopbackPort).toBeDefined();
- expect(state2.loopbackPort).toBeDefined();
- expect(state1.loopbackPort).not.toBe(state2.loopbackPort);
-
- // Make sure setting port to `undefined` always gives a new port, but keeps
- // existing loopback server
+ // Make sure setting port to `undefined` always gives a new port
opts.port = undefined;
await mf.setOptions(opts);
const state3 = await getState();
expect(state3.url.port).not.toBe("0");
expect(state1.url.port).not.toBe(state3.url.port);
- expect(state2.loopbackPort).toBe(state3.loopbackPort);
});
const interfaces = os.networkInterfaces();
@@ -1024,7 +1006,7 @@ test("Miniflare: service binding to named entrypoint that implements a method re
test("Miniflare: tail consumer called", async ({ expect }) => {
const mf = new Miniflare({
- handleRuntimeStdio: () => {},
+ handleStructuredLogs: () => {},
workers: [
{
name: "a",
@@ -1326,51 +1308,6 @@ test("Miniflare: handles redirect responses", async ({ expect }) => {
expect(await res.text()).toBe("end:https://custom.mf/external-redirected");
});
-test("Miniflare: fetch mocking", async ({ expect }) => {
- const fetchMock = createFetchMock();
- fetchMock.disableNetConnect();
- const origin = fetchMock.get("https://example.com");
- origin.intercept({ method: "GET", path: "/" }).reply(200, "Mocked response!");
-
- const mfOptions: MiniflareOptions = {
- modules: true,
- script: `export default {
- async fetch() {
- return fetch("https://example.com/");
- }
- }`,
- fetchMock,
- };
- const resultOptions = {} as MiniflareOptions;
-
- // Verify that options with `fetchMock` can be parsed first before passing to Miniflare
- // Regression test for https://github.com/cloudflare/workers-sdk/issues/5486
- for (const plugin of Object.values(PLUGINS)) {
- Object.assign(
- resultOptions,
- parseWithRootPath("", plugin.options, mfOptions)
- );
- }
-
- const mf = new Miniflare(resultOptions);
- useDispose(mf);
- const res = await mf.dispatchFetch("http://localhost");
- expect(await res.text()).toBe("Mocked response!");
-
- // Check `outboundService`and `fetchMock` mutually exclusive
- await expect(
- mf.setOptions({
- script: "",
- fetchMock,
- outboundService: "",
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_MULTIPLE_OUTBOUNDS",
- "Only one of `outboundService` or `fetchMock` may be specified per worker"
- )
- );
-});
test("Miniflare: custom upstream as origin (with colons)", async ({
expect,
}) => {
@@ -1724,11 +1661,11 @@ test("Miniflare: manually triggered scheduled events", async ({ expect }) => {
let res = await mf.dispatchFetch("http://localhost");
expect(await res.text()).toBe("false");
- res = await mf.dispatchFetch("http://localhost/cdn-cgi/handler/scheduled");
+ res = await mf.dispatchFetch("http://localhost/cdn-cgi/local/scheduled");
expect(await res.text()).toBe("ok");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/scheduled?format=json"
+ "http://localhost/cdn-cgi/local/scheduled?format=json"
);
expect(await res.json()).toEqual({ outcome: "ok", noRetry: true });
@@ -1795,7 +1732,7 @@ test("Miniflare: manually triggered scheduled events with assets", async ({
expect(res.headers.get("content-type")).toBe("text/markdown; charset=utf-8");
expect(await res.text()).toBe("asset");
- res = await mf.dispatchFetch("http://localhost/cdn-cgi/handler/scheduled");
+ res = await mf.dispatchFetch("http://localhost/cdn-cgi/local/scheduled");
expect(await res.text()).toBe("ok");
res = await mf.dispatchFetch("http://localhost");
@@ -1805,7 +1742,7 @@ test("Miniflare: manually triggered scheduled events with assets", async ({
expect(json.scheduledTime).toBeDefined();
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/scheduled?format=json&cron=0+0+0+0+0&time=1234567890987"
+ "http://localhost/cdn-cgi/local/scheduled?format=json&cron=0+0+0+0+0&time=1234567890987"
);
expect(await res.json()).toEqual({
outcome: "ok",
@@ -1845,7 +1782,7 @@ test("Miniflare: manually triggered email handler - valid email", async ({
expect(await res.text()).toBe("false");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com",
+ "http://localhost/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com",
{
body: `From: someone
To: someone else
@@ -1892,7 +1829,7 @@ test("Miniflare: manually triggered email handler - setReject does not throw", a
expect(await res.text()).toBe("false");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com",
+ "http://localhost/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com",
{
body: `From: someone
To: someone else
@@ -1941,7 +1878,7 @@ test("Miniflare: manually triggered email handler - forward does not throw", asy
expect(await res.text()).toBe("false");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com",
+ "http://localhost/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com",
{
body: `From: someone
To: someone else
@@ -1987,7 +1924,7 @@ test("Miniflare: manually triggered email handler - invalid email, no message id
expect(await res.text()).toBe("false");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com",
+ "http://localhost/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com",
{
body: `From: someone
To: someone else
@@ -2050,7 +1987,7 @@ This is a random email body.
expect(await res.text()).toBe("false");
res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com",
+ "http://localhost/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com",
{
body: `From: someone
To: someone else
@@ -2070,7 +2007,7 @@ This is a random email body.
expect(await res.text()).toBe("true");
});
-test("Miniflare: unimplemented /cdn-cgi/handler/ routes", async ({
+test("Miniflare: unrecognised /cdn-cgi/local/ routes fall through to user worker", async ({
expect,
}) => {
const mf = new Miniflare({
@@ -2086,11 +2023,9 @@ test("Miniflare: unimplemented /cdn-cgi/handler/ routes", async ({
});
useDispose(mf);
- const res = await mf.dispatchFetch("http://localhost/cdn-cgi/handler/foo");
- expect(await res.text()).toBe(
- `"/cdn-cgi/handler/foo" is not a valid handler. Did you mean to use "/cdn-cgi/handler/scheduled" or "/cdn-cgi/handler/email"?`
- );
- expect(res.status).toBe(404);
+ const res = await mf.dispatchFetch("http://localhost/cdn-cgi/local/foo");
+ expect(await res.text()).toBe("Hello world");
+ expect(res.status).toBe(200);
});
test("Miniflare: other /cdn-cgi/ routes", async ({ expect }) => {
@@ -2150,7 +2085,7 @@ test("Miniflare: blocks non-local Host headers from reaching /cdn-cgi/ routes",
setHost: false,
headers: {
Host: "example.trycloudflare.com",
- "MF-Original-URL": "http://localhost/cdn-cgi/handler/scheduled",
+ "MF-Original-URL": "http://localhost/cdn-cgi/local/scheduled",
},
},
(res) => {
@@ -2287,197 +2222,6 @@ test("Miniflare: getBindings() returns all bindings", async ({
)
);
});
-test("Miniflare: getBindings() returns wrapped bindings", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- Greeter: {
- scriptName: "greeter-implementation",
- },
- },
- modules: true,
- script: "",
- },
- {
- modules: true,
- name: "greeter-implementation",
- script: `
- class Greeter {
- sayHello(name) {
- return "Hello " + name;
- }
- }
-
- export default function (env) {
- return new Greeter();
- }
- `,
- },
- ],
- });
- useDispose(mf);
-
- interface Env {
- Greeter: {
- sayHello: (str: string) => string;
- };
- }
- const { Greeter } = await mf.getBindings();
-
- const helloWorld = Greeter.sayHello("World");
-
- expect(helloWorld).toBe("Hello World");
-});
-test("Miniflare: getBindings() handles wrapped bindings returning objects containing functions", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- Greeter: {
- scriptName: "greeter-obj-implementation",
- },
- },
- modules: true,
- script: "",
- },
- {
- modules: true,
- name: "greeter-obj-implementation",
- script: `
- export default function (env) {
- const objWithFunction = {
- greeting: "Hello",
- sayHello(name) {
- return this.greeting + ' ' + name;
- }
- };
- return objWithFunction;
- }
- `,
- },
- ],
- });
- useDispose(mf);
-
- interface Env {
- Greeter: {
- greeting: string;
- sayHello: (str: string) => string;
- };
- }
- const { Greeter } = await mf.getBindings();
-
- const helloWorld = Greeter.sayHello("World");
-
- expect(helloWorld).toBe("Hello World");
- expect(Greeter.greeting).toBe("Hello");
-});
-test("Miniflare: getBindings() handles wrapped bindings returning objects containing nested functions", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- Greeter: {
- scriptName: "greeter-obj-implementation",
- },
- },
- modules: true,
- script: "",
- },
- {
- modules: true,
- name: "greeter-obj-implementation",
- script: `
- export default function (env) {
- const objWithFunction = {
- obj: {
- obj1: {
- obj2: {
- sayHello: (name) => "Hello " + name + " from a nested function"
- }
- }
- }
- };
- return objWithFunction;
- }
- `,
- },
- ],
- });
- useDispose(mf);
-
- interface Env {
- Greeter: {
- obj: {
- obj1: {
- obj2: {
- sayHello: (str: string) => string;
- };
- };
- };
- };
- }
- const { Greeter } = await mf.getBindings();
-
- const helloWorld = Greeter.obj.obj1.obj2.sayHello("World");
-
- expect(helloWorld).toBe("Hello World from a nested function");
-});
-test("Miniflare: getBindings() handles wrapped bindings returning functions returning functions", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- GreetFactory: {
- scriptName: "greet-factory-obj-implementation",
- },
- },
- modules: true,
- script: "",
- },
- {
- modules: true,
- name: "greet-factory-obj-implementation",
- script: `
- export default function (env) {
- const factory = {
- getGreetFunction(name) {
- return (name) => {
- return this.greeting + ' ' + name;
- }
- },
- greeting: "Salutations",
- };
- return factory;
- }
- `,
- },
- ],
- });
- useDispose(mf);
-
- interface Env {
- GreetFactory: {
- greeting: string;
- getGreetFunction: () => (str: string) => string;
- };
- }
- const { GreetFactory } = await mf.getBindings();
-
- const greetFunction = GreetFactory.getGreetFunction();
-
- expect(greetFunction("Esteemed World")).toBe("Salutations Esteemed World");
- expect(GreetFactory.greeting).toBe("Salutations");
-});
test("Miniflare: getWorker() allows dispatching events directly", async ({
expect,
}) => {
@@ -3036,459 +2780,6 @@ test("Miniflare: supports unsafe eval bindings", async ({ expect }) => {
expect(await response.text()).toBe("the computed value is 3");
});
-test("Miniflare: supports wrapped bindings", async ({ expect }) => {
- const store = new Map();
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- MINI_KV: {
- scriptName: "mini-kv",
- bindings: { NAMESPACE: "ns" },
- },
- },
- modules: true,
- script: `export default {
- async fetch(request, env, ctx) {
- await env.MINI_KV.set("key", "value");
- const value = await env.MINI_KV.get("key");
- await env.MINI_KV.delete("key");
- const emptyValue = await env.MINI_KV.get("key");
- await env.MINI_KV.set("key", "another value");
- return Response.json({ value, emptyValue });
- }
- }`,
- },
- {
- name: "mini-kv",
- serviceBindings: {
- async STORE(request) {
- const { pathname } = new URL(request.url);
- const key = pathname.substring(1);
- if (request.method === "GET") {
- const value = store.get(key);
- const status = value === undefined ? 404 : 200;
- return new Response(value ?? null, { status });
- } else if (request.method === "PUT") {
- const value = await request.text();
- store.set(key, value);
- return new Response(null, { status: 204 });
- } else if (request.method === "DELETE") {
- store.delete(key);
- return new Response(null, { status: 204 });
- } else {
- return new Response(null, { status: 405 });
- }
- },
- },
- modules: true,
- script: `
- class MiniKV {
- constructor(env) {
- this.STORE = env.STORE;
- this.baseURL = "http://x/" + (env.NAMESPACE ?? "") + ":";
- }
- async get(key) {
- const res = await this.STORE.fetch(this.baseURL + key);
- return res.status === 404 ? null : await res.text();
- }
- async set(key, body) {
- await this.STORE.fetch(this.baseURL + key, { method: "PUT", body });
- }
- async delete(key) {
- await this.STORE.fetch(this.baseURL + key, { method: "DELETE" });
- }
- }
-
- export default function (env) {
- return new MiniKV(env);
- }
- `,
- },
- ],
- });
- useDispose(mf);
-
- const res = await mf.dispatchFetch("http://localhost/");
- expect(await res.json()).toEqual({ value: "value", emptyValue: null });
- expect(store).toEqual(new Map([["ns:key", "another value"]]));
-});
-test("Miniflare: check overrides default bindings with bindings from wrapped binding designator", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: {
- WRAPPED: {
- scriptName: "binding",
- entrypoint: "wrapped",
- bindings: { B: "overridden b" },
- },
- },
- modules: true,
- script: `export default {
- fetch(request, env, ctx) {
- return env.WRAPPED();
- }
- }`,
- },
- {
- name: "binding",
- modules: true,
- bindings: { A: "default a", B: "default b" },
- script: `export function wrapped(env) {
- return () => Response.json(env);
- }`,
- },
- ],
- });
- useDispose(mf);
-
- const res = await mf.dispatchFetch("http://localhost/");
- expect(await res.json()).toEqual({ A: "default a", B: "overridden b" });
-});
-test("Miniflare: checks uses compatibility and outbound configuration of binder", async ({
- expect,
-}) => {
- const workers: WorkerOptions[] = [
- {
- compatibilityDate: "2022-03-21", // Default-on date for `global_navigator`
- compatibilityFlags: ["nodejs_compat"],
- wrappedBindings: { WRAPPED: "binding" },
- modules: true,
- script: `export default {
- fetch(request, env, ctx) {
- return env.WRAPPED();
- }
- }`,
- outboundService(request) {
- return new Response(`outbound:${request.url}`);
- },
- },
- {
- name: "binding",
- modules: [
- {
- type: "ESModule",
- path: "index.mjs",
- contents: `export default function () {
- return async () => {
- const typeofNavigator = typeof navigator;
- let importedNode = false;
- try {
- await import("node:util");
- importedNode = true;
- } catch {}
- const outboundRes = await fetch("http://placeholder/");
- const outboundText = await outboundRes.text();
- return Response.json({ typeofNavigator, importedNode, outboundText });
- }
- }`,
- },
- ],
- },
- ];
- const mf = new Miniflare({ workers });
- useDispose(mf);
-
- let res = await mf.dispatchFetch("http://localhost/");
- expect(await res.json()).toEqual({
- typeofNavigator: "object",
- importedNode: true,
- outboundText: "outbound:http://placeholder/",
- });
-
- const fetchMock = createFetchMock();
- fetchMock.disableNetConnect();
- fetchMock
- .get("http://placeholder")
- .intercept({ path: "/" })
- .reply(200, "mocked");
- workers[0].compatibilityDate = "2022-03-20";
- workers[0].compatibilityFlags = [];
- workers[0].outboundService = undefined;
- workers[0].fetchMock = fetchMock;
- await mf.setOptions({ workers });
- res = await mf.dispatchFetch("http://localhost/");
- expect(await res.json()).toEqual({
- typeofNavigator: "undefined",
- importedNode: false,
- outboundText: "mocked",
- });
-});
-test("Miniflare: cannot call getWorker() on wrapped binding worker", async ({
- expect,
-}) => {
- const mf = new Miniflare({
- workers: [
- {
- wrappedBindings: { WRAPPED: "binding" },
- modules: true,
- script: `export default {
- fetch(request, env, ctx) {
- return env.WRAPPED;
- }
- }`,
- },
- {
- name: "binding",
- modules: true,
- script: `export default function () {
- return "🎁";
- }`,
- },
- ],
- });
- useDispose(mf);
-
- await expect(mf.getWorker("binding")).rejects.toThrow(
- new TypeError(
- '"binding" is being used as a wrapped binding, and cannot be accessed as a worker'
- )
- );
-});
-test("Miniflare: prohibits invalid wrapped bindings", async ({ expect }) => {
- const mf = new Miniflare({ modules: true, script: "" });
- useDispose(mf);
-
- // Check prohibits using entrypoint worker
- await expect(
- mf.setOptions({
- name: "a",
- modules: true,
- script: "",
- wrappedBindings: {
- WRAPPED: { scriptName: "a", entrypoint: "wrapped" },
- },
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "a" for wrapped binding because it\'s the entrypoint.\n' +
- 'Ensure "a" isn\'t the first entry in the `workers` array.'
- )
- );
-
- // Check prohibits using service worker
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- { name: "binding", script: "" },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it\'s a service worker.\n' +
- 'Ensure "binding" sets `modules` to `true` or an array of modules'
- )
- );
-
- // Check prohibits multiple modules
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- modules: [
- { type: "ESModule", path: "index.mjs", contents: "" },
- { type: "ESModule", path: "dep.mjs", contents: "" },
- ],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it isn\'t a single module.\n' +
- 'Ensure "binding" doesn\'t include unbundled `import`s.'
- )
- );
-
- // Check prohibits non-ES-modules
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- modules: [{ type: "CommonJS", path: "index.cjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it isn\'t a single ES module'
- )
- );
-
- // Check prohibits Durable Object bindings
- await expect(
- mf.setOptions({
- workers: [
- {
- modules: true,
- script: "",
- wrappedBindings: { WRAPPED: "binding" },
- durableObjects: {
- OBJECT: { scriptName: "binding", className: "TestObject" },
- },
- },
- {
- name: "binding",
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it is bound to with Durable Object bindings.\n' +
- 'Ensure other workers don\'t define Durable Object bindings to "binding".'
- )
- );
-
- // Check prohibits service bindings
- await expect(
- mf.setOptions({
- workers: [
- {
- modules: true,
- script: "",
- wrappedBindings: {
- WRAPPED: { scriptName: "binding", entrypoint: "wrapped" },
- },
- serviceBindings: { SERVICE: "binding" },
- },
- {
- name: "binding",
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it is bound to with service bindings.\n' +
- 'Ensure other workers don\'t define service bindings to "binding".'
- )
- );
-
- // Check prohibits compatibility date and flags
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- compatibilityDate: "2023-11-01",
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it defines a compatibility date.\n' +
- "Wrapped bindings use the compatibility date of the worker with the binding."
- )
- );
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- compatibilityFlags: ["nodejs_compat"],
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it defines compatibility flags.\n' +
- "Wrapped bindings use the compatibility flags of the worker with the binding."
- )
- );
-
- // Check prohibits outbound service
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- outboundService() {
- assert.fail();
- },
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_INVALID_WRAPPED",
- 'Cannot use "binding" for wrapped binding because it defines an outbound service.\n' +
- "Wrapped bindings use the outbound service of the worker with the binding."
- )
- );
-
- // Check prohibits cyclic wrapped bindings
- await expect(
- mf.setOptions({
- workers: [
- { modules: true, script: "", wrappedBindings: { WRAPPED: "binding" } },
- {
- name: "binding",
- wrappedBindings: { WRAPPED: "binding" }, // Simple cycle
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_CYCLIC",
- "Generated workerd config contains cycles. Ensure wrapped bindings don't have bindings to themselves."
- )
- );
- await expect(
- mf.setOptions({
- workers: [
- {
- modules: true,
- script: "",
- wrappedBindings: { WRAPPED1: "binding-1" },
- },
- {
- name: "binding-1",
- wrappedBindings: { WRAPPED2: "binding-2" },
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- {
- name: "binding-2",
- wrappedBindings: { WRAPPED3: "binding-3" },
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- {
- name: "binding-3",
- wrappedBindings: { WRAPPED1: "binding-1" }, // Multi-step cycle
- modules: [{ type: "ESModule", path: "index.mjs", contents: "" }],
- },
- ],
- })
- ).rejects.toThrow(
- new MiniflareCoreError(
- "ERR_CYCLIC",
- "Generated workerd config contains cycles. Ensure wrapped bindings don't have bindings to themselves."
- )
- );
-});
-
test("Miniflare: getCf() returns a standard cf object", async ({ expect }) => {
const mf = new Miniflare({ script: "", modules: true });
useDispose(mf);
@@ -3906,7 +3197,7 @@ test("Miniflare: respects rootPath for path-valued options", async ({
await fs.writeFile(path.join(tmp, "3.txt"), "three text");
const mf = new Miniflare({
rootPath: tmp,
- kvPersist: "kv",
+ resourcePersistencePath: tmp,
workers: [
{
name: "a",
@@ -3971,10 +3262,10 @@ test("Miniflare: respects rootPath for path-valued options", async ({
});
expect(existsSync(path.join(tmp, "kv", "namespace"))).toBe(true);
- // Check persistence URLs not resolved relative to root path
+ // Check persisted KV data survives an options reload
await mf.setOptions({
rootPath: tmp,
- kvPersist: url.pathToFileURL(path.join(tmp, "kv")).href,
+ resourcePersistencePath: tmp,
kvNamespaces: { NAMESPACE: "namespace" },
modules: true,
script: `export default {
diff --git a/packages/miniflare/test/logs.spec.ts b/packages/miniflare/test/logs.spec.ts
index 72bb347012b..3a449fd5c42 100644
--- a/packages/miniflare/test/logs.spec.ts
+++ b/packages/miniflare/test/logs.spec.ts
@@ -1,72 +1,26 @@
-import { Miniflare, MiniflareCoreError } from "miniflare";
-import { assert, test } from "vitest";
+import { Miniflare } from "miniflare";
+import { onTestFinished, test, vi } from "vitest";
import { useDispose } from "./test-shared";
import type { WorkerdStructuredLog } from "miniflare";
-test("logs are treated as standard stdout/stderr chunks by default", async ({
+test("logs are written to the console by default when no `handleStructuredLogs` is provided", async ({
expect,
}) => {
- const collected = {
- stdout: "",
- stderr: "",
- };
- const mf = new Miniflare({
- modules: true,
- handleRuntimeStdio(stdout, stderr) {
- stdout.forEach((data) => {
- collected.stdout += `${data}`;
- });
- stderr.forEach((error) => {
- collected.stderr += `${error}`;
- });
- },
- script: `
- export default {
- async fetch(req, env) {
- console.log('__LOG__');
- console.warn('__WARN__');
- console.error('__ERROR__');
- console.info('__INFO__');
- console.debug('__DEBUG__');
- return new Response('Hello world!');
- }
- }`,
+ const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
+ const errorSpy = vi.spyOn(console, "error").mockImplementation(() => {});
+ onTestFinished(() => {
+ logSpy.mockRestore();
+ errorSpy.mockRestore();
});
- useDispose(mf);
-
- const response = await mf.dispatchFetch("http://localhost");
- await response.text();
- expect(collected.stdout).toBe("__LOG__\n__INFO__\n__DEBUG__\n");
- expect(collected.stderr).toBe("__WARN__\n__ERROR__\n");
-});
-
-test("logs are structured and all sent to stdout when `structuredWorkerdLogs` is `true`", async ({
- expect,
-}) => {
- const collected = {
- stdout: "",
- stderr: "",
- };
const mf = new Miniflare({
modules: true,
- structuredWorkerdLogs: true,
- handleRuntimeStdio(stdout, stderr) {
- stdout.forEach((data) => {
- collected.stdout += `${data}`;
- });
- stderr.forEach((error) => {
- collected.stderr += `${error}`;
- });
- },
script: `
export default {
async fetch(req, env) {
console.log('__LOG__');
console.warn('__WARN__');
console.error('__ERROR__');
- console.info('__INFO__');
- console.debug('__DEBUG__');
return new Response('Hello world!');
}
}`,
@@ -76,26 +30,16 @@ test("logs are structured and all sent to stdout when `structuredWorkerdLogs` is
const response = await mf.dispatchFetch("http://localhost");
await response.text();
- expect(collected.stdout).toMatch(
- /{"timestamp":\d+,"level":"log","message":"__LOG__"}/
- );
- expect(collected.stdout).toMatch(
- /{"timestamp":\d+,"level":"warn","message":"__WARN__"}/
- );
- expect(collected.stdout).toMatch(
- /{"timestamp":\d+,"level":"error","message":"__ERROR__"}/
- );
- expect(collected.stdout).toMatch(
- /{"timestamp":\d+,"level":"info","message":"__INFO__"}/
- );
- expect(collected.stdout).toMatch(
- /{"timestamp":\d+,"level":"debug","message":"__DEBUG__"}/
- );
+ const stdout = logSpy.mock.calls.map((args) => args.join(" ")).join("\n");
+ const stderr = errorSpy.mock.calls.map((args) => args.join(" ")).join("\n");
- expect(collected.stderr).toBe("");
+ // `log` goes to stdout; `warn`/`error` go to stderr
+ expect(stdout).toContain("__LOG__");
+ expect(stderr).toContain("__WARN__");
+ expect(stderr).toContain("__ERROR__");
});
-test("logs are structured and handled via `handleStructuredLogs` when such option is provided (no `structuredWorkerdLogs: true` needed)", async ({
+test("logs are structured and handled via `handleStructuredLogs` when such option is provided", async ({
expect,
}) => {
const collectedLogs: (Pick & {
@@ -155,77 +99,6 @@ test("logs are structured and handled via `handleStructuredLogs` when such optio
]);
});
-test("even when `handleStructuredLogs` is provided, `handleRuntimeStdio` can still be used to read the raw stream values", async ({
- expect,
-}) => {
- let numOfCollectedStructuredLogs = 0;
- const collectedRaw = {
- stdout: "",
- stderr: "",
- };
- const mf = new Miniflare({
- modules: true,
- handleRuntimeStdio(stdout, stderr) {
- stdout.forEach((data) => {
- collectedRaw.stdout += `${data}`;
- });
- stderr.forEach((error) => {
- collectedRaw.stderr += `${error}`;
- });
- },
- handleStructuredLogs() {
- numOfCollectedStructuredLogs++;
- },
- script: `
- export default {
- async fetch(req, env) {
- console.log('__LOG__');
- console.error('__ERROR__');
- return new Response('Hello world!');
- }
- }`,
- });
- useDispose(mf);
-
- const response = await mf.dispatchFetch("http://localhost");
- await response.text();
-
- expect(numOfCollectedStructuredLogs).toBe(2);
-
- expect(collectedRaw.stdout).toMatch(
- /{"timestamp":\d+,"level":"log","message":"__LOG__"}/
- );
- expect(collectedRaw.stdout).toMatch(
- /{"timestamp":\d+,"level":"error","message":"__ERROR__"}/
- );
- expect(collectedRaw.stderr).toBe("");
-});
-
-test("setting `handleStructuredLogs` when `structuredWorkerdLogs` is `false` triggers an error", async ({
- expect,
-}) => {
- const mf = new Miniflare({ modules: true, script: "" });
- useDispose(mf);
-
- let error: MiniflareCoreError | undefined = undefined;
- try {
- new Miniflare({
- modules: true,
- script: "",
- structuredWorkerdLogs: false,
- handleStructuredLogs() {},
- });
- } catch (e) {
- error = e as MiniflareCoreError;
- }
-
- assert(error instanceof MiniflareCoreError);
- expect(error.code).toBe("ERR_VALIDATION");
- expect(error.message).toContain(
- "A `handleStructuredLogs` has been provided but `structuredWorkerdLogs` is set to `false`"
- );
-});
-
test("when using `handleStructuredLogs` some known unhelpful logs are filtered out (e.g. CODE_MOVED warnings)", async ({
expect,
}) => {
diff --git a/packages/miniflare/test/plugins/cache/index.spec.ts b/packages/miniflare/test/plugins/cache/index.spec.ts
index 61189d36e72..c510b9ebf3a 100644
--- a/packages/miniflare/test/plugins/cache/index.spec.ts
+++ b/packages/miniflare/test/plugins/cache/index.spec.ts
@@ -1,13 +1,8 @@
import assert from "node:assert";
import crypto from "node:crypto";
import fs from "node:fs/promises";
-import {
- CACHE_PLUGIN_NAME,
- LogLevel,
- Miniflare,
- Request,
- Response,
-} from "miniflare";
+import path from "node:path";
+import { CACHE_PLUGIN_NAME, Miniflare, Request, Response } from "miniflare";
import { beforeEach, type ExpectStatic, onTestFinished, test } from "vitest";
import {
MiniflareDurableObjectControlStub,
@@ -419,39 +414,13 @@ test("operations respect cf.cacheKey", async ({ expect }) => {
const deleted2 = await cache.delete(key2);
expect(deleted2).toBe(true);
});
-test("operations log warning on workers.dev subdomain", async ({ expect }) => {
- // Set option, then reset after test
- await ctx.setOptions({ cacheWarnUsage: true });
- onTestFinished(() => ctx.setOptions({}));
- ctx.caches = await ctx.mf.getCaches();
- const defaultObject = await getControlStub(ctx.mf);
-
- const cache = ctx.caches.default;
- const key = "http://localhost/cache-workers-dev-warning";
-
- ctx.log.logs = [];
- const resToCache = new Response("body", {
- headers: { "Cache-Control": "max-age=3600" },
- });
- await cache.put(key, resToCache.clone());
- await defaultObject.waitForFakeTasks();
- expect(ctx.log.logsAtLevel(LogLevel.WARN)).toEqual([
- "Cache operations will have no impact if you deploy to a workers.dev subdomain!",
- ]);
-
- // Check only warns once
- ctx.log.logs = [];
- await cache.put(key, resToCache);
- await defaultObject.waitForFakeTasks();
- expect(ctx.log.logsAtLevel(LogLevel.WARN)).toEqual([]);
-});
test("operations persist cached data", async ({ expect }) => {
// Create new temporary file-system persistence directory
const tmp = await useTmp();
const opts: MiniflareOptions = {
modules: true,
script: "",
- cachePersist: tmp,
+ resourcePersistencePath: tmp,
};
let mf = new Miniflare(opts);
useDispose(mf);
@@ -465,8 +434,8 @@ test("operations persist cached data", async ({ expect }) => {
});
await cache.put(key, resToCache);
- // Check directory created for namespace
- const names = await fs.readdir(tmp);
+ // Check directory created for namespace under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, CACHE_PLUGIN_NAME));
expect(names.includes("miniflare-CacheObject")).toBe(true);
// Check "restarting" keeps persisted data
@@ -486,7 +455,7 @@ test("operations persist cached data", async ({ expect }) => {
});
test("operations are no-ops when caching disabled", async ({ expect }) => {
// Set option, then reset after test
- await ctx.setOptions({ cache: false });
+ await ctx.setOptions({ cacheAPI: false });
onTestFinished(() => ctx.setOptions({}));
ctx.caches = await ctx.mf.getCaches();
diff --git a/packages/miniflare/test/plugins/core/inspector-proxy/index.spec.ts b/packages/miniflare/test/plugins/core/inspector-proxy/index.spec.ts
index 1a0fba7a40a..66181034f66 100644
--- a/packages/miniflare/test/plugins/core/inspector-proxy/index.spec.ts
+++ b/packages/miniflare/test/plugins/core/inspector-proxy/index.spec.ts
@@ -706,12 +706,10 @@ test("InspectorProxy: can proxy messages > 1MB", async ({ expect }) => {
const mf = new Miniflare({
inspectorPort: 0,
- // Avoid the default handling of stdio since that will console log the very large string in the test output.
- handleRuntimeStdio(stdout, stderr) {
- // We need to add these handlers otherwise the streams will not be consumed and the process will hang.
- stdout.on("data", () => {});
- stderr.on("data", () => {});
- },
+ // Avoid the default handling of stdio since that will console log the very
+ // large string in the test output. A no-op structured log handler consumes
+ // the runtime output without forwarding it to the console.
+ handleStructuredLogs() {},
workers: [
{
script: `
diff --git a/packages/miniflare/test/plugins/core/proxy/client.spec.ts b/packages/miniflare/test/plugins/core/proxy/client.spec.ts
index 01627a92759..b4f80928a2f 100644
--- a/packages/miniflare/test/plugins/core/proxy/client.spec.ts
+++ b/packages/miniflare/test/plugins/core/proxy/client.spec.ts
@@ -1,6 +1,7 @@
import assert from "node:assert";
import { Blob } from "node:buffer";
import http from "node:http";
+import path from "node:path";
import { text } from "node:stream/consumers";
import { ReadableStream, WritableStream } from "node:stream/web";
import util from "node:util";
@@ -13,7 +14,7 @@ import {
WebSocketPair,
} from "miniflare";
import { describe, onTestFinished, test } from "vitest";
-import { useDispose } from "../../../test-shared";
+import { EXPORTED_FIXTURES, useDispose } from "../../../test-shared";
import type { Fetcher } from "@cloudflare/workers-types/experimental";
import type { MessageEvent, ReplaceWorkersTypes } from "miniflare";
@@ -81,38 +82,29 @@ describe("ProxyClient", () => {
test("supports serialising multiple ReadableStreams, Blobs and Files", async ({
expect,
}) => {
- // For testing proxy client serialisation, add an API that just returns its
- // arguments. Note without the `.pipeThrough(new TransformStream())` below,
- // we'll see `TypeError: Inter-TransformStream ReadableStream.pipeTo() is
- // not implemented.`. `IdentityTransformStream` doesn't work here.
+ // For testing proxy client serialisation, use a wrapped binding inside an
+ // unsafe binding. This is just an echo module that just returns its arguments
+ // (see the `echo-plugin` fixture).
+ const echoPlugin = path.resolve(EXPORTED_FIXTURES, "echo-plugin/index.js");
const mf = new Miniflare({
- workers: [
- {
- name: "entry",
- modules: true,
- script: "",
- wrappedBindings: { IDENTITY: "identity" },
- },
+ name: "entry",
+ modules: true,
+ script: "",
+ unsafeBindings: [
{
- name: "identity",
- modules: true,
- script: `
- class Identity {
- async asyncIdentity(...args) {
- const i = args.findIndex((arg) => arg instanceof ReadableStream);
- if (i !== -1) args[i] = args[i].pipeThrough(new TransformStream());
- return args;
- }
- }
- export default function() { return new Identity(); }
- `,
+ name: "IDENTITY",
+ type: "wrapped",
+ plugin: { package: echoPlugin, name: "echo-plugin" },
+ options: {},
},
],
});
useDispose(mf);
const client = await mf._getProxyClient();
- const IDENTITY = client.env["MINIFLARE_PROXY:core:entry:IDENTITY"] as {
+ const IDENTITY = client.env[
+ "MINIFLARE_PROXY:echo-plugin:entry:IDENTITY"
+ ] as {
asyncIdentity(...args: Args): Promise;
};
@@ -151,6 +143,7 @@ describe("ProxyClient", () => {
expect(allResult[2].lastModified).toBe(1000);
expect(await allResult[2].text()).toBe("text file");
});
+
test("poisons dependent proxies after setOptions()/dispose()", async ({
expect,
}) => {
diff --git a/packages/miniflare/test/plugins/d1/index.spec.ts b/packages/miniflare/test/plugins/d1/index.spec.ts
index 3480e4c321d..f46edddd8d8 100644
--- a/packages/miniflare/test/plugins/d1/index.spec.ts
+++ b/packages/miniflare/test/plugins/d1/index.spec.ts
@@ -1,8 +1,3 @@
-import fs from "node:fs/promises";
-import path from "node:path";
-import { Miniflare } from "miniflare";
-import { test } from "vitest";
-import { FIXTURES_PATH, useDispose, useTmp } from "../../test-shared";
// Import suite tests - this registers the tests with vitest
import "./suite";
import { setupTest } from "./test";
@@ -10,24 +5,3 @@ import { setupTest } from "./test";
// Post-wrangler 3.3, D1 bindings work directly, so use the input file
// from the fixture, and no prefix on the binding name
setupTest("DB", "worker.mjs", (mf) => mf.getD1Database("DB"));
-
-test("migrates database to new location", async ({ expect }) => {
- // Copy legacy data to temporary directory
- const tmp = await useTmp();
- const persistFixture = path.join(FIXTURES_PATH, "migrations", "3.20230821.0");
- const d1Persist = path.join(tmp, "d1");
- await fs.cp(path.join(persistFixture, "d1"), d1Persist, { recursive: true });
-
- // Implicitly migrate data
- const mf = new Miniflare({
- modules: true,
- script: "",
- d1Databases: ["DATABASE"],
- d1Persist,
- });
- useDispose(mf);
-
- const database = await mf.getD1Database("DATABASE");
- const { results } = await database.prepare("SELECT * FROM entries").all();
- expect(results).toEqual([{ key: "a", value: "1" }]);
-});
diff --git a/packages/miniflare/test/plugins/d1/suite.ts b/packages/miniflare/test/plugins/d1/suite.ts
index 8b3cc171c8d..60c20a69e79 100644
--- a/packages/miniflare/test/plugins/d1/suite.ts
+++ b/packages/miniflare/test/plugins/d1/suite.ts
@@ -1,7 +1,8 @@
import assert from "node:assert";
import fs from "node:fs/promises";
+import path from "node:path";
import { type D1Database } from "@cloudflare/workers-types/experimental";
-import { Miniflare } from "miniflare";
+import { D1_PLUGIN_NAME, Miniflare } from "miniflare";
import { beforeEach, type ExpectStatic, onTestFinished, test } from "vitest";
import { useDispose, useTmp, utf8Encode } from "../../test-shared";
import { binding, ctx, getDatabase, opts } from "./test";
@@ -507,7 +508,10 @@ test("operations persist D1 data", async ({ expect }) => {
// Create new temporary file-system persistence directory
const tmp = await useTmp();
- const persistOpts: MiniflareOptions = { ...opts, d1Persist: tmp };
+ const persistOpts: MiniflareOptions = {
+ ...opts,
+ resourcePersistencePath: tmp,
+ };
const mf = new Miniflare(persistOpts);
useDispose(mf);
let db = await getDatabase(mf);
@@ -524,8 +528,8 @@ test("operations persist D1 data", async ({ expect }) => {
.first();
expect(result).toEqual({ name: "purple" });
- // Check directory created for database
- const names = await fs.readdir(tmp);
+ // Check directory created for database under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, D1_PLUGIN_NAME));
expect(names.includes("miniflare-D1DatabaseObject")).toBe(true);
// Check "restarting" keeps persisted data
@@ -599,7 +603,7 @@ test("dumpSql exports and imports complete database structure and content correc
const tmp1 = await useTmp();
const originalMF = new Miniflare({
...opts,
- d1Persist: tmp1,
+ resourcePersistencePath: tmp1,
d1Databases: { test: "test" },
});
useDispose(originalMF);
@@ -620,7 +624,7 @@ test("dumpSql exports and imports complete database structure and content correc
const tmp2 = await useTmp();
const mirrorMF = new Miniflare({
...opts,
- d1Persist: tmp2,
+ resourcePersistencePath: tmp2,
d1Databases: { test: "test" },
});
useDispose(mirrorMF);
diff --git a/packages/miniflare/test/plugins/do/index.spec.ts b/packages/miniflare/test/plugins/do/index.spec.ts
index 049b06e7d9d..e396c6ae586 100644
--- a/packages/miniflare/test/plugins/do/index.spec.ts
+++ b/packages/miniflare/test/plugins/do/index.spec.ts
@@ -5,6 +5,7 @@ import { setTimeout } from "node:timers/promises";
import { removeDir } from "@cloudflare/workers-utils";
import {
DeferredPromise,
+ DURABLE_OBJECTS_PLUGIN_NAME,
kUnsafeEphemeralUniqueKey,
Miniflare,
} from "miniflare";
@@ -71,20 +72,17 @@ test("persists Durable Object data in-memory between options reloads", async ({
res = await mf.dispatchFetch("http://localhost");
expect(await res.text()).toBe("Options #2: 2");
- opts.durableObjectsPersist = false;
opts.script = COUNTER_SCRIPT("Options #3: ");
await mf.setOptions(opts);
res = await mf.dispatchFetch("http://localhost");
expect(await res.text()).toBe("Options #3: 3");
- opts.durableObjectsPersist = "memory:";
opts.script = COUNTER_SCRIPT("Options #4: ");
await mf.setOptions(opts);
res = await mf.dispatchFetch("http://localhost");
expect(await res.text()).toBe("Options #4: 4");
// Check a `new Miniflare()` instance has its own in-memory storage
- delete opts.durableObjectsPersist;
opts.script = COUNTER_SCRIPT("Options #5: ");
await mf.dispose();
const mf2 = new Miniflare(opts);
@@ -112,7 +110,7 @@ test("persists Durable Object data on file-system", async ({ expect }) => {
modules: true,
script: COUNTER_SCRIPT(),
durableObjects: { COUNTER: "Counter" },
- durableObjectsPersist: tmp,
+ resourcePersistencePath: tmp,
};
const mf = new Miniflare(opts);
useDispose(mf);
@@ -120,8 +118,9 @@ test("persists Durable Object data on file-system", async ({ expect }) => {
let res = await mf.dispatchFetch("http://localhost");
expect(await res.text()).toBe("1");
- // Check directory created for "worker"'s Durable Object
- const names = await fs.readdir(tmp);
+ // Check directory created for "worker"'s Durable Object under the plugin subdirectory
+ const doTmp = path.join(tmp, DURABLE_OBJECTS_PLUGIN_NAME);
+ const names = await fs.readdir(doTmp);
expect(names).toEqual(["worker-Counter"]);
// Check reloading keeps persisted data
@@ -133,7 +132,7 @@ test("persists Durable Object data on file-system", async ({ expect }) => {
// reload here as `workerd` keeps a copy of the SQLite database in-memory,
// we also need to `dispose()` to avoid `EBUSY` error on Windows)
await mf.dispose();
- await removeDir(path.join(tmp, names[0]));
+ await removeDir(path.join(doTmp, names[0]));
const mf2 = new Miniflare(opts);
useDispose(mf2);
@@ -152,7 +151,7 @@ test("persists Durable Object data on file-system", async ({ expect }) => {
test("lists Durable Object ids with persisted storage", async ({ expect }) => {
const tmp = await useTmp();
const mf = new Miniflare({
- defaultPersistRoot: tmp,
+ resourcePersistencePath: tmp,
name: "worker",
modules: true,
script: COUNTER_SCRIPT(),
@@ -182,7 +181,7 @@ test("lists Durable Object ids with persisted storage", async ({ expect }) => {
test("multiple Workers access same Durable Object data", async ({ expect }) => {
const tmp = await useTmp();
const mf = new Miniflare({
- durableObjectsPersist: tmp,
+ resourcePersistencePath: tmp,
workers: [
{
name: "entry",
@@ -234,8 +233,8 @@ test("multiple Workers access same Durable Object data", async ({ expect }) => {
});
expect(await res.text()).toBe("via A: b: 1");
- // Check directory created for Durable Objects
- const names = await fs.readdir(tmp);
+ // Check directory created for Durable Objects under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, DURABLE_OBJECTS_PLUGIN_NAME));
expect(names.sort()).toEqual(["a-Counter", "b-Counter"]);
// Check accessing via a different service accesses same persisted data
diff --git a/packages/miniflare/test/plugins/email/index.spec.ts b/packages/miniflare/test/plugins/email/index.spec.ts
index e89eca93e3e..0f3dda90933 100644
--- a/packages/miniflare/test/plugins/email/index.spec.ts
+++ b/packages/miniflare/test/plugins/email/index.spec.ts
@@ -63,7 +63,7 @@ test("Unbound send_email binding works", async ({ expect }) => {
email: {
send_email: [{ name: "SEND_EMAIL" }],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -163,7 +163,7 @@ test("Single allowed destination send_email binding works", async ({
{ name: "SEND_EMAIL", destination_address: "someone-else@example.com" },
],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -478,7 +478,7 @@ test("reply validation: x-auto-response-suppress", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -518,7 +518,7 @@ test("reply validation: Auto-Submitted", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -558,7 +558,7 @@ test("reply validation: only In-Reply-To", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -598,7 +598,7 @@ test("reply validation: only References", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -639,7 +639,7 @@ test("reply validation: >100 References", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -682,7 +682,7 @@ test("reply: mismatched From: header", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -722,7 +722,7 @@ test("reply: unparseable", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -770,7 +770,7 @@ test("reply: no message id", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -820,7 +820,7 @@ test("reply: disallowed header", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -869,7 +869,7 @@ test("reply: missing In-Reply-To", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -921,7 +921,7 @@ test("reply: wrong In-Reply-To", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -976,7 +976,7 @@ test("reply: invalid references", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -1025,7 +1025,7 @@ test("reply: references generated correctly", async ({ expect }) => {
This is a random email body.`;
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/handler/email?" +
+ "http://localhost/cdn-cgi/local/email?" +
new URLSearchParams({
from: "someone@example.com",
to: "someone-else@example.com",
@@ -1079,7 +1079,7 @@ test("MessageBuilder with text only", async ({ expect }) => {
email: {
send_email: [{ name: "SEND_EMAIL" }],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -1194,7 +1194,7 @@ test("MessageBuilder with attachments", async ({ expect }) => {
email: {
send_email: [{ name: "SEND_EMAIL" }],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -1260,7 +1260,7 @@ test("MessageBuilder log output format snapshot", async ({ expect }) => {
email: {
send_email: [{ name: "SEND_EMAIL" }],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -2083,7 +2083,7 @@ test("disposing does not remove a concurrent email session", async ({
email: {
send_email: [{ name: "SEND_EMAIL" }],
},
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
compatibilityDate: "2025-03-17",
});
@@ -2123,7 +2123,7 @@ describe("EMAIL_PLUGIN.getServices", () => {
},
sharedOptions: {},
tmpPath: tmp,
- defaultProjectTmpPath: projectTmpPath,
+ resourceTmpPath: projectTmpPath,
workerNames: ["default"],
workerIndex: 0,
} as unknown as Parameters[0]);
@@ -2206,7 +2206,7 @@ describe("EMAIL_PLUGIN.getServices", () => {
expect(emailDiskServices[1].path).toBe(projectDisk.disk.path);
});
- test("creates only system disk service when defaultProjectTmpPath is undefined", async ({
+ test("creates only system disk service when resourceTmpPath is undefined", async ({
expect,
}) => {
const tmp = await useTmp();
@@ -2217,7 +2217,7 @@ describe("EMAIL_PLUGIN.getServices", () => {
},
sharedOptions: {},
tmpPath: tmp,
- defaultProjectTmpPath: undefined,
+ resourceTmpPath: undefined,
workerNames: ["default"],
workerIndex: 0,
} as unknown as Parameters[0]);
@@ -2307,7 +2307,7 @@ describe("getEmailPathsToClean", () => {
});
});
-test("MessageBuilder writes files to system temp when defaultProjectTmpPath is unset", async ({
+test("MessageBuilder writes files to system temp when resourceTmpPath is unset", async ({
expect,
}) => {
const log = new TestLog();
diff --git a/packages/miniflare/test/plugins/hello-world/index.spec.ts b/packages/miniflare/test/plugins/hello-world/index.spec.ts
index 5e6995beb0b..c9f0d90503d 100644
--- a/packages/miniflare/test/plugins/hello-world/index.spec.ts
+++ b/packages/miniflare/test/plugins/hello-world/index.spec.ts
@@ -10,7 +10,6 @@ test("hello-world", async ({ expect }) => {
enable_timer: true,
},
},
- helloWorldPersist: false,
modules: true,
script: `
export default {
diff --git a/packages/miniflare/test/plugins/images/index.spec.ts b/packages/miniflare/test/plugins/images/index.spec.ts
index 178d2e3f215..ecd37841438 100644
--- a/packages/miniflare/test/plugins/images/index.spec.ts
+++ b/packages/miniflare/test/plugins/images/index.spec.ts
@@ -54,7 +54,6 @@ function createMiniflare(): Miniflare {
return new Miniflare({
compatibilityDate: "2025-04-01",
images: { binding: "IMAGES" },
- imagesPersist: false,
modules: true,
script: WORKER_SCRIPT,
} satisfies MiniflareOptions);
@@ -93,7 +92,7 @@ function upload(
}
describe("Images local delivery", () => {
- test("variant URLs are absolute and use /cdn-cgi/mf/imagedelivery/ path", async ({
+ test("variant URLs are absolute and use /__cf_local/imagedelivery/ path", async ({
expect,
}) => {
const mf = createMiniflare();
@@ -103,7 +102,7 @@ describe("Images local delivery", () => {
const metadata = await upload(mf, TEST_IMAGE_BYTES, { id: "variant-test" });
expect(metadata.variants).toHaveLength(1);
expect(metadata.variants[0]).toBe(
- `${url.origin}/cdn-cgi/mf/imagedelivery/variant-test/public`
+ `${url.origin}/__cf_local/imagedelivery/variant-test/public`
);
});
@@ -115,7 +114,7 @@ describe("Images local delivery", () => {
await upload(mf, TEST_IMAGE_BYTES, { id: "delivery-test" });
const response = await mf.dispatchFetch(
- `${url.origin}/cdn-cgi/mf/imagedelivery/delivery-test/public`
+ `${url.origin}/__cf_local/imagedelivery/delivery-test/public`
);
expect(response.status).toBe(200);
const data = new Uint8Array(await response.arrayBuffer());
@@ -130,7 +129,7 @@ describe("Images local delivery", () => {
const url = await mf.ready;
const response = await mf.dispatchFetch(
- `${url.origin}/cdn-cgi/mf/imagedelivery/does-not-exist/public`
+ `${url.origin}/__cf_local/imagedelivery/does-not-exist/public`
);
expect(response.status).toBe(404);
await response.arrayBuffer();
diff --git a/packages/miniflare/test/plugins/kv/index.spec.ts b/packages/miniflare/test/plugins/kv/index.spec.ts
index bb16aed11ce..c595402abb5 100644
--- a/packages/miniflare/test/plugins/kv/index.spec.ts
+++ b/packages/miniflare/test/plugins/kv/index.spec.ts
@@ -2,12 +2,10 @@ import assert from "node:assert";
import { Blob } from "node:buffer";
import fs from "node:fs/promises";
import path from "node:path";
-import consumers from "node:stream/consumers";
import { KV_PLUGIN_NAME, MAX_BULK_GET_KEYS, Miniflare } from "miniflare";
import { beforeEach, type ExpectStatic, test } from "vitest";
import {
createJunkStream,
- FIXTURES_PATH,
MiniflareDurableObjectControlStub,
miniflareTest,
namespace,
@@ -790,7 +788,7 @@ test("persists on file-system", async ({ expect }) => {
modules: true,
script: "",
kvNamespaces: { NAMESPACE: "namespace" },
- kvPersist: tmp,
+ resourcePersistencePath: tmp,
};
let mf = new Miniflare(opts);
useDispose(mf);
@@ -799,8 +797,8 @@ test("persists on file-system", async ({ expect }) => {
await kv.put("key", "value");
expect(await kv.get("key")).toBe("value");
- // Check directory created for namespace
- const names = await fs.readdir(tmp);
+ // Check directory created for namespace under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, KV_PLUGIN_NAME));
expect(names.includes("miniflare-KVNamespaceObject")).toBe(true);
// Check "restarting" keeps persisted data
@@ -810,63 +808,3 @@ test("persists on file-system", async ({ expect }) => {
kv = await mf.getKVNamespace("NAMESPACE");
expect(await kv.get("key")).toBe("value");
});
-
-test("migrates database to new location", async ({ expect }) => {
- // Copy legacy data to temporary directory
- const tmp = await useTmp();
- const persistFixture = path.join(FIXTURES_PATH, "migrations", "3.20230821.0");
- const kvPersist = path.join(tmp, "kv");
- await fs.cp(path.join(persistFixture, "kv"), kvPersist, { recursive: true });
-
- // Implicitly migrate data
- const mf = new Miniflare({
- modules: true,
- script: "",
- kvNamespaces: ["NAMESPACE"],
- kvPersist,
- });
- useDispose(mf);
-
- const namespace = await mf.getKVNamespace("NAMESPACE");
- expect(await namespace.get("key")).toBe("value");
-});
-
-test("sticky blobs never deleted", async ({ expect }) => {
- // Checking regular behaviour that old blobs deleted in `put: overrides
- // existing keys` test. Only testing sticky blobs for KV, as the blob store
- // should only be constructed in the shared `MiniflareDurableObject` ABC.
-
- // Create instance with sticky blobs enabled (can't use `ctx.mf`)
- const mf = new Miniflare({
- script: "",
- modules: true,
- kvNamespaces: ["NAMESPACE"],
- unsafeStickyBlobs: true,
- });
- useDispose(mf);
-
- // Create control stub for newly created instance's namespace
- const objectNamespace = await mf._getInternalDurableObjectNamespace(
- KV_PLUGIN_NAME,
- "kv:ns",
- "KVNamespaceObject"
- );
- const objectId = objectNamespace.idFromName("NAMESPACE");
- const objectStub = objectNamespace.get(objectId);
- const object = new MiniflareDurableObjectControlStub(objectStub);
- await object.enableFakeTimers(secondsToMillis(TIME_NOW));
- const stmts = sqlStmts(object);
-
- // Store something in the namespace and get the blob ID
- const ns = await mf.getKVNamespace("NAMESPACE");
- await ns.put("key", "value 1");
- const blobId = await stmts.getBlobIdByKey("key");
- assert(blobId !== undefined);
-
- // Override key and check we can still access the old blob
- await ns.put("key", "value 2");
- await object.waitForFakeTasks();
- const blob = await object.getBlob(blobId);
- assert(blob !== null);
- expect(await consumers.text(blob)).toBe("value 1");
-});
diff --git a/packages/miniflare/test/plugins/local-explorer/aggregation.spec.ts b/packages/miniflare/test/plugins/local-explorer/aggregation.spec.ts
index 1034f276a7f..32c677a819f 100644
--- a/packages/miniflare/test/plugins/local-explorer/aggregation.spec.ts
+++ b/packages/miniflare/test/plugins/local-explorer/aggregation.spec.ts
@@ -633,7 +633,7 @@ describe("Same ID across multiple instances with same persistence directories",
script: `export default { fetch() { return new Response("Worker A"); } }`,
unsafeLocalExplorer: true,
unsafeDevRegistryPath: registryPath,
- defaultPersistRoot: persistencePath,
+ resourcePersistencePath: persistencePath,
kvNamespaces: {
MY_KV: "shared-kv-id",
},
@@ -652,7 +652,7 @@ describe("Same ID across multiple instances with same persistence directories",
script: `export default { fetch() { return new Response("Worker B"); } }`,
unsafeLocalExplorer: true,
unsafeDevRegistryPath: registryPath,
- defaultPersistRoot: persistencePath,
+ resourcePersistencePath: persistencePath,
kvNamespaces: {
MY_KV: "shared-kv-id",
},
diff --git a/packages/miniflare/test/plugins/local-explorer/do.spec.ts b/packages/miniflare/test/plugins/local-explorer/do.spec.ts
index d28897ac181..940dea834f3 100644
--- a/packages/miniflare/test/plugins/local-explorer/do.spec.ts
+++ b/packages/miniflare/test/plugins/local-explorer/do.spec.ts
@@ -188,7 +188,7 @@ describe("Durable Objects API", () => {
}
`,
unsafeLocalExplorer: true,
- defaultPersistRoot: persistPath,
+ resourcePersistencePath: persistPath,
durableObjects: {
TEST_DO: { className: "TestDO", useSQLite: true },
},
diff --git a/packages/miniflare/test/plugins/local-explorer/helpers.ts b/packages/miniflare/test/plugins/local-explorer/helpers.ts
index 414cac93dac..998dd80f2d5 100644
--- a/packages/miniflare/test/plugins/local-explorer/helpers.ts
+++ b/packages/miniflare/test/plugins/local-explorer/helpers.ts
@@ -1,11 +1,11 @@
+import { z } from "zod";
import type { ExpectStatic } from "vitest";
-import type { z } from "zod";
/**
* Validates a response body against a Zod schema and returns typed data.
* Throws a descriptive error if validation fails.
*/
-export async function expectValidResponse(
+export async function expectValidResponse(
response: Response,
schema: T,
expect: ExpectStatic,
@@ -18,7 +18,7 @@ export async function expectValidResponse(
if (!result.success) {
throw new Error(
`Response validation failed:\n${JSON.stringify(
- result.error.format(),
+ z.treeifyError(result.error),
null,
2
)}\n\nActual response:\n${JSON.stringify(json, null, 2)}`
diff --git a/packages/miniflare/test/plugins/local-explorer/index.spec.ts b/packages/miniflare/test/plugins/local-explorer/index.spec.ts
index 2b492d4f8d7..8fa26ece0c2 100644
--- a/packages/miniflare/test/plugins/local-explorer/index.spec.ts
+++ b/packages/miniflare/test/plugins/local-explorer/index.spec.ts
@@ -66,7 +66,7 @@ describe("Local Explorer API validation", () => {
errors: [
{
code: 10001,
- message: "limit: Number must be greater than or equal to 10",
+ message: "limit: Too small: expected number to be >=10",
},
],
});
@@ -83,7 +83,7 @@ describe("Local Explorer API validation", () => {
errors: [
{
code: 10001,
- message: "limit: Number must be less than or equal to 1000",
+ message: "limit: Too big: expected number to be <=1000",
},
],
});
@@ -107,7 +107,7 @@ describe("Local Explorer API validation", () => {
errors: [
{
code: 10001,
- message: "keys: Expected array, received string",
+ message: "keys: Invalid input: expected array, received string",
},
],
});
@@ -129,7 +129,7 @@ describe("Local Explorer API validation", () => {
errors: [
{
code: 10001,
- message: "keys: Required",
+ message: "keys: Invalid input: expected array, received undefined",
},
],
});
@@ -259,9 +259,11 @@ describe("Local Explorer API validation", () => {
});
describe("routing", () => {
- test("serves OpenAPI spec at /cdn-cgi/explorer/api", async ({ expect }) => {
+ test("serves OpenAPI spec at /cdn-cgi/local/explorer/api", async ({
+ expect,
+ }) => {
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/explorer/api"
+ "http://localhost/cdn-cgi/local/explorer/api"
);
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("application/json");
@@ -273,28 +275,36 @@ describe("Local Explorer API validation", () => {
});
});
- test("serves explorer UI at /cdn-cgi/explorer", async ({ expect }) => {
- const res = await mf.dispatchFetch("http://localhost/cdn-cgi/explorer");
+ test("serves explorer UI at /cdn-cgi/local/explorer", async ({
+ expect,
+ }) => {
+ const res = await mf.dispatchFetch(
+ "http://localhost/cdn-cgi/local/explorer"
+ );
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("text/html");
await res.arrayBuffer(); // Drain
});
- test("serves explorer UI at /cdn-cgi/explorer/", async ({ expect }) => {
- const res = await mf.dispatchFetch("http://localhost/cdn-cgi/explorer/");
+ test("serves explorer UI at /cdn-cgi/local/explorer/", async ({
+ expect,
+ }) => {
+ const res = await mf.dispatchFetch(
+ "http://localhost/cdn-cgi/local/explorer/"
+ );
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("text/html");
await res.arrayBuffer(); // Drain
});
- test("does not match paths that start with /cdn-cgi/explorer but are not the explorer", async ({
+ test("does not match paths that start with /cdn-cgi/local/explorer but are not the explorer", async ({
expect,
}) => {
- // This should fall through to the user worker, not match the explorer
+ // /cdn-cgi/local/explorerfoo falls through to the user worker
const res = await mf.dispatchFetch(
- "http://localhost/cdn-cgi/explorerfoo"
+ "http://localhost/cdn-cgi/local/explorerfoo"
);
expect(res.status).toBe(200);
expect(await res.text()).toBe("user worker");
diff --git a/packages/miniflare/test/plugins/local-explorer/telemetry.spec.ts b/packages/miniflare/test/plugins/local-explorer/telemetry.spec.ts
index 501a0c8ca38..6f2b28ad7ac 100644
--- a/packages/miniflare/test/plugins/local-explorer/telemetry.spec.ts
+++ b/packages/miniflare/test/plugins/local-explorer/telemetry.spec.ts
@@ -15,7 +15,7 @@ describe("getRouteName", () => {
test(`handles ${method.toUpperCase()} ${path}`, ({ expect }) => {
// Replace {param} placeholders with dummy values
const testPath = path.replace(/\{[^}]+\}/g, "test-id");
- const fullPath = `/cdn-cgi/explorer/api${testPath}`;
+ const fullPath = `/cdn-cgi/local/explorer/api${testPath}`;
const routeName = getRouteName(fullPath);
@@ -27,12 +27,14 @@ describe("getRouteName", () => {
});
test("maps routes to expected names", ({ expect }) => {
- expect(getRouteName(`/cdn-cgi/explorer/api/storage/kv/namespaces`)).toBe(
- "kv.namespaces"
- );
+ expect(
+ getRouteName(`/cdn-cgi/local/explorer/api/storage/kv/namespaces`)
+ ).toBe("kv.namespaces");
});
test("returns unknown for unrecognized paths", ({ expect }) => {
- expect(getRouteName("/cdn-cgi/explorer/api/unknown/path")).toBe("unknown");
+ expect(getRouteName("/cdn-cgi/local/explorer/api/unknown/path")).toBe(
+ "unknown"
+ );
});
});
diff --git a/packages/miniflare/test/plugins/queues/index.spec.ts b/packages/miniflare/test/plugins/queues/index.spec.ts
index ad2532b6053..42be440d041 100644
--- a/packages/miniflare/test/plugins/queues/index.spec.ts
+++ b/packages/miniflare/test/plugins/queues/index.spec.ts
@@ -64,7 +64,7 @@ test("maxBatchTimeout validation", async ({ expect }) => {
error = e as MiniflareCoreError;
}
expect(error?.code).toEqual("ERR_VALIDATION");
- expect(error?.message).toMatch(/Number must be less than or equal to 60/);
+ expect(error?.message).toMatch(/Too big: expected number to be <=60/);
});
test("flushes partial and full batches", async ({ expect }) => {
diff --git a/packages/miniflare/test/plugins/r2/index.spec.ts b/packages/miniflare/test/plugins/r2/index.spec.ts
index 077f1b6f1b3..7ca639a6585 100644
--- a/packages/miniflare/test/plugins/r2/index.spec.ts
+++ b/packages/miniflare/test/plugins/r2/index.spec.ts
@@ -8,7 +8,6 @@ import { text } from "node:stream/consumers";
import { Headers, Miniflare, R2_PLUGIN_NAME } from "miniflare";
import { beforeEach, type ExpectStatic, onTestFinished, test } from "vitest";
import {
- FIXTURES_PATH,
MiniflareDurableObjectControlStub,
miniflareTest,
namespace,
@@ -1000,7 +999,7 @@ test("operations persist stored data", async ({ expect }) => {
modules: true,
script: "",
r2Buckets: { BUCKET: "bucket" },
- r2Persist: tmp,
+ resourcePersistencePath: tmp,
};
const mf = new Miniflare(persistOpts);
useDispose(mf);
@@ -1013,8 +1012,8 @@ test("operations persist stored data", async ({ expect }) => {
let object = await r2.head("key");
expect(object?.size).toBe(5);
- // Check directory created for namespace
- const names = await fs.readdir(tmp);
+ // Check directory created for namespace under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, R2_PLUGIN_NAME));
expect(names.includes("miniflare-R2BucketObject")).toBe(true);
// Check "restarting" keeps persisted data
@@ -1608,24 +1607,3 @@ test("list: is multipart aware", async ({ expect }) => {
expect(object?.customMetadata).toEqual({ key: "value" });
expect(object?.httpMetadata).toEqual({ contentType: "text/plain" });
});
-
-test("migrates database to new location", async ({ expect }) => {
- // Copy legacy data to temporary directory
- const tmp = await useTmp();
- const persistFixture = path.join(FIXTURES_PATH, "migrations", "3.20230821.0");
- const r2Persist = path.join(tmp, "r2");
- await fs.cp(path.join(persistFixture, "r2"), r2Persist, { recursive: true });
-
- // Implicitly migrate data
- const mf = new Miniflare({
- modules: true,
- script: "",
- r2Buckets: ["BUCKET"],
- r2Persist,
- });
- useDispose(mf);
-
- const bucket = await mf.getR2Bucket("BUCKET");
- const object = await bucket.get("key");
- expect(await object?.text()).toBe("value");
-});
diff --git a/packages/miniflare/test/plugins/secret-store/index.spec.ts b/packages/miniflare/test/plugins/secret-store/index.spec.ts
index 25de329f69e..50dda2f052b 100644
--- a/packages/miniflare/test/plugins/secret-store/index.spec.ts
+++ b/packages/miniflare/test/plugins/secret-store/index.spec.ts
@@ -11,7 +11,6 @@ test("single secret-store", async ({ expect }) => {
secret_name: "secret_name",
},
},
- secretsStorePersist: false,
modules: true,
script: `
export default {
diff --git a/packages/miniflare/test/plugins/stream/index.spec.ts b/packages/miniflare/test/plugins/stream/index.spec.ts
index 33ed4a1a9aa..e47bc902fda 100644
--- a/packages/miniflare/test/plugins/stream/index.spec.ts
+++ b/packages/miniflare/test/plugins/stream/index.spec.ts
@@ -1,4 +1,3 @@
-import { pathToFileURL } from "node:url";
import {
Miniflare,
STREAM_COMPAT_DATE,
@@ -140,7 +139,6 @@ function createMiniflare(options: Partial = {}): Miniflare {
return new Miniflare({
compatibilityDate: STREAM_COMPAT_DATE,
stream: { binding: "STREAM" },
- streamPersist: false,
modules: true,
script: WORKER_SCRIPT,
...options,
@@ -211,7 +209,7 @@ describe("Stream videos", () => {
expect(video.hlsPlaybackUrl).toContain(video.id);
expect(video.dashPlaybackUrl).toContain(video.id);
expect(video.preview).toMatch(
- new RegExp(`^http://.*?/cdn-cgi/mf/stream/${video.id}/watch$`)
+ new RegExp(`^http://.*?/__cf_local/stream/${video.id}/watch$`)
);
const details = (await sendCmdToWorker(mf, "video.details", {
@@ -646,7 +644,7 @@ describe("Stream videos list", () => {
});
describe("Stream video serving", () => {
- test("serve video via /cdn-cgi/mf/stream/:id/watch", async ({ expect }) => {
+ test("serve video via /__cf_local/stream/:id/watch", async ({ expect }) => {
const mf = createMiniflare();
useDispose(mf);
const { http: videoUrl } = await useServer(
@@ -659,7 +657,7 @@ describe("Stream video serving", () => {
// Fetch the video via the preview URL path and consume body immediately
const resp = await mf.dispatchFetch(
- `http://placeholder/cdn-cgi/mf/stream/${video.id}/watch`
+ `http://placeholder/__cf_local/stream/${video.id}/watch`
);
const bytes = new Uint8Array(await resp.arrayBuffer());
expect(resp.status).toBe(200);
@@ -671,7 +669,7 @@ describe("Stream video serving", () => {
useDispose(mf);
const resp = await mf.dispatchFetch(
- "http://placeholder/cdn-cgi/mf/stream/00000000-0000-0000-0000-000000000000/watch"
+ "http://placeholder/__cf_local/stream/00000000-0000-0000-0000-000000000000/watch"
);
await resp.arrayBuffer(); // consume body to avoid dispatchFetch error
expect(resp.status).toBe(404);
@@ -706,7 +704,6 @@ describe("Stream reloads", () => {
const opts = {
compatibilityDate: STREAM_COMPAT_DATE,
stream: { binding: "STREAM" },
- streamPersist: false,
modules: true,
script: WORKER_SCRIPT,
} satisfies MiniflareOptions;
@@ -732,9 +729,7 @@ describe("Stream reloads", () => {
expect(videos[0].id).toBe(video.id);
});
- test("keeps persisted data when persistence path format changes on reload", async ({
- expect,
- }) => {
+ test("keeps persisted data across setOptions reloads", async ({ expect }) => {
const tmp = await useTmp();
const { http: videoUrl } = await useServer(
staticBytesListener(TEST_VIDEO_BYTES)
@@ -742,7 +737,7 @@ describe("Stream reloads", () => {
const opts = {
compatibilityDate: STREAM_COMPAT_DATE,
stream: { binding: "STREAM" },
- streamPersist: tmp,
+ resourcePersistencePath: tmp,
modules: true,
script: WORKER_SCRIPT,
} satisfies MiniflareOptions;
@@ -755,7 +750,6 @@ describe("Stream reloads", () => {
await mf.setOptions({
...opts,
- streamPersist: pathToFileURL(tmp).href,
script: `${WORKER_SCRIPT}\n// reload persisted stream worker`,
});
@@ -1626,7 +1620,7 @@ describe("Stream publicUrl", () => {
})) as Video;
expect(video.preview).toBe(
- `http://my-proxy.example.com:8080/cdn-cgi/mf/stream/${video.id}/watch`
+ `http://my-proxy.example.com:8080/__cf_local/stream/${video.id}/watch`
);
});
@@ -1647,7 +1641,7 @@ describe("Stream publicUrl", () => {
// (http://127.0.0.1:) rather than any external proxy URL
expect(video.preview).toMatch(
new RegExp(
- `^http://127\\.0\\.0\\.1:\\d+/cdn-cgi/mf/stream/${video.id}/watch$`
+ `^http://127\\.0\\.0\\.1:\\d+/__cf_local/stream/${video.id}/watch$`
)
);
});
diff --git a/packages/miniflare/test/plugins/workflows/index.spec.ts b/packages/miniflare/test/plugins/workflows/index.spec.ts
index 56061c1fb7d..9ab62d25b6d 100644
--- a/packages/miniflare/test/plugins/workflows/index.spec.ts
+++ b/packages/miniflare/test/plugins/workflows/index.spec.ts
@@ -1,6 +1,7 @@
import * as fs from "node:fs/promises";
+import path from "node:path";
import { scheduler } from "node:timers/promises";
-import { Miniflare } from "miniflare";
+import { Miniflare, WORKFLOWS_PLUGIN_NAME } from "miniflare";
import { describe, test } from "vitest";
import { useDispose, useTmp } from "../../test-shared";
import type { MiniflareOptions } from "miniflare";
@@ -42,7 +43,7 @@ test("starts Workflows with user-provided experimental compatibility flag", asyn
],
},
},
- workflowsPersist: tmp,
+ resourcePersistencePath: tmp,
});
useDispose(mf);
@@ -67,7 +68,7 @@ test("persists Workflow data on file-system between runs", async ({
name: "MY_WORKFLOW",
},
},
- workflowsPersist: tmp,
+ resourcePersistencePath: tmp,
};
const mf = new Miniflare(opts);
useDispose(mf);
@@ -97,8 +98,8 @@ test("persists Workflow data on file-system between runs", async ({
true
);
- // check if files were committed
- const names = await fs.readdir(tmp);
+ // check if files were committed under the plugin subdirectory
+ const names = await fs.readdir(path.join(tmp, WORKFLOWS_PLUGIN_NAME));
expect(names).toEqual(["miniflare-workflows-MY_WORKFLOW"]);
// restart miniflare
@@ -191,7 +192,7 @@ function lifecycleMiniflareOpts(tmp: string): MiniflareOptions {
name: "LIFECYCLE_WORKFLOW",
},
},
- workflowsPersist: tmp,
+ resourcePersistencePath: tmp,
};
}
diff --git a/packages/miniflare/test/snapshots/zod-format.spec.ts.md b/packages/miniflare/test/snapshots/zod-format.spec.ts.md
deleted file mode 100644
index ffad9d4bf0f..00000000000
--- a/packages/miniflare/test/snapshots/zod-format.spec.ts.md
+++ /dev/null
@@ -1,307 +0,0 @@
-# Snapshot report for `test/zod-format.spec.ts`
-
-The actual snapshot is saved in `zod-format.spec.ts.snap`.
-
-Generated by [AVA](https://avajs.dev).
-
-## formatZodError: formats primitive schema with primitive input
-
-> Snapshot 1
-
- `false␊
- ^ Expected number, received boolean`
-
-## formatZodError: formats primitive schema with object input
-
-> Snapshot 1
-
- `{ a: 1, b: [Object] }␊
- ^ Expected string, received object`
-
-## formatZodError: formats object schema with primitive input
-
-> Snapshot 1
-
- `true␊
- ^ Expected object, received boolean`
-
-## formatZodError: formats object schema with object input
-
-> Snapshot 1
-
- `{␊
- ...,␊
- b: '2',␊
- ^ Expected number, received string␊
- ...,␊
- g: '7',␊
- ^ Expected boolean, received string␊
- f: undefined,␊
- ^ Required␊
- }`
-
-## formatZodError: formats object schema with additional options
-
-> Snapshot 1
-
- `{ a: 1, b: 2 }␊
- ^ Unrecognized key(s) in object: 'b'`
-
-## formatZodError: formats array schema with primitive input
-
-> Snapshot 1
-
- `1␊
- ^ Expected array, received number`
-
-## formatZodError: formats array schema with array input
-
-> Snapshot 1
-
- `[␊
- ...,␊
- /* [2] */ '3',␊
- ^ Expected number, received string␊
- ...,␊
- /* [5] */ false,␊
- ^ Expected number, received boolean␊
- ]`
-
-## formatZodError: formats array schema with additional options
-
-> Snapshot 1
-
- `[ 1, 2, 3, 4, 5 ]␊
- ^ Array must contain at most 3 element(s)`
-
-## formatZodError: formats deeply nested schema
-
-> Snapshot 1
-
- `{␊
- a: '1',␊
- ^ Expected number, received string␊
- b: {␊
- c: 2,␊
- ^ Expected string, received number␊
- d: [␊
- ...,␊
- /* [1] */ {␊
- e: 42,␊
- ^ Expected boolean, received number␊
- },␊
- /* [2] */ false,␊
- ^ Expected object, received boolean␊
- /* [3] */ {␊
- e: undefined,␊
- ^ Required␊
- },␊
- ],␊
- f: [Function: f],␊
- ^ Expected array, received function␊
- },␊
- g: undefined,␊
- ^ Required␊
- }`
-
-## formatZodError: formats large actual values
-
-> Snapshot 1
-
- `{␊
- a: {␊
- b: [␊
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,␊
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,␊
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,␊
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,␊
- 44, 45, 46, 47, 48, 49␊
- ],␊
- ^ Expected string, received array␊
- },␊
- }`
-
-## formatZodError: formats union schema
-
-> Snapshot 1
-
- `'a'␊
- ^ Expected boolean, received string␊
- Invalid literal value, expected 1`
-
-## formatZodError: formats discriminated union schema
-
-> Snapshot 1
-
- `{␊
- ...,␊
- a: false,␊
- ^ Expected number, received boolean␊
- }`
-
-## formatZodError: formats discriminated union schema with invalid discriminator
-
-> Snapshot 1
-
- `{␊
- type: 'c',␊
- ^ Invalid discriminator value. Expected 'a' | 'b'␊
- }`
-
-## formatZodError: formats intersection schema
-
-> Snapshot 1
-
- `false␊
- ^ Expected number, received boolean␊
- Invalid literal value, expected 2`
-
-## formatZodError: formats object union schema
-
-> Snapshot 1
-
- `{␊
- key: false,␊
- ^ Expected string, received boolean␊
- objects: [␊
- /* [0] */ false,␊
- ^ Expected object, received boolean␊
- ...,␊
- /* [2] */ {␊
- a: undefined,␊
- ^1 Required *or*␊
- b: undefined,␊
- ^1 Required *or*␊
- c: undefined,␊
- ^1 Required␊
- },␊
- /* [3] */ [],␊
- ^ Expected object, received array␊
- /* [4] */ {␊
- ...,␊
- a: undefined,␊
- ^2 Required *or*␊
- b: undefined,␊
- ^2 Required *or*␊
- c: undefined,␊
- ^2 Required␊
- },␊
- ],␊
- }`
-
-## formatZodError: formats object union schema in colour
-
-> Snapshot 1
-
- `[2m{[22m␊
- [2mkey: [22m[33mfalse[39m[2m,[22m␊
- [31m ^ Expected string, received boolean[39m␊
- [2mobjects: [[22m␊
- [2m/* [0] */ [22m[33mfalse[39m[2m,[22m␊
- [31m ^ Expected object, received boolean[39m␊
- [2m/* [1] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [33m ^1 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [33m ^1 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [33m ^1 Required[39m␊
- [2m},[22m␊
- [2m/* [2] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [36m ^2 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [36m ^2 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [36m ^2 Required[39m␊
- [2m},[22m␊
- [2m/* [3] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [34m ^3 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [34m ^3 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [34m ^3 Required[39m␊
- [2m},[22m␊
- [2m/* [4] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [35m ^4 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [35m ^4 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [35m ^4 Required[39m␊
- [2m},[22m␊
- [2m/* [5] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [32m ^5 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [32m ^5 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [32m ^5 Required[39m␊
- [2m},[22m␊
- [2m/* [6] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [33m ^6 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [33m ^6 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [33m ^6 Required[39m␊
- [2m},[22m␊
- [2m/* [7] */ {[22m␊
- [2ma: [22m[90mundefined[39m[2m,[22m␊
- [36m ^7 Required *or*[39m␊
- [2mb: [22m[90mundefined[39m[2m,[22m␊
- [36m ^7 Required *or*[39m␊
- [2mc: [22m[90mundefined[39m[2m,[22m␊
- [36m ^7 Required[39m␊
- [2m},[22m␊
- [2m],[22m␊
- [2m}[22m`
-
-## formatZodError: formats tuple union schema
-
-> Snapshot 1
-
- `{␊
- tuples: [␊
- /* [0] */ false,␊
- ^ Expected array, received boolean␊
- /* [1] */ { a: 1 },␊
- ^ Expected array, received object␊
- /* [2] */ [],␊
- ^ Array must contain at least 2 element(s)␊
- Array must contain at least 3 element(s)␊
- /* [3] */ [␊
- ...,␊
- /* [1] */ '3',␊
- ^ Expected number, received string␊
- ],␊
- /* [4] */ [␊
- /* [0] */ 4,␊
- ^1 Expected string, received number␊
- Expected boolean, received number *or*␊
- /* [1] */ 5,␊
- ^1 Expected boolean, received number *or*␊
- /* [2] */ 6,␊
- ^1 Expected boolean, received number␊
- ],␊
- /* [5] */ [␊
- /* [0] */ true,␊
- ^2 Expected string, received boolean *or*␊
- /* [1] */ 7,␊
- ^2 Expected boolean, received number␊
- ...,␊
- ],␊
- ],␊
- }`
-
-## formatZodError: formats custom message schema
-
-> Snapshot 1
-
- `{␊
- a: Symbol(kOoh),␊
- ^ Custom message␊
- with multiple␊
- lines␊
- }`
diff --git a/packages/miniflare/test/snapshots/zod-format.spec.ts.snap b/packages/miniflare/test/snapshots/zod-format.spec.ts.snap
deleted file mode 100644
index 3c9c0e27d67..00000000000
Binary files a/packages/miniflare/test/snapshots/zod-format.spec.ts.snap and /dev/null differ
diff --git a/packages/miniflare/test/zod-format.spec.ts b/packages/miniflare/test/zod-format.spec.ts
deleted file mode 100644
index d3ebfb0bec1..00000000000
--- a/packages/miniflare/test/zod-format.spec.ts
+++ /dev/null
@@ -1,458 +0,0 @@
-import assert from "node:assert";
-import { _forceColour, formatZodError } from "miniflare";
-import { describe, test } from "vitest";
-import { z } from "zod";
-
-function formatZodErrorForTest(
- schema: z.ZodTypeAny,
- input: unknown,
- colour?: boolean
-) {
- const result = schema.safeParse(input);
- assert(!result.success);
- // Disable colours by default for easier-to-read snapshots
- _forceColour(colour ?? false);
- return formatZodError(result.error, input);
-}
-
-describe("formatZodError:", () => {
- test("formats primitive schema with primitive input", ({ expect }) => {
- const formatted = formatZodErrorForTest(z.number(), false);
- expect(formatted).toMatchInlineSnapshot(`
- "false
- ^ Expected number, received boolean"
- `);
- });
- test("formats primitive schema with object input", ({ expect }) => {
- const formatted = formatZodErrorForTest(z.string(), {
- a: 1,
- b: { c: 1 },
- });
- expect(formatted).toMatchInlineSnapshot(`
- "{ a: 1, b: [Object] }
- ^ Expected string, received object"
- `);
- });
-
- test("formats object schema with primitive input", ({ expect }) => {
- const formatted = formatZodErrorForTest(z.object({ a: z.number() }), true);
- expect(formatted).toMatchInlineSnapshot(`
- "true
- ^ Expected object, received boolean"
- `);
- });
- test("formats object schema with object input", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({
- a: z.string(),
- b: z.number(),
- c: z.boolean(),
- d: z.number(),
- e: z.number(),
- f: z.boolean(),
- g: z.boolean(),
- }),
- {
- a: "", // Check skips valid
- b: "2",
- c: true, // Check skips valid
- d: 4, // Check doesn't duplicate `...` when skipping valid
- e: 5,
- /*f*/ // Check required options
- g: "7",
- }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{
- ...,
- b: '2',
- ^ Expected number, received string
- ...,
- g: '7',
- ^ Expected boolean, received string
- f: undefined,
- ^ Required
- }"
- `);
- });
- test("formats object schema with additional options", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({ a: z.number() }).strict(),
- { a: 1, b: 2 }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{ a: 1, b: 2 }
- ^ Unrecognized key(s) in object: 'b'"
- `);
- });
-
- test("formats array schema with primitive input", ({ expect }) => {
- const formatted = formatZodErrorForTest(z.array(z.boolean()), 1);
- expect(formatted).toMatchInlineSnapshot(`
- "1
- ^ Expected array, received number"
- `);
- });
- test("formats array schema with array input", ({ expect }) => {
- const formatted = formatZodErrorForTest(z.array(z.number()), [
- 1, // Check skips valid
- 2, // Check doesn't duplicate `...` when skipping valid
- "3",
- 4,
- 5,
- false,
- ]);
- expect(formatted).toMatchInlineSnapshot(`
- "[
- ...,
- /* [2] */ '3',
- ^ Expected number, received string
- ...,
- /* [5] */ false,
- ^ Expected number, received boolean
- ]"
- `);
- });
- test("formats array schema with additional options", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.array(z.number()).max(3),
- [1, 2, 3, 4, 5]
- );
- expect(formatted).toMatchInlineSnapshot(`
- "[ 1, 2, 3, 4, 5 ]
- ^ Array must contain at most 3 element(s)"
- `);
- });
-
- test("formats deeply nested schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({
- a: z.number(),
- b: z.object({
- c: z.string(),
- d: z.array(z.object({ e: z.boolean() })),
- f: z.array(z.number()),
- }),
- g: z.string(),
- }),
- {
- a: "1",
- b: {
- c: 2,
- d: [{ e: true }, { e: 42 }, false, {}],
- f: () => {},
- },
- }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{
- a: '1',
- ^ Expected number, received string
- b: {
- c: 2,
- ^ Expected string, received number
- d: [
- ...,
- /* [1] */ {
- e: 42,
- ^ Expected boolean, received number
- },
- /* [2] */ false,
- ^ Expected object, received boolean
- /* [3] */ {
- e: undefined,
- ^ Required
- },
- ],
- f: [Function: f],
- ^ Expected array, received function
- },
- g: undefined,
- ^ Required
- }"
- `);
- });
-
- test("formats large actual values", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({
- a: z.object({
- b: z.string(),
- }),
- }),
- {
- a: {
- // Check indents inspected value at correct depth
- b: Array.from({ length: 50 }).map((_, i) => i),
- },
- }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{
- a: {
- b: [
- 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
- 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
- 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
- 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
- 44, 45, 46, 47, 48, 49
- ],
- ^ Expected string, received array
- },
- }"
- `);
- });
-
- test("formats union schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.union([z.boolean(), z.literal(1)]),
- "a"
- );
- expect(formatted).toMatchInlineSnapshot(`
- "'a'
- ^ Expected boolean, received string
- Invalid literal value, expected 1"
- `);
- });
-
- const discriminatedUnionSchema = z.discriminatedUnion("type", [
- z.object({
- type: z.literal("a"),
- a: z.number(),
- }),
- z.object({
- type: z.literal("b"),
- b: z.boolean(),
- }),
- ]);
- test("formats discriminated union schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(discriminatedUnionSchema, {
- type: "a",
- a: false,
- });
- expect(formatted).toMatchInlineSnapshot(`
- "{
- ...,
- a: false,
- ^ Expected number, received boolean
- }"
- `);
- });
- test("formats discriminated union schema with invalid discriminator", ({
- expect,
- }) => {
- const formatted = formatZodErrorForTest(discriminatedUnionSchema, {
- type: "c",
- });
- expect(formatted).toMatchInlineSnapshot(`
- "{
- type: 'c',
- ^ Invalid discriminator value. Expected 'a' | 'b'
- }"
- `);
- });
-
- test("formats intersection schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.intersection(z.number(), z.literal(2)),
- false
- );
- expect(formatted).toMatchInlineSnapshot(`
- "false
- ^ Expected number, received boolean
- Invalid literal value, expected 2"
- `);
- });
-
- const objectUnionSchema = z.object({
- key: z.string(),
- objects: z.array(
- z.union([
- z.object({ a: z.number() }),
- z.object({ b: z.boolean() }),
- z.object({ c: z.string() }),
- ])
- ),
- });
- test("formats object union schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(objectUnionSchema, {
- key: false,
- objects: [false, { a: 1 }, {}, [], { d: "" }],
- });
- expect(formatted).toMatchInlineSnapshot(`
- "{
- key: false,
- ^ Expected string, received boolean
- objects: [
- /* [0] */ false,
- ^ Expected object, received boolean
- ...,
- /* [2] */ {
- a: undefined,
- ^1 Required *or*
- b: undefined,
- ^1 Required *or*
- c: undefined,
- ^1 Required
- },
- /* [3] */ [],
- ^ Expected object, received array
- /* [4] */ {
- ...,
- a: undefined,
- ^2 Required *or*
- b: undefined,
- ^2 Required *or*
- c: undefined,
- ^2 Required
- },
- ],
- }"
- `);
- });
- test("formats object union schema in colour", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- objectUnionSchema,
- {
- key: false,
- objects: [false, {}, {}, {}, {}, {}, /* cycle */ {}, {}],
- },
- /* colour */ true
- );
- expect(formatted).toMatchInlineSnapshot(`
- "[2m{[22m
- [2mkey: [22m[33mfalse[39m[2m,[22m
- [31m ^ Expected string, received boolean[39m
- [2mobjects: [[22m
- [2m/* [0] */ [22m[33mfalse[39m[2m,[22m
- [31m ^ Expected object, received boolean[39m
- [2m/* [1] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [33m ^1 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [33m ^1 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [33m ^1 Required[39m
- [2m},[22m
- [2m/* [2] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [36m ^2 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [36m ^2 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [36m ^2 Required[39m
- [2m},[22m
- [2m/* [3] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [34m ^3 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [34m ^3 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [34m ^3 Required[39m
- [2m},[22m
- [2m/* [4] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [35m ^4 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [35m ^4 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [35m ^4 Required[39m
- [2m},[22m
- [2m/* [5] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [32m ^5 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [32m ^5 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [32m ^5 Required[39m
- [2m},[22m
- [2m/* [6] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [33m ^6 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [33m ^6 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [33m ^6 Required[39m
- [2m},[22m
- [2m/* [7] */ {[22m
- [2ma: [22m[90mundefined[39m[2m,[22m
- [36m ^7 Required *or*[39m
- [2mb: [22m[90mundefined[39m[2m,[22m
- [36m ^7 Required *or*[39m
- [2mc: [22m[90mundefined[39m[2m,[22m
- [36m ^7 Required[39m
- [2m},[22m
- [2m],[22m
- [2m}[22m"
- `);
- });
-
- test("formats tuple union schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({
- tuples: z.array(
- z.union([
- z.tuple([z.string(), z.number()]),
- z.tuple([z.boolean(), z.boolean(), z.boolean()]),
- ])
- ),
- }),
- {
- tuples: [false, { a: 1 }, [], ["2", "3"], [4, 5, 6], [true, 7, false]],
- }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{
- tuples: [
- /* [0] */ false,
- ^ Expected array, received boolean
- /* [1] */ { a: 1 },
- ^ Expected array, received object
- /* [2] */ [],
- ^ Array must contain at least 2 element(s)
- Array must contain at least 3 element(s)
- /* [3] */ [
- ...,
- /* [1] */ '3',
- ^ Expected number, received string
- ],
- /* [4] */ [
- /* [0] */ 4,
- ^1 Expected string, received number
- Expected boolean, received number *or*
- /* [1] */ 5,
- ^1 Expected boolean, received number *or*
- /* [2] */ 6,
- ^1 Expected boolean, received number
- ],
- /* [5] */ [
- /* [0] */ true,
- ^2 Expected string, received boolean *or*
- /* [1] */ 7,
- ^2 Expected boolean, received number
- ...,
- ],
- ],
- }"
- `);
- });
-
- test("formats custom message schema", ({ expect }) => {
- const formatted = formatZodErrorForTest(
- z.object({
- a: z.custom(() => false, {
- message: "Custom message\nwith multiple\nlines",
- }),
- }),
- { a: Symbol("kOoh") }
- );
- expect(formatted).toMatchInlineSnapshot(`
- "{
- a: Symbol(kOoh),
- ^ Custom message
- with multiple
- lines
- }"
- `);
- });
-});
diff --git a/packages/miniflare/turbo.json b/packages/miniflare/turbo.json
index 2da61e9c5b0..8f6e6089f2c 100644
--- a/packages/miniflare/turbo.json
+++ b/packages/miniflare/turbo.json
@@ -4,7 +4,7 @@
"tasks": {
"build": {
"inputs": ["$TURBO_DEFAULT$", "!test/**"],
- "outputs": ["dist/**", "bootstrap.js", "worker-metafiles/**"],
+ "outputs": ["dist/**", "worker-metafiles/**"],
"env": ["CI_OS", "SPARROW_SOURCE_KEY"]
},
"test:ci": {
diff --git a/packages/vite-plugin-cloudflare/playground/bindings/__tests__/worker.spec.ts b/packages/vite-plugin-cloudflare/playground/bindings/__tests__/worker.spec.ts
index 3615cdba070..266afd06483 100644
--- a/packages/vite-plugin-cloudflare/playground/bindings/__tests__/worker.spec.ts
+++ b/packages/vite-plugin-cloudflare/playground/bindings/__tests__/worker.spec.ts
@@ -2,7 +2,9 @@ import { test } from "vitest";
import { getResponse, getTextResponse } from "../../__test-utils__";
test("serves Local Explorer UI", async ({ expect }) => {
- const response = await getResponse("/cdn-cgi/explorer");
+ let response = await getResponse("/cdn-cgi/local/explorer");
+ expect(response.status()).toBe(200);
+ response = await getResponse("/cdn-cgi/explorer");
expect(response.status()).toBe(200);
});
diff --git a/packages/vite-plugin-cloudflare/playground/cron-triggers/__tests__/cron-triggers.spec.ts b/packages/vite-plugin-cloudflare/playground/cron-triggers/__tests__/cron-triggers.spec.ts
index 5b940cd0e13..83aee80d067 100644
--- a/packages/vite-plugin-cloudflare/playground/cron-triggers/__tests__/cron-triggers.spec.ts
+++ b/packages/vite-plugin-cloudflare/playground/cron-triggers/__tests__/cron-triggers.spec.ts
@@ -1,10 +1,13 @@
-import { test } from "vitest";
+import { describe, test } from "vitest";
import { getTextResponse, serverLogs } from "../../__test-utils__";
-test("Supports testing Cron Triggers at '/cdn-cgi/handler/scheduled' route", async ({
- expect,
-}) => {
- const cronResponse = await getTextResponse("/cdn-cgi/handler/scheduled");
- expect(cronResponse).toBe("ok");
- expect(serverLogs.info.join()).toContain("Cron processed");
-});
+describe.each(["/cdn-cgi/local/scheduled", "/cdn-cgi/handler/scheduled"])(
+ "%s",
+ (path) => {
+ test("Supports testing Cron Triggers", async ({ expect }) => {
+ const cronResponse = await getTextResponse(path);
+ expect(cronResponse).toBe("ok");
+ expect(serverLogs.info.join()).toContain("Cron processed");
+ });
+ }
+);
diff --git a/packages/vite-plugin-cloudflare/playground/email-worker/__tests__/email-triggers.spec.ts b/packages/vite-plugin-cloudflare/playground/email-worker/__tests__/email-triggers.spec.ts
index 76600cffb55..637ab8e5dac 100644
--- a/packages/vite-plugin-cloudflare/playground/email-worker/__tests__/email-triggers.spec.ts
+++ b/packages/vite-plugin-cloudflare/playground/email-worker/__tests__/email-triggers.spec.ts
@@ -1,7 +1,7 @@
import { readFileSync } from "node:fs";
-import path from "node:path";
+import { join } from "node:path";
import dedent from "ts-dedent";
-import { test, vi } from "vitest";
+import { describe, test, vi } from "vitest";
import {
getTextResponse,
rootDir,
@@ -16,41 +16,43 @@ test("Supports sending email via the email binding", async ({ expect }) => {
expect(sendEmailResponse).toBe("Email message sent successfully!");
});
-test("Supports testing Email Workers at '/cdn-cgi/handler/scheduled' route", async ({
- expect,
-}) => {
- const params = new URLSearchParams();
- params.append("from", "sender@example.com");
- params.append("to", "recipient@example.com");
+// The canonical path is `/cdn-cgi/local/email`; `/cdn-cgi/handler/email` is the
+// legacy path kept working via a rewrite in the trigger-handlers plugin.
+describe.each(["/cdn-cgi/local/email", "/cdn-cgi/handler/email"])(
+ "%s",
+ (path) => {
+ test("Supports testing Email Workers", async ({ expect }) => {
+ const params = new URLSearchParams();
+ params.append("from", "sender@example.com");
+ params.append("to", "recipient@example.com");
- const fetchResponse = await fetch(
- `${viteTestUrl}/cdn-cgi/handler/email?${params}`,
- {
- method: "POST",
- body: dedent`
- From: "John"
- Reply-To: sender@example.com
- To: recipient@example.com
- Subject: Testing Email Workers Local Dev
- Content-Type: text/html; charset="windows-1252"
- X-Mailer: Curl
- Date: Tue, 27 Aug 2024 08:49:44 -0700
- Message-ID: <6114391943504294873000@ZSH-GHOSTTY>
+ const fetchResponse = await fetch(`${viteTestUrl}${path}?${params}`, {
+ method: "POST",
+ body: dedent`
+ From: "John"
+ Reply-To: sender@example.com
+ To: recipient@example.com
+ Subject: Testing Email Workers Local Dev
+ Content-Type: text/html; charset="windows-1252"
+ X-Mailer: Curl
+ Date: Tue, 27 Aug 2024 08:49:44 -0700
+ Message-ID: <6114391943504294873000@ZSH-GHOSTTY>
- Hi there
- `,
- }
- );
+ Hi there
+ `,
+ });
- const emailStdout = serverLogs.info.join();
- expect(await fetchResponse.text()).toBe(
- "Worker successfully processed email"
- );
- expect(emailStdout).toContain(
- `Received email from sender@example.com on ${new Date(" 27 Aug 2024 08:49:44 -0700").toISOString()} with following message:`
- );
- expect(emailStdout).toContain("Hi there");
-});
+ const emailStdout = serverLogs.info.join();
+ expect(await fetchResponse.text()).toBe(
+ "Worker successfully processed email"
+ );
+ expect(emailStdout).toContain(
+ `Received email from sender@example.com on ${new Date(" 27 Aug 2024 08:49:44 -0700").toISOString()} with following message:`
+ );
+ expect(emailStdout).toContain("Hi there");
+ });
+ }
+);
test("logs sent emails to a directory within the project directory", async ({
expect,
@@ -70,9 +72,7 @@ test("logs sent emails to a directory within the project directory", async ({
return emailPath as string;
}, WAIT_FOR_OPTIONS);
- const projectEmailDir = slash(
- path.join(rootDir, ".wrangler", "tmp", "email")
- );
+ const projectEmailDir = slash(join(rootDir, ".wrangler", "tmp", "email"));
expect(slash(loggedPath).startsWith(projectEmailDir)).toBe(true);
const fileContents = readFileSync(loggedPath, "utf-8");
diff --git a/packages/vite-plugin-cloudflare/playground/stream-binding/__tests__/worker.spec.ts b/packages/vite-plugin-cloudflare/playground/stream-binding/__tests__/worker.spec.ts
index 2c851f27850..e8e86e3c9a0 100644
--- a/packages/vite-plugin-cloudflare/playground/stream-binding/__tests__/worker.spec.ts
+++ b/packages/vite-plugin-cloudflare/playground/stream-binding/__tests__/worker.spec.ts
@@ -8,7 +8,7 @@ test("stream upload returns a valid preview URL", async ({ expect }) => {
id: string;
};
expect(result.id).toBeTruthy();
- expect(result.preview).toContain("/cdn-cgi/mf/stream/");
+ expect(result.preview).toContain("/__cf_local/stream/");
expect(result.preview).toContain("/watch");
});
diff --git a/packages/vite-plugin-cloudflare/src/__tests__/shortcuts.spec.ts b/packages/vite-plugin-cloudflare/src/__tests__/shortcuts.spec.ts
index 55480575eee..63a50f269ea 100644
--- a/packages/vite-plugin-cloudflare/src/__tests__/shortcuts.spec.ts
+++ b/packages/vite-plugin-cloudflare/src/__tests__/shortcuts.spec.ts
@@ -264,7 +264,9 @@ describe.skipIf(!satisfiesMinimumViteVersion("7.2.7"))("shortcuts", () => {
await explorerShortcut?.action?.(mockServer);
expect(mockOpen).toHaveBeenCalledWith(
- expect.stringMatching(/^http:\/\/localhost:\d+\/cdn-cgi\/explorer$/)
+ expect.stringMatching(
+ /^http:\/\/localhost:\d+\/cdn-cgi\/local\/explorer$/
+ )
);
});
diff --git a/packages/vite-plugin-cloudflare/src/miniflare-options.ts b/packages/vite-plugin-cloudflare/src/miniflare-options.ts
index 5b9eef7644a..44b98d44c65 100644
--- a/packages/vite-plugin-cloudflare/src/miniflare-options.ts
+++ b/packages/vite-plugin-cloudflare/src/miniflare-options.ts
@@ -251,6 +251,7 @@ export async function getDevMiniflareOptions(
];
const containerTagToOptionsMap: ContainerTagToOptionsMap = new Map();
+ let containerEngine: string | undefined;
const workersFromConfig =
resolvedPluginConfig.type === "workers"
@@ -293,8 +294,7 @@ export async function getDevMiniflareOptions(
worker.config.dev.enable_containers
) {
const dockerPath = getDockerPath();
- worker.config.dev.container_engine =
- resolveDockerHost(dockerPath);
+ containerEngine = resolveDockerHost(dockerPath);
containerBuildId = generateContainerBuildId();
const options = getContainerOptions({
@@ -474,14 +474,12 @@ export async function getDevMiniflareOptions(
unsafeLocalExplorer: getLocalExplorerEnabledFromEnv(),
telemetry: { enabled: false },
handleStructuredLogs: getStructuredLogsLogger(logger),
- defaultPersistRoot: getPersistenceRoot(
+ resourcePersistencePath: getPersistenceRoot(
resolvedViteConfig.root,
resolvedPluginConfig.persistState
),
- defaultProjectTmpPath: path.resolve(
- resolvedViteConfig.root,
- ".wrangler/tmp"
- ),
+ resourceTmpPath: path.resolve(resolvedViteConfig.root, ".wrangler/tmp"),
+ containerEngine,
workers: [...assetWorkers, ...externalWorkers, ...userWorkers],
async unsafeModuleFallbackService(request) {
const parsed = await parseModuleFallbackRequest(request);
@@ -648,6 +646,7 @@ export async function getPreviewMiniflareOptions(
);
const { resolvedPluginConfig, resolvedViteConfig } = ctx;
const containerTagToOptionsMap: ContainerTagToOptionsMap = new Map();
+ let containerEngine: string | undefined;
const workers: Array = (
await Promise.all(
@@ -688,7 +687,7 @@ export async function getPreviewMiniflareOptions(
workerConfig.dev.enable_containers
) {
const dockerPath = getDockerPath();
- workerConfig.dev.container_engine = resolveDockerHost(dockerPath);
+ containerEngine = resolveDockerHost(dockerPath);
containerBuildId = generateContainerBuildId();
const options = getContainerOptions({
@@ -758,14 +757,12 @@ export async function getPreviewMiniflareOptions(
unsafeLocalExplorer: getLocalExplorerEnabledFromEnv(),
telemetry: { enabled: false },
handleStructuredLogs: getStructuredLogsLogger(logger),
- defaultPersistRoot: getPersistenceRoot(
+ resourcePersistencePath: getPersistenceRoot(
resolvedViteConfig.root,
resolvedPluginConfig.persistState
),
- defaultProjectTmpPath: path.resolve(
- resolvedViteConfig.root,
- ".wrangler/tmp"
- ),
+ resourceTmpPath: path.resolve(resolvedViteConfig.root, ".wrangler/tmp"),
+ containerEngine,
workers,
},
containerTagToOptionsMap,
diff --git a/packages/vite-plugin-cloudflare/src/plugins/preview.ts b/packages/vite-plugin-cloudflare/src/plugins/preview.ts
index 1617b295de7..4ef4e1d7709 100644
--- a/packages/vite-plugin-cloudflare/src/plugins/preview.ts
+++ b/packages/vite-plugin-cloudflare/src/plugins/preview.ts
@@ -5,13 +5,14 @@ import {
} from "@cloudflare/containers-shared";
import { cleanupContainers } from "@cloudflare/containers-shared/src/utils";
import { UserError } from "@cloudflare/workers-utils";
-import { buildPublicUrl } from "miniflare";
+import { buildPublicUrl, Request as MiniflareRequest } from "miniflare";
import colors from "picocolors";
import { getDockerPath } from "../containers";
import { assertIsPreview } from "../context";
import { getPreviewMiniflareOptions } from "../miniflare-options";
import { createPlugin, createRequestHandler } from "../utils";
import { handleWebSocket } from "../websockets";
+import { rewriteLegacyMiniflarePath } from "./trigger-handlers";
let exitCallback = () => {};
@@ -121,6 +122,12 @@ export const previewPlugin = createPlugin("preview", (ctx) => {
// In preview mode we put our middleware at the front of the chain so that all assets are handled in Miniflare
vitePreviewServer.middlewares.use(
createRequestHandler((request) => {
+ const url = new URL(request.url);
+ const rewritten = rewriteLegacyMiniflarePath(url.pathname);
+ if (rewritten !== url.pathname) {
+ url.pathname = rewritten;
+ request = new MiniflareRequest(url, request);
+ }
return ctx.miniflare.dispatchFetch(request, { redirect: "manual" });
})
);
diff --git a/packages/vite-plugin-cloudflare/src/plugins/trigger-handlers.ts b/packages/vite-plugin-cloudflare/src/plugins/trigger-handlers.ts
index 961f1c28cdf..ae1bbb44bd5 100644
--- a/packages/vite-plugin-cloudflare/src/plugins/trigger-handlers.ts
+++ b/packages/vite-plugin-cloudflare/src/plugins/trigger-handlers.ts
@@ -1,8 +1,31 @@
-import { CoreHeaders } from "miniflare";
+import { CoreHeaders, Request as MiniflareRequest } from "miniflare";
import { createPlugin, createRequestHandler } from "../utils";
+// Miniflare v5 moved its internal endpoints under `/cdn-cgi/local/` (and
+// `/__cf_local/` for endpoints that must remain reachable over tunnels). These
+// map the pre-v5 paths onto their current equivalents. This must stay in sync
+// with `rewriteLegacyMiniflarePath()` in Wrangler's ProxyWorker.
+const LEGACY_PATH_REWRITES: readonly [string, string][] = [
+ ["/cdn-cgi/handler", "/cdn-cgi/local"],
+ ["/cdn-cgi/mf/scheduled", "/cdn-cgi/local/scheduled"],
+ ["/cdn-cgi/mf/stream", "/__cf_local/stream"],
+ ["/cdn-cgi/mf/imagedelivery", "/__cf_local/imagedelivery"],
+ ["/cdn-cgi/explorer", "/cdn-cgi/local/explorer"],
+];
+
+export function rewriteLegacyMiniflarePath(pathname: string): string {
+ for (const [oldPrefix, newPrefix] of LEGACY_PATH_REWRITES) {
+ if (pathname === oldPrefix || pathname.startsWith(`${oldPrefix}/`)) {
+ return newPrefix + pathname.slice(oldPrefix.length);
+ }
+ }
+ return pathname;
+}
+
/**
- * Plugin to forward `/cdn-cgi/handler/*` routes to trigger handlers in development
+ * Plugin to forward trigger handler routes (scheduled, email) and other
+ * internal Miniflare endpoints to Miniflare in development, including
+ * backwards-compatible rewrites for pre-v5 paths.
*/
export const triggerHandlersPlugin = createPlugin("trigger-handlers", (ctx) => {
return {
@@ -15,14 +38,32 @@ export const triggerHandlersPlugin = createPlugin("trigger-handlers", (ctx) => {
}
const entryWorkerName = entryWorkerConfig.name;
- const requestHandler = createRequestHandler((request) => {
+
+ function dispatch(request: MiniflareRequest) {
request.headers.set(CoreHeaders.ROUTE_OVERRIDE, entryWorkerName);
return ctx.miniflare.dispatchFetch(request, {
redirect: "manual",
});
- });
+ }
+
+ // Canonical paths: forward directly to Miniflare.
+ viteDevServer.middlewares.use(
+ "/cdn-cgi/local/",
+ createRequestHandler((request) => dispatch(request))
+ );
- viteDevServer.middlewares.use("/cdn-cgi/handler/", requestHandler);
+ // Backwards compatibility: rewrite legacy paths onto their canonical
+ // equivalents before dispatching.
+ for (const [oldPrefix, newPrefix] of LEGACY_PATH_REWRITES) {
+ viteDevServer.middlewares.use(
+ oldPrefix,
+ createRequestHandler((request) => {
+ const url = new URL(request.url);
+ url.pathname = newPrefix + url.pathname.slice(oldPrefix.length);
+ return dispatch(new MiniflareRequest(url, request));
+ })
+ );
+ }
},
};
});
diff --git a/packages/vitest-pool-workers/package.json b/packages/vitest-pool-workers/package.json
index 20b5c2aa6fe..2d60c593c1b 100644
--- a/packages/vitest-pool-workers/package.json
+++ b/packages/vitest-pool-workers/package.json
@@ -57,7 +57,7 @@
"esbuild": "catalog:default",
"miniflare": "workspace:*",
"wrangler": "workspace:*",
- "zod": "3.25.76"
+ "zod": "catalog:default"
},
"devDependencies": {
"@cloudflare/mock-npm-registry": "workspace:*",
diff --git a/packages/vitest-pool-workers/src/pool/config.ts b/packages/vitest-pool-workers/src/pool/config.ts
index a6ee42ca974..1b9e70a3b19 100644
--- a/packages/vitest-pool-workers/src/pool/config.ts
+++ b/packages/vitest-pool-workers/src/pool/config.ts
@@ -1,6 +1,6 @@
import path from "node:path";
+import { formatZodError } from "@cloudflare/workers-utils";
import {
- formatZodError,
getRootPath,
Log,
LogLevel,
@@ -17,7 +17,7 @@ import {
import type { ModuleRule, WorkerOptions } from "miniflare";
import type { TestProject } from "vitest/node";
import type { Binding, RemoteProxySession } from "wrangler";
-import type { ParseParams, ZodError } from "zod";
+import type { ZodError } from "zod";
export interface WorkersConfigPluginAPI {
setMain(newMain?: string): void;
@@ -35,7 +35,7 @@ const WorkersPoolOptionsSchema = z.object({
* `module` instance as is used internally for the `SELF` and Durable Object
* bindings.
*/
- main: z.ostring(),
+ main: z.string().optional(),
/**
* Enables remote bindings to access remote resources configured
* with `remote: true` in the wrangler configuration file.
@@ -60,18 +60,25 @@ const WorkersPoolOptionsSchema = z.object({
)
.default({}),
miniflare: z
- .object({
- workers: z.array(z.object({}).passthrough()).optional(),
+ .looseObject({
+ workers: z.array(z.looseObject({})).optional(),
})
- .passthrough()
.optional(),
wrangler: z
- .object({ configPath: z.ostring(), environment: z.ostring() })
+ .object({
+ configPath: z.string().optional(),
+ environment: z.string().optional(),
+ })
.optional(),
});
+type CompatibleWorkerOptions = WorkerOptions & {
+ /** @deprecated Use `cacheAPI` instead. */
+ cache?: WorkerOptions["cacheAPI"];
+};
+
export type SourcelessWorkerOptions = Omit<
- WorkerOptions,
+ CompatibleWorkerOptions,
"script" | "scriptPath" | "modules" | "modulesRoot"
> & {
// `modulesRules` is not included in all members of the `SourceOptions` type
@@ -81,7 +88,7 @@ export type SourcelessWorkerOptions = Omit<
export type WorkersPoolOptions = z.input & {
miniflare?: SourcelessWorkerOptions & {
- workers?: WorkerOptions[];
+ workers?: CompatibleWorkerOptions[];
};
};
@@ -89,7 +96,14 @@ export type WorkersPoolOptionsWithDefines = WorkersPoolOptions & {
defines?: Record;
};
-type PathParseParams = Pick;
+type PathParseParams = { path?: (string | number)[] };
+
+function normalizeMiniflareWorkerOptions(value: Record): void {
+ if (value.cacheAPI === undefined) {
+ value.cacheAPI = value.cache;
+ }
+ delete value.cache;
+}
function isZodErrorLike(value: unknown): value is ZodError {
return (
@@ -118,6 +132,8 @@ function parseWorkerOptions(
withoutScript: boolean,
opts: PathParseParams
): WorkerOptions {
+ normalizeMiniflareWorkerOptions(value);
+
// If this worker shouldn't have a configurable script, remove all script data
// and replace it with an empty `script` that will pass validation
if (withoutScript) {
diff --git a/packages/vitest-pool-workers/src/pool/index.ts b/packages/vitest-pool-workers/src/pool/index.ts
index 26edf2485d8..7d34883a574 100644
--- a/packages/vitest-pool-workers/src/pool/index.ts
+++ b/packages/vitest-pool-workers/src/pool/index.ts
@@ -626,7 +626,6 @@ const SHARED_MINIFLARE_OPTIONS: SharedOptions = {
log: mfLog,
verbose: true,
handleStructuredLogs,
- unsafeStickyBlobs: true,
} satisfies Partial;
const DEFAULT_INSPECTOR_PORT = 9229;
diff --git a/packages/vitest-pool-workers/test/validation.test.ts b/packages/vitest-pool-workers/test/validation.test.ts
index f43fdea5e7c..3d3e1c19655 100644
--- a/packages/vitest-pool-workers/test/validation.test.ts
+++ b/packages/vitest-pool-workers/test/validation.test.ts
@@ -23,9 +23,9 @@ test(
TypeError: Unexpected options in project ${path.join(tmpPathName, "vitest.config.mts")}:
{
miniflare: [],
- ^ Expected object, received array
+ ^ Invalid input: expected object, received array
wrangler: './wrangler.toml',
- ^ Expected object, received string
+ ^ Invalid input: expected object, received string
}
`;
expect(result.stderr).toMatch(expected);
@@ -46,7 +46,7 @@ test(
{
miniflare: {
compatibilityDate: { year: 2024, month: 1, day: 1 },
- ^ Expected string, received object
+ ^ Invalid input: expected string, received object
},
}
`;
@@ -54,6 +54,73 @@ test(
}
);
+test(
+ "normalizes the deprecated cache option",
+ { timeout: 45_000 },
+ async ({ expect, seed, vitestRun }) => {
+ await seed({
+ "vitest.config.mts": vitestConfig({
+ miniflare: {
+ cache: false,
+ compatibilityDate: "2025-12-02",
+ compatibilityFlags: ["nodejs_compat"],
+ },
+ }),
+ "index.test.ts": dedent /* javascript */ `
+ import { it } from "vitest";
+
+ it("disables the cache", async ({ expect }) => {
+ const key = "https://example.com/cache";
+ await caches.default.put(
+ key,
+ new Response("cached", {
+ headers: { "Cache-Control": "max-age=3600" },
+ })
+ );
+ expect(await caches.default.match(key)).toBeUndefined();
+ });
+ `,
+ });
+
+ const result = await vitestRun();
+ expect(await result.exitCode, result.stderr).toBe(0);
+ }
+);
+
+test(
+ "gives cacheAPI precedence over the deprecated cache option",
+ { timeout: 45_000 },
+ async ({ expect, seed, vitestRun }) => {
+ await seed({
+ "vitest.config.mts": vitestConfig({
+ miniflare: {
+ cache: true,
+ cacheAPI: false,
+ compatibilityDate: "2025-12-02",
+ compatibilityFlags: ["nodejs_compat"],
+ },
+ }),
+ "index.test.ts": dedent /* javascript */ `
+ import { it } from "vitest";
+
+ it("disables the cache", async ({ expect }) => {
+ const key = "https://example.com/cache";
+ await caches.default.put(
+ key,
+ new Response("cached", {
+ headers: { "Cache-Control": "max-age=3600" },
+ })
+ );
+ expect(await caches.default.match(key)).toBeUndefined();
+ });
+ `,
+ });
+
+ const result = await vitestRun();
+ expect(await result.exitCode, result.stderr).toBe(0);
+ }
+);
+
test(
"requires modules entrypoint to use SELF",
{ timeout: 45_000 },
diff --git a/packages/workers-shared/package.json b/packages/workers-shared/package.json
index 0f1e837fdc0..388ea7423bb 100644
--- a/packages/workers-shared/package.json
+++ b/packages/workers-shared/package.json
@@ -52,7 +52,7 @@
"toucan-js": "4.0.0",
"typescript": "catalog:default",
"vitest": "catalog:default",
- "zod": "^3.25.76"
+ "zod": "catalog:default"
},
"engines": {
"node": ">=22.0.0"
diff --git a/packages/workers-shared/utils/types.ts b/packages/workers-shared/utils/types.ts
index 1cd4374b3c1..acb70865e03 100644
--- a/packages/workers-shared/utils/types.ts
+++ b/packages/workers-shared/utils/types.ts
@@ -38,17 +38,20 @@ const MetadataRedirectEntry = z.object({
to: z.string(),
});
-const MetadataStaticRedirects = z.record(MetadataStaticRedirectEntry);
+const MetadataStaticRedirects = z.record(
+ z.string(),
+ MetadataStaticRedirectEntry
+);
export type MetadataStaticRedirects = z.infer;
-const MetadataRedirects = z.record(MetadataRedirectEntry);
+const MetadataRedirects = z.record(z.string(), MetadataRedirectEntry);
export type MetadataRedirects = z.infer;
const MetadataHeaderEntry = z.object({
- set: z.record(z.string()).optional(),
+ set: z.record(z.string(), z.string()).optional(),
unset: z.array(z.string()).optional(),
});
-const MetadataHeaders = z.record(MetadataHeaderEntry);
+const MetadataHeaders = z.record(z.string(), MetadataHeaderEntry);
export type MetadataHeaders = z.infer;
export const RedirectsSchema = z
diff --git a/packages/workers-utils/package.json b/packages/workers-utils/package.json
index d012ef4548b..affa631b333 100644
--- a/packages/workers-utils/package.json
+++ b/packages/workers-utils/package.json
@@ -65,6 +65,7 @@
"concurrently": "^8.2.2",
"empathic": "^2.0.0",
"jsonc-parser": "catalog:default",
+ "kleur": "^4.1.5",
"open": "catalog:default",
"signal-exit": "catalog:default",
"smol-toml": "catalog:default",
@@ -74,7 +75,8 @@
"typescript": "catalog:default",
"update-check": "^1.5.4",
"vitest": "catalog:default",
- "xdg-app-paths": "^8.3.0"
+ "xdg-app-paths": "^8.3.0",
+ "zod": "catalog:default"
},
"peerDependencies": {
"vitest": "^4.1.0"
diff --git a/packages/workers-utils/src/environment-variables/factory.ts b/packages/workers-utils/src/environment-variables/factory.ts
index 03ee9f4566a..bf0a699dc48 100644
--- a/packages/workers-utils/src/environment-variables/factory.ts
+++ b/packages/workers-utils/src/environment-variables/factory.ts
@@ -114,7 +114,7 @@ type VariableNames =
// ## Experimental Feature Flags
- /** Enable the local explorer UI at /cdn-cgi/explorer (experimental, default: false). */
+ /** Enable the local explorer UI at /cdn-cgi/local/explorer (experimental, default: false). */
| "X_LOCAL_EXPLORER"
/** Open the browser in headful (visible) mode when using the Browser Run API in local dev (default: false). */
| "X_BROWSER_HEADFUL"
diff --git a/packages/workers-utils/src/environment-variables/misc-variables.ts b/packages/workers-utils/src/environment-variables/misc-variables.ts
index 5b0d551a490..4b48913f80d 100644
--- a/packages/workers-utils/src/environment-variables/misc-variables.ts
+++ b/packages/workers-utils/src/environment-variables/misc-variables.ts
@@ -345,7 +345,7 @@ export const getOpenNextDeployFromEnv = getEnvironmentVariableFactory({
});
/**
- * `X_LOCAL_EXPLORER` enables the local explorer UI at /cdn-cgi/explorer.
+ * `X_LOCAL_EXPLORER` enables the local explorer UI at /cdn-cgi/local/explorer.
*/
export const getLocalExplorerEnabledFromEnv =
getBooleanEnvironmentVariableFactory({
diff --git a/packages/workers-utils/src/index.ts b/packages/workers-utils/src/index.ts
index 833b0cd0aef..526e0ed381e 100644
--- a/packages/workers-utils/src/index.ts
+++ b/packages/workers-utils/src/index.ts
@@ -171,3 +171,5 @@ export {
getWorkerName,
getWorkerNameFromProject,
} from "./worker-name";
+
+export { _forceColour, formatZodError } from "./zod-format";
diff --git a/packages/miniflare/src/zod-format.ts b/packages/workers-utils/src/zod-format.ts
similarity index 83%
rename from packages/miniflare/src/zod-format.ts
rename to packages/workers-utils/src/zod-format.ts
index 465bedce476..d50c15e41ea 100644
--- a/packages/miniflare/src/zod-format.ts
+++ b/packages/workers-utils/src/zod-format.ts
@@ -1,4 +1,3 @@
-// TODO(someday): publish this as a separate package
// noinspection JSUnusedAssignment
// ^ WebStorm incorrectly thinks some variables might not have been initialised
// before use without this. TypeScript is better at catching these errors. :)
@@ -17,6 +16,15 @@ import {
} from "kleur/colors";
import type { z } from "zod";
+const originalEnabled = $colors.enabled;
+
+// `kleur` is marked as a dev dependency, so will get bundled. We'd still like
+// to be able to control whether it's enabled in tests though. Therefore, export
+// a function that toggles the enabled state of our bundled version.
+export function _forceColour(enabled = originalEnabled) {
+ $colors.enabled = enabled;
+}
+
// This file contains a `formatZodError(error, input)` function for formatting
// a Zod `error` that came from parsing a specific `input`. This works by
// building an "annotated" version of the `input`, with roughly the same shape,
@@ -292,24 +300,38 @@ function isRecord(value: unknown): value is Record {
}
function arrayShallowEqual(a: T[], b: T[]) {
- if (a.length !== b.length) return false;
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
+ if (a.length !== b.length) {
+ return false;
+ }
+ for (let i = 0; i < a.length; i++) {
+ if (a[i] !== b[i]) {
+ return false;
+ }
+ }
return true;
}
-function issueEqual(a: z.ZodIssue, b: z.ZodIssue) {
+function issueEqual(a: z.core.$ZodIssue, b: z.core.$ZodIssue) {
// We consider issues to be equal if their messages and paths are
return a.message === b.message && arrayShallowEqual(a.path, b.path);
}
-function hasMultipleDistinctMessages(issues: z.ZodIssue[], atDepth: number) {
+function hasMultipleDistinctMessages(
+ issues: z.core.$ZodIssue[],
+ atDepth: number
+) {
// Returns true iff `issues` has issues that aren't "the same" at the
// specified depth or below
- let firstIssue: z.ZodIssue | undefined;
+ let firstIssue: z.core.$ZodIssue | undefined;
for (const issue of issues) {
- if (issue.path.length < atDepth) continue;
- if (firstIssue === undefined) firstIssue = issue;
- else if (!issueEqual(firstIssue, issue)) return true;
+ if (issue.path.length < atDepth) {
+ continue;
+ }
+ if (firstIssue === undefined) {
+ firstIssue = issue;
+ } else if (!issueEqual(firstIssue, issue)) {
+ return true;
+ }
}
return false;
}
@@ -318,7 +340,7 @@ function annotate(
groupCounts: GroupCountsMap,
annotated: Annotated,
input: unknown,
- issue: z.ZodIssue,
+ issue: z.core.$ZodIssue,
path: (string | number)[],
groupId?: number
): Annotated {
@@ -327,38 +349,52 @@ function annotate(
// If this is an `invalid_union` error, make sure we include all sub-issues
if (issue.code === "invalid_union") {
- const unionIssues = issue.unionErrors.flatMap(({ issues }) => issues);
-
- // If the `input` is an object/array with multiple distinct messages,
- // annotate it as a group
- let newGroupId: number | undefined;
- const multipleDistinct = hasMultipleDistinctMessages(
- unionIssues,
- // For this check, we only include messages that are deeper than our
- // current level, so we don't include messages we'd ignore if we grouped
- issue.path.length + 1
- );
- if (isRecord(input) && multipleDistinct) {
- newGroupId = groupCounts.size;
- groupCounts.set(newGroupId, 0);
- }
+ // In zod v4, `issue.errors` is `$ZodIssue[][]` where each inner
+ // array holds the issues for one union branch.
+ const allBranchIssues = issue.errors;
+ const unionIssues = allBranchIssues.flat();
+
+ // In zod v4, discriminated unions with an invalid discriminator
+ // produce an `invalid_union` issue with an empty `errors` array
+ // and the error message on the issue itself. Fall through to
+ // treat it as a regular annotation in that case.
+ if (unionIssues.length > 0) {
+ // If the `input` is an object/array with multiple distinct messages,
+ // annotate it as a group
+ // In zod v4, branch issue paths are already relative to the union
+ // issue, so use depth 1 (deeper than current level 0) rather than
+ // issue.path.length + 1.
+ const multipleDistinct = hasMultipleDistinctMessages(unionIssues, 1);
- for (const unionIssue of unionIssues) {
- const unionPath = unionIssue.path.slice(issue.path.length);
- // If we have multiple distinct messages at deeper levels, and this
- // issue is for the current path, skip it, so we don't end up annotating
- // the current path and sub-paths
- if (multipleDistinct && unionPath.length === 0) continue;
- annotated = annotate(
- groupCounts,
- annotated,
- input,
- unionIssue,
- unionPath,
- newGroupId
- );
+ let newGroupId: number | undefined;
+ if (isRecord(input) && multipleDistinct) {
+ newGroupId = groupCounts.size;
+ groupCounts.set(newGroupId, 0);
+ }
+
+ for (const branchIssues of allBranchIssues) {
+ for (const unionIssue of branchIssues) {
+ // In zod v4, branch issue paths are already relative to the
+ // union issue (not absolute), so use them directly.
+ const unionPath = unionIssue.path as (string | number)[];
+ // If we have multiple distinct messages at deeper levels, and this
+ // issue is for the current path, skip it, so we don't end up annotating
+ // the current path and sub-paths
+ if (multipleDistinct && unionPath.length === 0) {
+ continue;
+ }
+ annotated = annotate(
+ groupCounts,
+ annotated,
+ input,
+ unionIssue,
+ unionPath,
+ newGroupId
+ );
+ }
+ }
+ return annotated;
}
- return annotated;
}
const message = issue.message;
@@ -449,7 +485,9 @@ function print(
messagePrefix += annotated[kGroupId] + 1;
const remaining = groupCounts.get(annotated[kGroupId]);
assert(remaining !== undefined);
- if (remaining > 1) groupOr = " *or*";
+ if (remaining > 1) {
+ groupOr = " *or*";
+ }
groupCounts.set(annotated[kGroupId], remaining - 1);
}
messagePrefix += " ";
@@ -514,8 +552,12 @@ export function formatZodError(error: z.ZodError, input: unknown): string {
// annotate the input with an `invalid_type` error instead
const sortedIssues = Array.from(error.issues).sort((a, b) => {
if (a.code !== b.code) {
- if (a.code === "invalid_union") return -1;
- if (b.code === "invalid_union") return 1;
+ if (a.code === "invalid_union") {
+ return -1;
+ }
+ if (b.code === "invalid_union") {
+ return 1;
+ }
}
return 0;
});
@@ -524,7 +566,13 @@ export function formatZodError(error: z.ZodError, input: unknown): string {
let annotated: Annotated;
const groupCounts = new GroupCountsMap();
for (const issue of sortedIssues) {
- annotated = annotate(groupCounts, annotated, input, issue, issue.path);
+ annotated = annotate(
+ groupCounts,
+ annotated,
+ input,
+ issue,
+ issue.path as (string | number)[]
+ );
}
// Print to pretty string
diff --git a/packages/workers-utils/tests/zod-format.test.ts b/packages/workers-utils/tests/zod-format.test.ts
new file mode 100644
index 00000000000..02a15604253
--- /dev/null
+++ b/packages/workers-utils/tests/zod-format.test.ts
@@ -0,0 +1,458 @@
+import assert from "node:assert";
+import { _forceColour, formatZodError } from "@cloudflare/workers-utils";
+import { describe, test } from "vitest";
+import { z } from "zod";
+
+function formatZodErrorForTest(
+ schema: z.ZodType,
+ input: unknown,
+ colour?: boolean
+) {
+ const result = schema.safeParse(input);
+ assert(!result.success);
+ // Disable colours by default for easier-to-read snapshots
+ _forceColour(colour ?? false);
+ return formatZodError(result.error, input);
+}
+
+describe("formatZodError:", () => {
+ test("formats primitive schema with primitive input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(z.number(), false);
+ expect(formatted).toMatchInlineSnapshot(`
+ "false
+ ^ Invalid input: expected number, received boolean"
+ `);
+ });
+ test("formats primitive schema with object input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(z.string(), {
+ a: 1,
+ b: { c: 1 },
+ });
+ expect(formatted).toMatchInlineSnapshot(`
+ "{ a: 1, b: [Object] }
+ ^ Invalid input: expected string, received object"
+ `);
+ });
+
+ test("formats object schema with primitive input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(z.object({ a: z.number() }), true);
+ expect(formatted).toMatchInlineSnapshot(`
+ "true
+ ^ Invalid input: expected object, received boolean"
+ `);
+ });
+ test("formats object schema with object input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({
+ a: z.string(),
+ b: z.number(),
+ c: z.boolean(),
+ d: z.number(),
+ e: z.number(),
+ f: z.boolean(),
+ g: z.boolean(),
+ }),
+ {
+ a: "", // Check skips valid
+ b: "2",
+ c: true, // Check skips valid
+ d: 4, // Check doesn't duplicate `...` when skipping valid
+ e: 5,
+ /*f*/ // Check required options
+ g: "7",
+ }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ ...,
+ b: '2',
+ ^ Invalid input: expected number, received string
+ ...,
+ g: '7',
+ ^ Invalid input: expected boolean, received string
+ f: undefined,
+ ^ Invalid input: expected boolean, received undefined
+ }"
+ `);
+ });
+ test("formats object schema with additional options", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({ a: z.number() }).strict(),
+ { a: 1, b: 2 }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{ a: 1, b: 2 }
+ ^ Unrecognized key: "b""
+ `);
+ });
+
+ test("formats array schema with primitive input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(z.array(z.boolean()), 1);
+ expect(formatted).toMatchInlineSnapshot(`
+ "1
+ ^ Invalid input: expected array, received number"
+ `);
+ });
+ test("formats array schema with array input", ({ expect }) => {
+ const formatted = formatZodErrorForTest(z.array(z.number()), [
+ 1, // Check skips valid
+ 2, // Check doesn't duplicate `...` when skipping valid
+ "3",
+ 4,
+ 5,
+ false,
+ ]);
+ expect(formatted).toMatchInlineSnapshot(`
+ "[
+ ...,
+ /* [2] */ '3',
+ ^ Invalid input: expected number, received string
+ ...,
+ /* [5] */ false,
+ ^ Invalid input: expected number, received boolean
+ ]"
+ `);
+ });
+ test("formats array schema with additional options", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.array(z.number()).max(3),
+ [1, 2, 3, 4, 5]
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "[ 1, 2, 3, 4, 5 ]
+ ^ Too big: expected array to have <=3 items"
+ `);
+ });
+
+ test("formats deeply nested schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({
+ a: z.number(),
+ b: z.object({
+ c: z.string(),
+ d: z.array(z.object({ e: z.boolean() })),
+ f: z.array(z.number()),
+ }),
+ g: z.string(),
+ }),
+ {
+ a: "1",
+ b: {
+ c: 2,
+ d: [{ e: true }, { e: 42 }, false, {}],
+ f: () => {},
+ },
+ }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ a: '1',
+ ^ Invalid input: expected number, received string
+ b: {
+ c: 2,
+ ^ Invalid input: expected string, received number
+ d: [
+ ...,
+ /* [1] */ {
+ e: 42,
+ ^ Invalid input: expected boolean, received number
+ },
+ /* [2] */ false,
+ ^ Invalid input: expected object, received boolean
+ /* [3] */ {
+ e: undefined,
+ ^ Invalid input: expected boolean, received undefined
+ },
+ ],
+ f: [Function: f],
+ ^ Invalid input: expected array, received function
+ },
+ g: undefined,
+ ^ Invalid input: expected string, received undefined
+ }"
+ `);
+ });
+
+ test("formats large actual values", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({
+ a: z.object({
+ b: z.string(),
+ }),
+ }),
+ {
+ a: {
+ // Check indents inspected value at correct depth
+ b: Array.from({ length: 50 }).map((_, i) => i),
+ },
+ }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ a: {
+ b: [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
+ 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21,
+ 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
+ 44, 45, 46, 47, 48, 49
+ ],
+ ^ Invalid input: expected string, received array
+ },
+ }"
+ `);
+ });
+
+ test("formats union schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.union([z.boolean(), z.literal(1)]),
+ "a"
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "'a'
+ ^ Invalid input: expected boolean, received string
+ Invalid input: expected 1"
+ `);
+ });
+
+ const discriminatedUnionSchema = z.discriminatedUnion("type", [
+ z.object({
+ type: z.literal("a"),
+ a: z.number(),
+ }),
+ z.object({
+ type: z.literal("b"),
+ b: z.boolean(),
+ }),
+ ]);
+ test("formats discriminated union schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(discriminatedUnionSchema, {
+ type: "a",
+ a: false,
+ });
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ ...,
+ a: false,
+ ^ Invalid input: expected number, received boolean
+ }"
+ `);
+ });
+ test("formats discriminated union schema with invalid discriminator", ({
+ expect,
+ }) => {
+ const formatted = formatZodErrorForTest(discriminatedUnionSchema, {
+ type: "c",
+ });
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ type: 'c',
+ ^ Invalid discriminator value. Expected 'a' | 'b'
+ }"
+ `);
+ });
+
+ test("formats intersection schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.intersection(z.number(), z.literal(2)),
+ false
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "false
+ ^ Invalid input: expected number, received boolean
+ Invalid input: expected 2"
+ `);
+ });
+
+ const objectUnionSchema = z.object({
+ key: z.string(),
+ objects: z.array(
+ z.union([
+ z.object({ a: z.number() }),
+ z.object({ b: z.boolean() }),
+ z.object({ c: z.string() }),
+ ])
+ ),
+ });
+ test("formats object union schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(objectUnionSchema, {
+ key: false,
+ objects: [false, { a: 1 }, {}, [], { d: "" }],
+ });
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ key: false,
+ ^ Invalid input: expected string, received boolean
+ objects: [
+ /* [0] */ false,
+ ^ Invalid input: expected object, received boolean
+ ...,
+ /* [2] */ {
+ a: undefined,
+ ^1 Invalid input: expected number, received undefined *or*
+ b: undefined,
+ ^1 Invalid input: expected boolean, received undefined *or*
+ c: undefined,
+ ^1 Invalid input: expected string, received undefined
+ },
+ /* [3] */ [],
+ ^ Invalid input: expected object, received array
+ /* [4] */ {
+ ...,
+ a: undefined,
+ ^2 Invalid input: expected number, received undefined *or*
+ b: undefined,
+ ^2 Invalid input: expected boolean, received undefined *or*
+ c: undefined,
+ ^2 Invalid input: expected string, received undefined
+ },
+ ],
+ }"
+ `);
+ });
+ test("formats object union schema in colour", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ objectUnionSchema,
+ {
+ key: false,
+ objects: [false, {}, {}, {}, {}, {}, /* cycle */ {}, {}],
+ },
+ /* colour */ true
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "[2m{[22m
+ [2mkey: [22m[33mfalse[39m[2m,[22m
+ [31m ^ Invalid input: expected string, received boolean[39m
+ [2mobjects: [[22m
+ [2m/* [0] */ [22m[33mfalse[39m[2m,[22m
+ [31m ^ Invalid input: expected object, received boolean[39m
+ [2m/* [1] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [33m ^1 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [33m ^1 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [33m ^1 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [2] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [36m ^2 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [36m ^2 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [36m ^2 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [3] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [34m ^3 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [34m ^3 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [34m ^3 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [4] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [35m ^4 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [35m ^4 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [35m ^4 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [5] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [32m ^5 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [32m ^5 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [32m ^5 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [6] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [33m ^6 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [33m ^6 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [33m ^6 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m/* [7] */ {[22m
+ [2ma: [22m[90mundefined[39m[2m,[22m
+ [36m ^7 Invalid input: expected number, received undefined *or*[39m
+ [2mb: [22m[90mundefined[39m[2m,[22m
+ [36m ^7 Invalid input: expected boolean, received undefined *or*[39m
+ [2mc: [22m[90mundefined[39m[2m,[22m
+ [36m ^7 Invalid input: expected string, received undefined[39m
+ [2m},[22m
+ [2m],[22m
+ [2m}[22m"
+ `);
+ });
+
+ test("formats tuple union schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({
+ tuples: z.array(
+ z.union([
+ z.tuple([z.string(), z.number()]),
+ z.tuple([z.boolean(), z.boolean(), z.boolean()]),
+ ])
+ ),
+ }),
+ {
+ tuples: [false, { a: 1 }, [], ["2", "3"], [4, 5, 6], [true, 7, false]],
+ }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ tuples: [
+ /* [0] */ false,
+ ^ Invalid input: expected tuple, received boolean
+ /* [1] */ { a: 1 },
+ ^ Invalid input: expected tuple, received object
+ /* [2] */ [],
+ ^ Too small: expected array to have >=2 items
+ Too small: expected array to have >=3 items
+ /* [3] */ [
+ ...,
+ /* [1] */ '3',
+ ^ Invalid input: expected number, received string
+ ],
+ /* [4] */ [
+ /* [0] */ 4,
+ ^1 Invalid input: expected string, received number
+ Invalid input: expected boolean, received number *or*
+ /* [1] */ 5,
+ ^1 Invalid input: expected boolean, received number *or*
+ /* [2] */ 6,
+ ^1 Invalid input: expected boolean, received number
+ ],
+ /* [5] */ [
+ /* [0] */ true,
+ ^2 Invalid input: expected string, received boolean *or*
+ /* [1] */ 7,
+ ^2 Invalid input: expected boolean, received number
+ ...,
+ ],
+ ],
+ }"
+ `);
+ });
+
+ test("formats custom message schema", ({ expect }) => {
+ const formatted = formatZodErrorForTest(
+ z.object({
+ a: z.custom(() => false, {
+ message: "Custom message\nwith multiple\nlines",
+ }),
+ }),
+ { a: Symbol("kOoh") }
+ );
+ expect(formatted).toMatchInlineSnapshot(`
+ "{
+ a: Symbol(kOoh),
+ ^ Custom message
+ with multiple
+ lines
+ }"
+ `);
+ });
+});
diff --git a/packages/wrangler/e2e/createTestHarness.test.ts b/packages/wrangler/e2e/createTestHarness.test.ts
index d103a41b89b..ab40daaa608 100644
--- a/packages/wrangler/e2e/createTestHarness.test.ts
+++ b/packages/wrangler/e2e/createTestHarness.test.ts
@@ -1795,8 +1795,8 @@ describe("createTestHarness", () => {
[server] startup - completed
[server] fetch - GET / - started
[server] fetch - GET / - 200
- [server] [scheduled-worker] scheduled - GET /cdn-cgi/handler/scheduled?format=json&cron=*+*+*+*+*&time=1700000100000 - started
- [server] [scheduled-worker] scheduled - GET /cdn-cgi/handler/scheduled?format=json&cron=*+*+*+*+*&time=1700000100000 - 200
+ [server] [scheduled-worker] scheduled - GET /cdn-cgi/local/scheduled?format=json&cron=*+*+*+*+*&time=1700000100000 - started
+ [server] [scheduled-worker] scheduled - GET /cdn-cgi/local/scheduled?format=json&cron=*+*+*+*+*&time=1700000100000 - 200
[server] fetch - GET / - started
[server] fetch - GET / - 200"
`);
diff --git a/packages/wrangler/e2e/dev.test.ts b/packages/wrangler/e2e/dev.test.ts
index 35b1477a3b6..e107b0e8230 100644
--- a/packages/wrangler/e2e/dev.test.ts
+++ b/packages/wrangler/e2e/dev.test.ts
@@ -300,7 +300,7 @@ describe.each([
"Scheduled Workers are not automatically triggered"
);
expect(worker.currentOutput).toContain(
- `curl "http://${hostname}:${port}/cdn-cgi/handler/scheduled"`
+ `curl "http://${hostname}:${port}/cdn-cgi/local/scheduled"`
);
expect(worker.currentOutput).not.toContain("undefined");
});
@@ -2395,7 +2395,7 @@ This is a random email body.
const { url } = await worker.waitForReady();
const response = await fetch(
- `${url}/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com`,
+ `${url}/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com`,
{
body: dedent`
From: someone
@@ -2442,15 +2442,20 @@ This is a random email body.
`);
});
- it("should print reject with reason", async ({ expect }) => {
- const helper = new WranglerE2ETestHelper();
- await helper.seed({
- "wrangler.toml": dedent`
+ // The canonical path is `/cdn-cgi/local/email`; `/cdn-cgi/handler/email` is
+ // the legacy path kept working via a rewrite in the dev proxy.
+ describe.each(["/cdn-cgi/local/email", "/cdn-cgi/handler/email"])(
+ "%s",
+ (path) => {
+ it("should print reject with reason", async ({ expect }) => {
+ const helper = new WranglerE2ETestHelper();
+ await helper.seed({
+ "wrangler.toml": dedent`
name = "${workerName}"
main = "src/index.ts"
compatibility_date = "2025-03-17"
`,
- "src/index.ts": dedent`
+ "src/index.ts": dedent`
import { EmailMessage } from "cloudflare:email";
export default {
@@ -2458,16 +2463,16 @@ This is a random email body.
await emailMessage.setReject('I dont like this email')
}
}`,
- });
+ });
- const worker = helper.runLongLived("wrangler dev");
+ const worker = helper.runLongLived("wrangler dev");
- const { url } = await worker.waitForReady();
+ const { url } = await worker.waitForReady();
- const response = await fetch(
- `${url}/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com`,
- {
- body: `From: someone
+ const response = await fetch(
+ `${url}${path}?from=someone@example.com&to=someone-else@example.com`,
+ {
+ body: `From: someone
To: someone else
MIME-Version: 1.0
Message-ID:
@@ -2475,16 +2480,18 @@ Content-Type: text/plain
This is a random email body.
`,
- method: "POST",
- }
- );
+ method: "POST",
+ }
+ );
- expect(await response.text()).toMatchInlineSnapshot(
- `"Worker rejected email with the following reason: I dont like this email"`
- );
+ expect(await response.text()).toMatchInlineSnapshot(
+ `"Worker rejected email with the following reason: I dont like this email"`
+ );
- expect(response.status).toBe(400);
- });
+ expect(response.status).toBe(400);
+ });
+ }
+ );
it("should print forward email", async ({ expect }) => {
const helper = new WranglerE2ETestHelper();
@@ -2509,7 +2516,7 @@ This is a random email body.
const { url } = await worker.waitForReady();
const response = await fetch(
- `${url}/cdn-cgi/handler/email?from=someone@example.com&to=someone-else@example.com`,
+ `${url}/cdn-cgi/local/email?from=someone@example.com&to=someone-else@example.com`,
{
body: `From: someone
To: someone else
diff --git a/packages/wrangler/e2e/multiworker-dev.test.ts b/packages/wrangler/e2e/multiworker-dev.test.ts
index 4a44c137749..e3acb5aee1a 100644
--- a/packages/wrangler/e2e/multiworker-dev.test.ts
+++ b/packages/wrangler/e2e/multiworker-dev.test.ts
@@ -643,7 +643,7 @@ describe("multiworker", () => {
"Scheduled Workers are not automatically triggered"
);
expect(worker.currentOutput).toContain(
- `curl "http://${hostname}:${port}/cdn-cgi/handler/scheduled"`
+ `curl "http://${hostname}:${port}/cdn-cgi/local/scheduled"`
);
expect(worker.currentOutput).not.toContain("undefined");
});
diff --git a/packages/wrangler/src/__tests__/workflows.test.ts b/packages/wrangler/src/__tests__/workflows.test.ts
index 148dbc75c58..fa5b27b085f 100644
--- a/packages/wrangler/src/__tests__/workflows.test.ts
+++ b/packages/wrangler/src/__tests__/workflows.test.ts
@@ -1364,12 +1364,12 @@ describe("wrangler workflows", () => {
});
// =========================================================================
- // Local commands (--local) — hitting /cdn-cgi/explorer/api/workflows/...
+ // Local commands (--local) — hitting /cdn-cgi/local/explorer/api/workflows/...
// =========================================================================
describe("local", () => {
const LOCAL_PORT = 8787;
- const LOCAL_BASE = `http://localhost:${LOCAL_PORT}/cdn-cgi/explorer/api`;
+ const LOCAL_BASE = `http://localhost:${LOCAL_PORT}/cdn-cgi/local/explorer/api`;
describe("workflows list --local", () => {
it("should list workflows from local dev session", async ({ expect }) => {
diff --git a/packages/wrangler/src/api/integrations/platform/index.ts b/packages/wrangler/src/api/integrations/platform/index.ts
index cb35d7e6551..522193935c9 100644
--- a/packages/wrangler/src/api/integrations/platform/index.ts
+++ b/packages/wrangler/src/api/integrations/platform/index.ts
@@ -1,8 +1,6 @@
import path from "node:path";
-import { resolveDockerHost } from "@cloudflare/containers-shared";
import { extractBindingsOfType } from "@cloudflare/deploy-helpers";
import {
- getDockerPath,
getRegistryPath,
getTodaysCompatDate,
} from "@cloudflare/workers-utils";
@@ -321,11 +319,11 @@ async function getMiniflareOptionsFromConfig(args: {
? buildAssetOptions({ assets: processedAssetOptions })
: {};
- const defaultPersistRoot = getMiniflarePersistRoot(options.persist);
+ const resourcePersistencePath = getMiniflarePersistRoot(options.persist);
const projectRoot = config.userConfigPath
? path.dirname(config.userConfigPath)
: process.cwd();
- const defaultProjectTmpPath = getDefaultProjectTmpPath(projectRoot);
+ const resourceTmpPath = getDefaultProjectTmpPath(projectRoot);
const miniflareOptions: MiniflareOptions = {
workers: [
@@ -339,8 +337,8 @@ async function getMiniflareOptionsFromConfig(args: {
},
...externalWorkers,
],
- defaultPersistRoot,
- defaultProjectTmpPath,
+ resourcePersistencePath,
+ resourceTmpPath,
};
return {
@@ -502,15 +500,10 @@ export function unstable_getMiniflareWorkerOptions(
? buildAssetOptions({ assets: processedAssetOptions })
: {};
- const useContainers =
- config.dev?.enable_containers && config.containers?.length;
const workerOptions: SourcelessWorkerOptions = {
compatibilityDate: config.compatibility_date,
compatibilityFlags: config.compatibility_flags,
modulesRules,
- containerEngine: useContainers
- ? (config.dev.container_engine ?? resolveDockerHost(getDockerPath()))
- : undefined,
zone: getZoneFromConfig(config),
...bindingOptions,
diff --git a/packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts b/packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts
index 9997e287cb1..4e1c4bd18ef 100644
--- a/packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts
+++ b/packages/wrangler/src/api/startDevWorker/LocalRuntimeController.ts
@@ -194,14 +194,11 @@ export async function convertToConfigBundle(
inspectorHost: event.config.dev.inspector?.hostname,
}),
localPersistencePath: event.config.dev.persist,
- liveReload: event.config.dev?.liveReload ?? false,
crons,
routes: event.config.dev.routeRequestsByRoutes ? routes : undefined,
queueConsumers,
outboundService: event.config.dev.outboundService,
localProtocol: event.config.dev?.server?.secure ? "https" : "http",
- httpsCertPath: event.config.dev?.server?.httpsCertPath,
- httpsKeyPath: event.config.dev?.server?.httpsKeyPath,
localUpstream: event.config.dev?.origin?.hostname,
upstreamProtocol: event.config.dev?.origin?.secure ? "https" : "http",
testScheduled: !!event.config.dev.testScheduled,
@@ -408,7 +405,6 @@ export class LocalRuntimeController extends RuntimeController {
});
}
);
- options.liveReload = false; // TODO: set in buildMiniflareOptions once old code path is removed
options.handleUncaughtError = this.dispatchRuntimeError;
// Bail out if a newer bundle arrived while we were building
diff --git a/packages/wrangler/src/api/startDevWorker/ProxyController.ts b/packages/wrangler/src/api/startDevWorker/ProxyController.ts
index 991c6b1657f..a5fee81f145 100644
--- a/packages/wrangler/src/api/startDevWorker/ProxyController.ts
+++ b/packages/wrangler/src/api/startDevWorker/ProxyController.ts
@@ -113,7 +113,7 @@ export class ProxyController extends Controller {
},
// no need to use file-system, so don't
- cache: false,
+ cacheAPI: false,
unsafeEphemeralDurableObjects: true,
},
],
@@ -133,7 +133,6 @@ export class ProxyController extends Controller {
this.localServerReady.promise
),
handleStructuredLogs,
- liveReload: false,
};
if (this.inspectorEnabled) {
@@ -173,7 +172,7 @@ export class ProxyController extends Controller {
},
],
// no need to use file-system, so don't
- cache: false,
+ cacheAPI: false,
unsafeEphemeralDurableObjects: true,
});
}
diff --git a/packages/wrangler/src/api/test-harness.ts b/packages/wrangler/src/api/test-harness.ts
index 5e1c0f2cd22..bccc2721218 100644
--- a/packages/wrangler/src/api/test-harness.ts
+++ b/packages/wrangler/src/api/test-harness.ts
@@ -989,7 +989,7 @@ export function createTestHarness(options?: TestHarnessOptions): TestHarness {
const response = await dispatchFetch(
miniflare,
- `/cdn-cgi/handler/scheduled?${searchParams.toString()}`,
+ `/cdn-cgi/local/scheduled?${searchParams.toString()}`,
undefined,
workerName,
"scheduled"
diff --git a/packages/wrangler/src/d1/execute.ts b/packages/wrangler/src/d1/execute.ts
index 327824bf793..290a0a64fbb 100644
--- a/packages/wrangler/src/d1/execute.ts
+++ b/packages/wrangler/src/d1/execute.ts
@@ -317,7 +317,8 @@ async function executeLocally({
// TODO(#11870): Really we should prefer localDB.name here, but that would break users with existing local databases.
const id = localDB.previewDatabaseUuid ?? localDB.uuid ?? localDB.binding;
const persistencePath = getLocalPersistencePath(persistTo, config);
- const d1Persist = path.join(persistencePath, "v3", "d1");
+ const resourcePersistencePath = path.join(persistencePath, "v3");
+ const d1Persist = path.join(resourcePersistencePath, "d1");
logger.log(
`🌀 Executing on local database ${name} (${id}) from ${readableRelative(
@@ -331,7 +332,7 @@ async function executeLocally({
const mf = new Miniflare({
modules: true,
script: "",
- d1Persist,
+ resourcePersistencePath,
d1Databases: { DATABASE: id },
});
const db = await mf.getD1Database("DATABASE");
diff --git a/packages/wrangler/src/d1/export.ts b/packages/wrangler/src/d1/export.ts
index b6e6ae71788..43617f0c348 100644
--- a/packages/wrangler/src/d1/export.ts
+++ b/packages/wrangler/src/d1/export.ts
@@ -152,7 +152,8 @@ async function exportLocal(
// TODO: should we allow customising persistence path?
// Should it be --persist-to for consistency (even though this isn't persisting anything)?
const persistencePath = getLocalPersistencePath(undefined, config);
- const d1Persist = path.join(persistencePath, "v3", "d1");
+ const resourcePersistencePath = path.join(persistencePath, "v3");
+ const d1Persist = path.join(resourcePersistencePath, "d1");
logger.log(
`🌀 Exporting local database ${name} (${id}) from ${readableRelative(
@@ -166,7 +167,7 @@ async function exportLocal(
const mf = new Miniflare({
modules: true,
script: "export default {}",
- d1Persist,
+ resourcePersistencePath,
d1Databases: { DATABASE: id },
});
const db = await mf.getD1Database("DATABASE");
diff --git a/packages/wrangler/src/dev/miniflare/index.ts b/packages/wrangler/src/dev/miniflare/index.ts
index ff4be0e2aca..d2c8964f6b2 100644
--- a/packages/wrangler/src/dev/miniflare/index.ts
+++ b/packages/wrangler/src/dev/miniflare/index.ts
@@ -87,13 +87,10 @@ export interface ConfigBundle {
inspectorPort: number | undefined;
inspectorHost: string | undefined;
localPersistencePath: string | false;
- liveReload: boolean;
crons: Config["triggers"]["crons"];
routes: string[] | undefined;
queueConsumers: Config["queues"]["consumers"];
localProtocol: "http" | "https";
- httpsKeyPath: string | undefined;
- httpsCertPath: string | undefined;
localUpstream: string | undefined;
upstreamProtocol: "http" | "https";
inspect: boolean;
@@ -466,7 +463,6 @@ type WorkerOptionsBindings = Pick<
| "serviceBindings"
| "ratelimits"
| "workflows"
- | "wrappedBindings"
| "secretsStoreSecrets"
| "images"
| "email"
@@ -696,8 +692,6 @@ export function buildMiniflareBindingOptions(
const externalWorkers: WorkerOptions[] = [];
- const wrappedBindings: WorkerOptions["wrappedBindings"] = {};
-
for (const ai of aiBindings) {
warnOrError("ai", ai.remote);
}
@@ -1085,7 +1079,6 @@ export function buildMiniflareBindingOptions(
})
),
serviceBindings,
- wrappedBindings: wrappedBindings,
tails,
streamingTails,
};
@@ -1159,8 +1152,10 @@ export async function buildMiniflareOptions(
bindingOptions.browserRendering.headful = true;
}
const sitesOptions = buildSitesOptions(config);
- const defaultPersistRoot = getDefaultPersistRoot(config.localPersistencePath);
- const defaultProjectTmpPath = getDefaultProjectTmpPath(config.projectRoot);
+ const resourcePersistencePath = getDefaultPersistRoot(
+ config.localPersistencePath
+ );
+ const resourceTmpPath = getDefaultProjectTmpPath(config.projectRoot);
const assetOptions = buildAssetOptions(config);
const options: MiniflareOptions = {
@@ -1169,7 +1164,6 @@ export async function buildMiniflareOptions(
publicUrl: config.publicUrl,
inspectorPort: config.inspect ? config.inspectorPort : undefined,
inspectorHost: config.inspect ? config.inspectorHost : undefined,
- liveReload: config.liveReload,
upstream,
unsafeDevRegistryPath: config.devRegistry,
unsafeHandleDevRegistryUpdate: onDevRegistryUpdate,
@@ -1188,8 +1182,9 @@ export async function buildMiniflareOptions(
log,
verbose: logger.loggerLevel === "debug",
handleStructuredLogs: config.structuredLogsHandler ?? handleStructuredLogs,
- defaultPersistRoot,
- defaultProjectTmpPath,
+ resourcePersistencePath,
+ resourceTmpPath,
+ containerEngine: config.containerEngine,
workers: [
{
name: getName(config),
@@ -1202,7 +1197,6 @@ export async function buildMiniflareOptions(
...assetOptions,
routes: config.routes,
outboundService: config.outboundService,
- containerEngine: config.containerEngine,
zone: config.zone,
},
...externalWorkers,
diff --git a/packages/wrangler/src/dev/start-dev.ts b/packages/wrangler/src/dev/start-dev.ts
index f7a27d61877..c77804fe2f3 100644
--- a/packages/wrangler/src/dev/start-dev.ts
+++ b/packages/wrangler/src/dev/start-dev.ts
@@ -362,7 +362,7 @@ function maybePrintScheduledWorkerWarning(
logger.once.warn(
`Scheduled Workers are not automatically triggered during local development.\n` +
`To manually trigger a scheduled event, run:\n` +
- ` curl "http://${host}:${port}/cdn-cgi/handler/scheduled"\n` +
+ ` curl "http://${host}:${port}/cdn-cgi/local/scheduled"\n` +
`For more details, see https://developers.cloudflare.com/workers/configuration/cron-triggers/#test-cron-triggers-locally`
);
}
diff --git a/packages/wrangler/src/hello-world/index.ts b/packages/wrangler/src/hello-world/index.ts
index 3a6f8934412..9145ad0c1e1 100644
--- a/packages/wrangler/src/hello-world/index.ts
+++ b/packages/wrangler/src/hello-world/index.ts
@@ -23,11 +23,11 @@ export async function usingLocalHelloWorldBinding(
) => Promise
): Promise {
const persist = getLocalPersistencePath(persistTo, config);
- const defaultPersistRoot = getDefaultPersistRoot(persist);
+ const resourcePersistencePath = getDefaultPersistRoot(persist);
const mf = new Miniflare({
script:
'addEventListener("fetch", (e) => e.respondWith(new Response(null, { status: 404 })))',
- defaultPersistRoot,
+ resourcePersistencePath,
helloWorld: {
BINDING: {
enable_timer: false,
diff --git a/packages/wrangler/src/kv/helpers.ts b/packages/wrangler/src/kv/helpers.ts
index cd01bff4b3c..f07ba894375 100644
--- a/packages/wrangler/src/kv/helpers.ts
+++ b/packages/wrangler/src/kv/helpers.ts
@@ -584,11 +584,11 @@ export async function usingLocalNamespace(
// We need to cast to Config for the getLocalPersistencePath function since
// it expects a full Config object, even though it only uses compliance_region
const persist = getLocalPersistencePath(persistTo, config);
- const defaultPersistRoot = getDefaultPersistRoot(persist);
+ const resourcePersistencePath = getDefaultPersistRoot(persist);
const mf = new Miniflare({
script:
'addEventListener("fetch", (e) => e.respondWith(new Response(null, { status: 404 })))',
- defaultPersistRoot,
+ resourcePersistencePath,
kvNamespaces: { NAMESPACE: namespaceId },
});
const namespace = await mf.getKVNamespace("NAMESPACE");
diff --git a/packages/wrangler/src/r2/helpers/object.ts b/packages/wrangler/src/r2/helpers/object.ts
index 9563b60ed97..3a169ed0fd2 100644
--- a/packages/wrangler/src/r2/helpers/object.ts
+++ b/packages/wrangler/src/r2/helpers/object.ts
@@ -157,7 +157,7 @@ export async function usingLocalBucket(
) => Promise
): Promise {
const persist = getLocalPersistencePath(persistTo, config);
- const defaultPersistRoot = getDefaultPersistRoot(persist);
+ const resourcePersistencePath = getDefaultPersistRoot(persist);
const mf = new Miniflare({
modules: true,
// TODO(soon): import `reduceError()` from `miniflare:shared`
@@ -189,7 +189,7 @@ export async function usingLocalBucket(
}
}
}`,
- defaultPersistRoot,
+ resourcePersistencePath,
r2Buckets: { BUCKET: bucketName },
});
const bucket = await mf.getR2Bucket("BUCKET");
diff --git a/packages/wrangler/src/secrets-store/commands.ts b/packages/wrangler/src/secrets-store/commands.ts
index 040e4cf5022..aeabc4796f3 100644
--- a/packages/wrangler/src/secrets-store/commands.ts
+++ b/packages/wrangler/src/secrets-store/commands.ts
@@ -34,11 +34,11 @@ export async function usingLocalSecretsStoreSecretAPI(
) => Promise
): Promise {
const persist = getLocalPersistencePath(persistTo, config);
- const defaultPersistRoot = getDefaultPersistRoot(persist);
+ const resourcePersistencePath = getDefaultPersistRoot(persist);
const mf = new Miniflare({
script:
'addEventListener("fetch", (e) => e.respondWith(new Response(null, { status: 404 })))',
- defaultPersistRoot,
+ resourcePersistencePath,
secretsStoreSecrets: {
SECRET: {
store_id: storeId,
diff --git a/packages/wrangler/src/workflows/local.ts b/packages/wrangler/src/workflows/local.ts
index 505872aedd4..bb73fdce884 100644
--- a/packages/wrangler/src/workflows/local.ts
+++ b/packages/wrangler/src/workflows/local.ts
@@ -6,7 +6,7 @@ import type {
WorkflowInstanceRestartFrom,
} from "./types";
-const LOCAL_EXPLORER_BASE_PATH = "/cdn-cgi/explorer/api";
+const LOCAL_EXPLORER_BASE_PATH = "/cdn-cgi/local/explorer/api";
const DEFAULT_LOCAL_PORT = 8787;
/**
diff --git a/packages/wrangler/templates/new-worker-scheduled.js b/packages/wrangler/templates/new-worker-scheduled.js
index a56d30b72af..c95c7b1c10e 100644
--- a/packages/wrangler/templates/new-worker-scheduled.js
+++ b/packages/wrangler/templates/new-worker-scheduled.js
@@ -2,7 +2,7 @@
* Welcome to Cloudflare Workers! This is your first scheduled worker.
*
* - Run `wrangler dev` in your terminal to start a development server
- * - Run `curl "http://localhost:8787/cdn-cgi/handler/scheduled"` to trigger the scheduled event
+ * - Run `curl "http://localhost:8787/cdn-cgi/local/scheduled"` to trigger the scheduled event
* - Go back to the console to see what your worker has logged
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/configuration/cron-triggers/)
* - Run `wrangler publish --name my-worker` to publish your worker
diff --git a/packages/wrangler/templates/new-worker-scheduled.ts b/packages/wrangler/templates/new-worker-scheduled.ts
index f7f5992dfc8..67fef0d66c5 100644
--- a/packages/wrangler/templates/new-worker-scheduled.ts
+++ b/packages/wrangler/templates/new-worker-scheduled.ts
@@ -2,7 +2,7 @@
* Welcome to Cloudflare Workers! This is your first scheduled worker.
*
* - Run `wrangler dev` in your terminal to start a development server
- * - Run `curl "http://localhost:8787/cdn-cgi/handler/scheduled"` to trigger the scheduled event
+ * - Run `curl "http://localhost:8787/cdn-cgi/local/scheduled"` to trigger the scheduled event
* - Go back to the console to see what your worker has logged
* - Update the Cron trigger in wrangler.toml (see https://developers.cloudflare.com/workers/configuration/cron-triggers/)
* - Run `wrangler deploy --name my-worker` to deploy your worker
diff --git a/packages/wrangler/templates/startDevWorker/ProxyWorker.ts b/packages/wrangler/templates/startDevWorker/ProxyWorker.ts
index fc28268c008..32c92f6ba4b 100644
--- a/packages/wrangler/templates/startDevWorker/ProxyWorker.ts
+++ b/packages/wrangler/templates/startDevWorker/ProxyWorker.ts
@@ -131,6 +131,13 @@ export class ProxyWorker implements DurableObject {
request.url
);
+ // rewrite requests to old miniflare paths
+ // because wrangler cannot have a breaking change
+ userWorkerUrl.pathname = rewriteLegacyMiniflarePath(
+ userWorkerUrl.pathname
+ );
+ innerUrl.pathname = rewriteLegacyMiniflarePath(innerUrl.pathname);
+
// Preserve client `Accept-Encoding`, rather than using Worker's default
// of `Accept-Encoding: br, gzip`
const encoding = request.cf?.clientAcceptEncoding;
@@ -236,6 +243,26 @@ export class ProxyWorker implements DurableObject {
function isRequestFromProxyController(req: Request, env: Env): boolean {
return req.headers.get("Authorization") === env.PROXY_CONTROLLER_AUTH_SECRET;
}
+
+// Miniflare v5 moved its internal endpoints under `/cdn-cgi/local/` (and
+// `/__cf_local/` for endpoints that must remain reachable over tunnels). These
+// map the pre-v5 paths onto their current equivalents.
+const LEGACY_PATH_REWRITES: readonly [string, string][] = [
+ ["/cdn-cgi/handler", "/cdn-cgi/local"],
+ ["/cdn-cgi/mf/scheduled", "/cdn-cgi/local/scheduled"],
+ ["/cdn-cgi/mf/stream", "/__cf_local/stream"],
+ ["/cdn-cgi/mf/imagedelivery", "/__cf_local/imagedelivery"],
+ ["/cdn-cgi/explorer", "/cdn-cgi/local/explorer"],
+];
+
+function rewriteLegacyMiniflarePath(pathname: string): string {
+ for (const [oldPrefix, newPrefix] of LEGACY_PATH_REWRITES) {
+ if (pathname === oldPrefix || pathname.startsWith(`${oldPrefix}/`)) {
+ return newPrefix + pathname.slice(oldPrefix.length);
+ }
+ }
+ return pathname;
+}
function isHtmlResponse(res: Response): boolean {
return res.headers.get("content-type")?.startsWith("text/html") ?? false;
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d9a05df14f8..54f5dc8017a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -11,7 +11,7 @@ catalogs:
version: 0.13.3
'@cloudflare/workers-types':
specifier: ^5.20260714.1
- version: 5.20260714.1
+ version: 5.20260716.1
'@hey-api/openapi-ts':
specifier: 0.94.0
version: 0.94.0
@@ -191,7 +191,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/shared':
specifier: workspace:*
version: link:../shared
@@ -239,7 +239,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/node':
specifier: 22.15.17
version: 22.15.17
@@ -266,7 +266,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@playwright/test':
specifier: catalog:default
version: 1.60.0
@@ -296,7 +296,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -317,7 +317,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -341,7 +341,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -377,7 +377,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
vitest:
specifier: catalog:default
version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
@@ -392,7 +392,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
undici:
specifier: catalog:default
version: 7.28.0
@@ -407,7 +407,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/mimetext':
specifier: ^2.0.4
version: 2.0.4
@@ -446,7 +446,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/shared':
specifier: workspace:*
version: link:../shared
@@ -470,7 +470,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/jest-image-snapshot':
specifier: ^6.4.0
version: 6.4.0
@@ -497,7 +497,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
miniflare:
specifier: workspace:*
version: link:../../packages/miniflare
@@ -567,7 +567,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/is-even':
specifier: ^1.0.2
version: 1.0.2
@@ -585,11 +585,11 @@ importers:
dependencies:
'@sentry/cloudflare':
specifier: ^10
- version: 10.50.0(@cloudflare/workers-types@5.20260714.1)
+ version: 10.50.0(@cloudflare/workers-types@5.20260716.1)
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
vitest:
specifier: catalog:default
version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
@@ -620,7 +620,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -648,7 +648,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/node':
specifier: 22.15.17
version: 22.15.17
@@ -678,7 +678,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
undici:
specifier: catalog:default
version: 7.28.0
@@ -696,7 +696,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/debug':
specifier: 4.1.12
version: 4.1.12
@@ -729,7 +729,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -754,7 +754,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/pages-plugin':
specifier: workspace:*
version: link:../pages-plugin-example
@@ -778,7 +778,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -817,7 +817,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -838,7 +838,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -859,7 +859,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -877,7 +877,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
is-odd:
specifier: ^3.0.1
version: 3.0.1
@@ -896,7 +896,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/pages-plugin':
specifier: workspace:*
version: link:../pages-plugin-example
@@ -956,7 +956,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -977,7 +977,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1133,19 +1133,19 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
fixtures/rules-app:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
fixtures/secrets-store:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
wrangler:
specifier: workspace:*
version: link:../../packages/wrangler
@@ -1172,7 +1172,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/is-even':
specifier: ^1.0.2
version: 1.0.2
@@ -1196,7 +1196,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
vitest:
specifier: catalog:default
version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
@@ -1211,7 +1211,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
esbuild:
specifier: catalog:default
version: 0.28.1
@@ -1229,7 +1229,7 @@ importers:
devDependencies:
'@better-auth/stripe':
specifier: ^1.4.6
- version: 1.5.4(f660eebfb07999a7aa04507f74bf93ad)
+ version: 1.5.4(eef0467959f7f6b3622637d2501852d3)
'@cloudflare/containers':
specifier: ^0.2.2
version: 0.2.2
@@ -1238,7 +1238,7 @@ importers:
version: link:../../packages/vitest-pool-workers
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@microlabs/otel-cf-workers':
specifier: 1.0.0-rc.45
version: 1.0.0-rc.45(@opentelemetry/api@1.9.1)
@@ -1253,7 +1253,7 @@ importers:
version: 3.2.6
better-auth:
specifier: ^1.4.6
- version: 1.5.4(@cloudflare/workers-types@5.20260714.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0)
+ version: 1.5.4(@cloudflare/workers-types@5.20260716.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0)
cjs-wasm-module-dep:
specifier: file:./module-resolution/vendor/cjs-wasm-module-dep
version: file:fixtures/vitest-pool-workers-examples/module-resolution/vendor/cjs-wasm-module-dep
@@ -1327,7 +1327,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/shared':
specifier: workspace:*
version: link:../shared
@@ -1382,7 +1382,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
wrangler:
specifier: workspace:*
version: link:../../packages/wrangler
@@ -1394,7 +1394,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
miniflare:
specifier: workspace:*
version: link:../../packages/miniflare
@@ -1442,7 +1442,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
run-script-os:
specifier: ^1.1.6
version: 1.1.6
@@ -1466,7 +1466,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1487,7 +1487,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1508,7 +1508,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1529,7 +1529,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1550,7 +1550,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/jest-image-snapshot':
specifier: ^6.4.0
version: 6.4.0
@@ -1583,7 +1583,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/node':
specifier: 22.15.17
version: 22.15.17
@@ -1610,7 +1610,7 @@ importers:
version: link:../../packages/workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1628,7 +1628,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -1692,7 +1692,7 @@ importers:
version: 5.8.3
vitest:
specifier: catalog:default
- version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
+ version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
packages/chrome-devtools-patches:
devDependencies:
@@ -1762,7 +1762,7 @@ importers:
version: 5.8.3
vitest:
specifier: catalog:default
- version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))
+ version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))
packages/config:
dependencies:
@@ -1775,7 +1775,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -1838,7 +1838,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2046,7 +2046,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@octokit/types':
specifier: ^13.8.0
version: 13.8.0
@@ -2067,7 +2067,7 @@ importers:
version: link:../vitest-pool-workers
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2091,7 +2091,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2118,10 +2118,10 @@ importers:
devDependencies:
'@cloudflare/vitest-pool-workers':
specifier: catalog:default
- version: 0.13.3(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
+ version: 0.13.3(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/mime':
specifier: ^3.0.4
version: 3.0.4
@@ -2297,7 +2297,7 @@ importers:
version: link:../workers-shared
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2422,8 +2422,8 @@ importers:
specifier: ^2.0.2
version: 2.0.2
zod:
- specifier: ^3.25.76
- version: 3.25.76
+ specifier: catalog:default
+ version: 4.4.3
packages/mock-npm-registry:
devDependencies:
@@ -2460,7 +2460,7 @@ importers:
devDependencies:
'@cloudflare/vitest-pool-workers':
specifier: catalog:default
- version: 0.13.3(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
+ version: 0.13.3(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
'@cloudflare/workers-shared':
specifier: workspace:*
version: link:../workers-shared
@@ -2469,7 +2469,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
concurrently:
specifier: ^8.2.2
version: 8.2.2
@@ -2497,7 +2497,7 @@ importers:
devDependencies:
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2531,7 +2531,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/node':
specifier: 22.15.17
version: 22.15.17
@@ -2555,7 +2555,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
esbuild:
specifier: catalog:default
version: 0.28.1
@@ -2672,7 +2672,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -2768,7 +2768,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2789,7 +2789,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2810,7 +2810,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2831,7 +2831,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2852,7 +2852,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2873,7 +2873,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2894,7 +2894,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2915,7 +2915,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2936,7 +2936,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2957,7 +2957,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2978,7 +2978,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -2999,7 +2999,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3020,7 +3020,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3041,7 +3041,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3062,7 +3062,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3083,7 +3083,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3104,7 +3104,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/mimetext':
specifier: ^2.0.4
version: 2.0.4
@@ -3137,7 +3137,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3158,7 +3158,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3179,7 +3179,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3200,7 +3200,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3221,7 +3221,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3242,7 +3242,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3263,7 +3263,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3284,7 +3284,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3305,7 +3305,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@playground/main-resolution-package':
specifier: file:./package
version: file:packages/vite-plugin-cloudflare/playground/main-resolution/package
@@ -3329,7 +3329,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/express':
specifier: ^5.0.1
version: 5.0.1
@@ -3356,7 +3356,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@playground/module-resolution-excludes':
specifier: file:./packages/excludes
version: file:packages/vite-plugin-cloudflare/playground/module-resolution/packages/excludes
@@ -3368,7 +3368,7 @@ importers:
version: file:packages/vite-plugin-cloudflare/playground/module-resolution/packages/requires
'@remix-run/cloudflare':
specifier: 2.12.0
- version: 2.12.0(@cloudflare/workers-types@5.20260714.1)(typescript@5.8.3)
+ version: 2.12.0(@cloudflare/workers-types@5.20260716.1)(typescript@5.8.3)
'@types/react':
specifier: ^18.3.11
version: 18.3.18
@@ -3401,7 +3401,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3422,7 +3422,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@fixture/shared':
specifier: workspace:*
version: link:../../../../fixtures/shared
@@ -3474,7 +3474,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/react':
specifier: 19.1.0
version: 19.1.0
@@ -3495,7 +3495,7 @@ importers:
dependencies:
partyserver:
specifier: ^0.3.3
- version: 0.3.3(@cloudflare/workers-types@5.20260714.1)
+ version: 0.3.3(@cloudflare/workers-types@5.20260716.1)
partysocket:
specifier: ^1.1.16
version: 1.1.16(react@19.2.1)
@@ -3514,7 +3514,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@tailwindcss/vite':
specifier: ^4.2.1
version: 4.2.2(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
@@ -3550,7 +3550,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3571,7 +3571,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../../../workers-utils
@@ -3611,7 +3611,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/react':
specifier: 19.1.0
version: 19.1.0
@@ -3641,7 +3641,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3662,7 +3662,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3690,7 +3690,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/react':
specifier: 19.1.0
version: 19.1.0
@@ -3723,7 +3723,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3744,7 +3744,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3765,7 +3765,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3786,7 +3786,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@vitejs/plugin-basic-ssl':
specifier: ^2.2.0
version: 2.2.0(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
@@ -3810,7 +3810,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3831,7 +3831,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3852,7 +3852,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3873,7 +3873,7 @@ importers:
version: link:../../../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
typescript:
specifier: catalog:default
version: 5.8.3
@@ -3899,8 +3899,8 @@ importers:
specifier: workspace:*
version: link:../wrangler
zod:
- specifier: 3.25.76
- version: 3.25.76
+ specifier: catalog:default
+ version: 4.4.3
devDependencies:
'@cloudflare/mock-npm-registry':
specifier: workspace:*
@@ -3910,7 +3910,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -4168,13 +4168,13 @@ importers:
devDependencies:
'@cloudflare/vitest-pool-workers':
specifier: catalog:default
- version: 0.13.3(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
+ version: 0.13.3(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
'@cloudflare/workers-tsconfig':
specifier: workspace:*
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@sentry/cli':
specifier: ^2.37.0
version: 2.41.1(encoding@0.1.13)
@@ -4200,8 +4200,8 @@ importers:
specifier: catalog:default
version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
zod:
- specifier: ^3.25.76
- version: 3.25.76
+ specifier: catalog:default
+ version: 4.4.3
packages/workers-tsconfig: {}
@@ -4247,6 +4247,9 @@ importers:
jsonc-parser:
specifier: catalog:default
version: 3.2.0
+ kleur:
+ specifier: ^4.1.5
+ version: 4.1.5
open:
specifier: catalog:default
version: 11.0.0
@@ -4277,6 +4280,9 @@ importers:
xdg-app-paths:
specifier: ^8.3.0
version: 8.3.0
+ zod:
+ specifier: catalog:default
+ version: 4.4.3
packages/workflows-shared:
dependencies:
@@ -4295,13 +4301,13 @@ importers:
devDependencies:
'@cloudflare/vitest-pool-workers':
specifier: catalog:default
- version: 0.13.3(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
+ version: 0.13.3(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)
'@cloudflare/workers-tsconfig':
specifier: workspace:*
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@types/mime':
specifier: ^3.0.4
version: 3.0.4
@@ -4386,7 +4392,7 @@ importers:
version: link:../workers-tsconfig
'@cloudflare/workers-types':
specifier: catalog:default
- version: 5.20260714.1
+ version: 5.20260716.1
'@cloudflare/workers-utils':
specifier: workspace:*
version: link:../workers-utils
@@ -5641,8 +5647,8 @@ packages:
'@cloudflare/workers-types@4.20260702.1':
resolution: {integrity: sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA==}
- '@cloudflare/workers-types@5.20260714.1':
- resolution: {integrity: sha512-HGCTQVIQwzqAMLrZBCgLDrWKMgOdi6yn+S0Do1rF6z7t8tVvNprQfa53V6EDRRwsVO92uN5gviX2SxASDINZfA==}
+ '@cloudflare/workers-types@5.20260716.1':
+ resolution: {integrity: sha512-LqQPmGAvdpQxzZGAMlDI6fnCsTlr8nRQibWsREaERqD0PucwFl25aXpUskSob70uSY2n6K1sp6Te9xkmzSFgaw==}
'@codemirror/autocomplete@6.20.0':
resolution: {integrity: sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==}
@@ -5724,15 +5730,15 @@ packages:
'@esbuild-kit/cjs-loader@2.4.4':
resolution: {integrity: sha512-NfsJX4PdzhwSkfJukczyUiZGc7zNNWZcEAyqeISpDnn0PTfzMJR1aR8xAIPskBejIxBJbIgCCMzbaYa9SXepIg==}
- deprecated: 'Merged into tsx: https://tsx.hirok.io'
+ deprecated: 'Merged into tsx: https://tsx.is'
'@esbuild-kit/core-utils@3.3.2':
resolution: {integrity: sha512-sPRAnw9CdSsRmEtnsl2WXWdyquogVpB3yZ3dgwJfe8zrOzTsV7cJvmwrKVa+0ma5BoiGJ+BoqkMvawbayKUsqQ==}
- deprecated: 'Merged into tsx: https://tsx.hirok.io'
+ deprecated: 'Merged into tsx: https://tsx.is'
'@esbuild-kit/esm-loader@2.6.5':
resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
- deprecated: 'Merged into tsx: https://tsx.hirok.io'
+ deprecated: 'Merged into tsx: https://tsx.is'
'@esbuild/aix-ppc64@0.23.1':
resolution: {integrity: sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==}
@@ -16539,7 +16545,7 @@ snapshots:
optionalDependencies:
'@types/react': 19.2.13
- '@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)':
+ '@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)':
dependencies:
'@better-auth/utils': 0.3.1
'@better-fetch/fetch': 1.1.21
@@ -16550,9 +16556,9 @@ snapshots:
nanostores: 1.1.1
zod: 4.4.3
optionalDependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
- '@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@6.2.1)(kysely@0.28.11)(nanostores@1.1.1)':
+ '@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@6.2.1)(kysely@0.28.11)(nanostores@1.1.1)':
dependencies:
'@better-auth/utils': 0.3.1
'@better-fetch/fetch': 1.1.21
@@ -16563,50 +16569,50 @@ snapshots:
nanostores: 1.1.1
zod: 4.4.3
optionalDependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
- '@better-auth/drizzle-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))':
+ '@better-auth/drizzle-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
- drizzle-orm: 0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
+ drizzle-orm: 0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
- '@better-auth/kysely-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11)':
+ '@better-auth/kysely-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11)':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
kysely: 0.28.11
- '@better-auth/memory-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)':
+ '@better-auth/memory-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
- '@better-auth/mongo-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)':
+ '@better-auth/mongo-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
mongodb: 7.1.0
- '@better-auth/prisma-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))':
+ '@better-auth/prisma-adapter@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
'@prisma/client': 7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3)
prisma: 7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)
- '@better-auth/stripe@1.5.4(f660eebfb07999a7aa04507f74bf93ad)':
+ '@better-auth/stripe@1.5.4(eef0467959f7f6b3622637d2501852d3)':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
- better-auth: 1.5.4(@cloudflare/workers-types@5.20260714.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ better-auth: 1.5.4(@cloudflare/workers-types@5.20260716.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0)
better-call: 1.3.2(zod@4.4.3)
defu: 6.1.4
stripe: 20.4.1(@types/node@22.15.17)
zod: 4.4.3
- '@better-auth/telemetry@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))':
+ '@better-auth/telemetry@1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))':
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1)
'@better-auth/utils': 0.3.1
'@better-fetch/fetch': 1.1.21
@@ -17171,7 +17177,7 @@ snapshots:
lodash.memoize: 4.1.2
marked: 0.3.19
- '@cloudflare/vitest-pool-workers@0.13.3(@cloudflare/workers-types@5.20260714.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)':
+ '@cloudflare/vitest-pool-workers@0.13.3(@cloudflare/workers-types@5.20260716.1)(@vitest/runner@4.1.0)(@vitest/snapshot@4.1.0)(vitest@4.1.0)':
dependencies:
'@vitest/runner': 4.1.0
'@vitest/snapshot': 4.1.0
@@ -17179,7 +17185,7 @@ snapshots:
esbuild: 0.27.3
miniflare: 4.20260317.1
vitest: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.9.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
- wrangler: 4.76.0(@cloudflare/workers-types@5.20260714.1)
+ wrangler: 4.76.0(@cloudflare/workers-types@5.20260716.1)
zod: 3.25.76
transitivePeerDependencies:
- '@cloudflare/workers-types'
@@ -17241,7 +17247,7 @@ snapshots:
'@cloudflare/workers-types@4.20260702.1': {}
- '@cloudflare/workers-types@5.20260714.1': {}
+ '@cloudflare/workers-types@5.20260716.1': {}
'@codemirror/autocomplete@6.20.0':
dependencies:
@@ -18937,10 +18943,10 @@ snapshots:
optionalDependencies:
'@types/react': 18.3.3
- '@remix-run/cloudflare@2.12.0(@cloudflare/workers-types@5.20260714.1)(typescript@5.8.3)':
+ '@remix-run/cloudflare@2.12.0(@cloudflare/workers-types@5.20260716.1)(typescript@5.8.3)':
dependencies:
'@cloudflare/kv-asset-handler': 0.1.3
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
'@remix-run/server-runtime': 2.12.0(typescript@5.8.3)
optionalDependencies:
typescript: 5.8.3
@@ -19476,12 +19482,12 @@ snapshots:
- encoding
- supports-color
- '@sentry/cloudflare@10.50.0(@cloudflare/workers-types@5.20260714.1)':
+ '@sentry/cloudflare@10.50.0(@cloudflare/workers-types@5.20260716.1)':
dependencies:
'@opentelemetry/api': 1.9.1
'@sentry/core': 10.50.0
optionalDependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
'@sentry/core@10.50.0': {}
@@ -20689,14 +20695,23 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.0.3
- '@vitest/mocker@4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))':
+ '@vitest/mocker@4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))':
+ dependencies:
+ '@vitest/spy': 4.1.0
+ estree-walker: 3.0.3
+ magic-string: 0.30.21
+ optionalDependencies:
+ msw: 2.12.4(@types/node@22.15.17)(typescript@5.8.3)
+ vite: 8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)
+
+ '@vitest/mocker@4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))':
dependencies:
'@vitest/spy': 4.1.0
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
msw: 2.12.4(@types/node@22.15.17)(typescript@5.8.3)
- vite: 8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)
+ vite: 8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)
'@vitest/mocker@4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))':
dependencies:
@@ -21119,15 +21134,15 @@ snapshots:
before-after-hook@2.2.3: {}
- better-auth@1.5.4(@cloudflare/workers-types@5.20260714.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0):
+ better-auth@1.5.4(@cloudflare/workers-types@5.20260716.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))(mongodb@7.1.0)(mysql2@3.15.3)(pg@8.16.3)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(vitest@4.1.0):
dependencies:
- '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@6.2.1)(kysely@0.28.11)(nanostores@1.1.1)
- '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))
- '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11)
- '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)
- '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)
- '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
- '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260714.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))
+ '@better-auth/core': 1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@6.2.1)(kysely@0.28.11)(nanostores@1.1.1)
+ '@better-auth/drizzle-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)))
+ '@better-auth/kysely-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(kysely@0.28.11)
+ '@better-auth/memory-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)
+ '@better-auth/mongo-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(mongodb@7.1.0)
+ '@better-auth/prisma-adapter': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))(@better-auth/utils@0.3.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
+ '@better-auth/telemetry': 1.5.4(@better-auth/core@1.5.4(@better-auth/utils@0.3.1)(@better-fetch/fetch@1.1.21)(@cloudflare/workers-types@5.20260716.1)(better-call@1.3.2(zod@4.4.3))(jose@5.9.3)(kysely@0.28.11)(nanostores@1.1.1))
'@better-auth/utils': 0.3.1
'@better-fetch/fetch': 1.1.21
'@noble/ciphers': 2.1.1
@@ -21140,7 +21155,7 @@ snapshots:
zod: 4.4.3
optionalDependencies:
'@prisma/client': 7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3)
- drizzle-orm: 0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
+ drizzle-orm: 0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))
mongodb: 7.1.0
mysql2: 3.15.3
pg: 8.16.3
@@ -21953,9 +21968,9 @@ snapshots:
dependencies:
wordwrap: 1.0.0
- drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260714.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)):
+ drizzle-orm@0.45.1(@cloudflare/workers-types@5.20260716.1)(@electric-sql/pglite@0.3.2)(@opentelemetry/api@1.9.1)(@prisma/client@7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3))(@types/pg@8.15.4)(kysely@0.28.11)(mysql2@3.15.3)(pg@8.16.3)(postgres@3.4.7)(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3)):
optionalDependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
'@electric-sql/pglite': 0.3.2
'@opentelemetry/api': 1.9.1
'@prisma/client': 7.0.1(prisma@7.0.1(@types/react@19.2.13)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.8.3))(typescript@5.8.3)
@@ -24439,9 +24454,9 @@ snapshots:
parseurl@1.3.3: {}
- partyserver@0.3.3(@cloudflare/workers-types@5.20260714.1):
+ partyserver@0.3.3(@cloudflare/workers-types@5.20260716.1):
dependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
nanoid: 5.1.7
partysocket@1.1.16(react@19.2.1):
@@ -27014,7 +27029,7 @@ snapshots:
tsx: 4.21.0
yaml: 2.8.1
- vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1):
+ vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1):
dependencies:
lightningcss: 1.32.0
picomatch: 4.0.4
@@ -27026,6 +27041,21 @@ snapshots:
esbuild: 0.23.1
fsevents: 2.3.3
jiti: 2.6.1
+ tsx: 4.21.0
+ yaml: 2.8.1
+
+ vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1):
+ dependencies:
+ lightningcss: 1.32.0
+ picomatch: 4.0.4
+ postcss: 8.5.14
+ rolldown: 1.0.1
+ tinyglobby: 0.2.17
+ optionalDependencies:
+ '@types/node': 22.15.17
+ esbuild: 0.28.1
+ fsevents: 2.3.3
+ jiti: 2.6.1
tsx: 3.12.10
yaml: 2.8.1
@@ -27050,10 +27080,39 @@ snapshots:
mock-socket: 9.3.1
vitest: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
- vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)):
+ vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)):
+ dependencies:
+ '@vitest/expect': 4.1.0
+ '@vitest/mocker': 4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1))
+ '@vitest/pretty-format': 4.1.0
+ '@vitest/runner': 4.1.0
+ '@vitest/snapshot': 4.1.0
+ '@vitest/spy': 4.1.0
+ '@vitest/utils': 4.1.0
+ es-module-lexer: 2.0.0
+ expect-type: 1.3.0
+ magic-string: 0.30.21
+ obug: 2.1.1
+ pathe: 2.0.3
+ picomatch: 4.0.4
+ std-env: 4.0.0
+ tinybench: 2.9.0
+ tinyexec: 1.2.4
+ tinyglobby: 0.2.17
+ tinyrainbow: 3.0.3
+ vite: 8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.1)
+ why-is-node-running: 2.3.0
+ optionalDependencies:
+ '@opentelemetry/api': 1.9.1
+ '@types/node': 22.15.17
+ '@vitest/ui': 4.1.0(vitest@4.1.0)
+ transitivePeerDependencies:
+ - msw
+
+ vitest@4.1.0(@opentelemetry/api@1.9.1)(@types/node@22.15.17)(@vitest/ui@4.1.0)(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)):
dependencies:
'@vitest/expect': 4.1.0
- '@vitest/mocker': 4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))
+ '@vitest/mocker': 4.1.0(msw@2.12.4(@types/node@22.15.17)(typescript@5.8.3))(vite@8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1))
'@vitest/pretty-format': 4.1.0
'@vitest/runner': 4.1.0
'@vitest/snapshot': 4.1.0
@@ -27070,7 +27129,7 @@ snapshots:
tinyexec: 1.2.4
tinyglobby: 0.2.17
tinyrainbow: 3.0.3
- vite: 8.0.13(@types/node@22.15.17)(esbuild@0.23.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)
+ vite: 8.0.13(@types/node@22.15.17)(esbuild@0.28.1)(jiti@2.6.1)(tsx@3.12.10)(yaml@2.8.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@opentelemetry/api': 1.9.1
@@ -27276,7 +27335,7 @@ snapshots:
'@cloudflare/workerd-linux-arm64': 1.20260714.1
'@cloudflare/workerd-windows-64': 1.20260714.1
- wrangler@4.76.0(@cloudflare/workers-types@5.20260714.1):
+ wrangler@4.76.0(@cloudflare/workers-types@5.20260716.1):
dependencies:
'@cloudflare/kv-asset-handler': 0.4.2
'@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260317.1)
@@ -27287,7 +27346,7 @@ snapshots:
unenv: 2.0.0-rc.24
workerd: 1.20260317.1
optionalDependencies:
- '@cloudflare/workers-types': 5.20260714.1
+ '@cloudflare/workers-types': 5.20260716.1
fsevents: 2.3.3
transitivePeerDependencies:
- bufferutil
diff --git a/tools/deployments/validate-catalog-usage.ts b/tools/deployments/validate-catalog-usage.ts
index 992a78d3d4d..11f7540353c 100644
--- a/tools/deployments/validate-catalog-usage.ts
+++ b/tools/deployments/validate-catalog-usage.ts
@@ -12,12 +12,7 @@ const ROOT = resolve(__dirname, "../..");
// Deps that are deliberately pinned outside the catalog (e.g. workerd is
// bumped in coordinated PRs with its own automation).
-const IGNORED_DEPS = new Set([
- "workerd",
- // miniflare, vitest-pool-workers, and workers-shared still use zod v3
- // and cannot adopt the v4 catalog version yet.
- "zod",
-]);
+const IGNORED_DEPS = new Set(["workerd"]);
/**
* Parses the `catalog:` block of a pnpm-workspace.yaml into a map of
diff --git a/tools/deployments/validate-changesets.ts b/tools/deployments/validate-changesets.ts
index 53683b821c4..498072b819c 100644
--- a/tools/deployments/validate-changesets.ts
+++ b/tools/deployments/validate-changesets.ts
@@ -31,7 +31,12 @@ export function validateChangesets(
);
}
- if (release.type === "major" && targetPackage?.private !== true) {
+ // TODO(miniflare-v5): restore this check after miniflare v5 is released
+ if (
+ release.type === "major" &&
+ targetPackage?.private !== true &&
+ release.name !== "miniflare"
+ ) {
errors.push(
`Major version bumps are not allowed for package "${release.name}" in changeset at "${file}".`
);