diff --git a/.github/workflows/miniflare-dependabot-versioning-prs.yml b/.github/workflows/miniflare-dependabot-versioning-prs.yml index c1fc5cb5b695..1f1b7e7fe5f9 100644 --- a/.github/workflows/miniflare-dependabot-versioning-prs.yml +++ b/.github/workflows/miniflare-dependabot-versioning-prs.yml @@ -12,7 +12,7 @@ permissions: jobs: generate-changeset: - runs-on: ubuntu-slim + runs-on: ubuntu-latest if: | github.event.pull_request.user.login == 'dependabot[bot]' steps: @@ -26,11 +26,29 @@ jobs: - name: Install Dependencies uses: ./.github/actions/install-dependencies + - name: Install Cap'n Proto compiler + run: | + sudo apt-get update + sudo apt-get install -y capnproto + - name: Configure Git run: | git config --global user.email wrangler@cloudflare.com git config --global user.name 'Wrangler automated PR updater' + - name: Regenerate compatibility flags + run: | + pnpm --filter miniflare run capnp:compat + + # Commit the regenerated files if there are changes + git add packages/miniflare/src/shared/compatibility-date.ts packages/miniflare/src/shared/compatibility-flags-metadata.ts + if git diff --cached --quiet; then + echo "No changes to compatibility flags files" + else + git commit -m "chore(miniflare): regenerate compatibility flags for workerd update" + git push + fi + - name: Generate Miniflare changesets # The paremeters to the script are: # - PR: The number of the current Dependabot PR diff --git a/packages/miniflare/package.json b/packages/miniflare/package.json index 7f3b4959b119..d3fcbb6d56b3 100644 --- a/packages/miniflare/package.json +++ b/packages/miniflare/package.json @@ -31,6 +31,7 @@ ], "scripts": { "build": "node scripts/build.mjs && pnpm run types:build", + "capnp:compat": "node scripts/build-capnp-compat.mjs", "capnp:workerd": "node scripts/build-capnp.mjs", "check:lint": "eslint --max-warnings=0 \"{src,test}/**/*.ts\" \"types/**/*.ts\"", "check:type": "tsc", diff --git a/packages/miniflare/scripts/build-capnp-compat.mjs b/packages/miniflare/scripts/build-capnp-compat.mjs new file mode 100644 index 000000000000..4f5af1997fc8 --- /dev/null +++ b/packages/miniflare/scripts/build-capnp-compat.mjs @@ -0,0 +1,425 @@ +import { execSync } from "node:child_process"; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import { join } from "node:path"; + +/** + * # Compatibility Flags Resolution + * + * This script generates TypeScript files that are used by `getWorkerdFeatureFlags()`, an implementation of workerd's compatibility flag resolution logic. + * + * ## Overview + * + * The `getWorkerdFeatureFlags()` function replicates the behavior of workerd's C++ `compileCompatibilityFlags()` function from `src/workerd/io/compatibility-date.c++`. It computes which compatibility flags are enabled based on: + * + * 1. **Compatibility Date**: Flags are automatically enabled after their `compatEnableDate` + * 2. **Explicit Flags**: Users can explicitly enable/disable specific flags + * 3. **Implied Flags**: Some flags automatically enable others after a certain date (cascading) + * + * ## Files + * + * - **`src/shared/compatibility-date.ts`**: Cap'n Proto types generated by `capnp-es` (generated and committed to repo) + * - **`src/shared/compatibility-flags-metadata.ts`**: Annotation metadata extracted from the schema (generated and committed to repo) + * - **`src/shared/compatibility-flags.ts`**: Main implementation with `getWorkerdFeatureFlags()` function + * + * ## Regenerating the Files + * + * The generated files should be regenerated when the workerd dependency is updated. This is typically done in CI as part of the workerd update PR automation. + * + * ### Prerequisites + * + * The build script requires `capnpc` (Cap'n Proto compiler) to be installed: + * + * ```bash + * # macOS + * brew install capnp + * + * # Ubuntu/Debian + * sudo apt-get install capnproto + * + * # Other platforms + * # See: https://capnproto.org/install.html + * ``` + * + * ### Running the Build Script + * + * ```bash + * cd packages/miniflare + * pnpm run capnp:compat + * ``` + * + * The script will: + * + * 1. Create a unique temporary directory + * 2. Download `compatibility-date.capnp` from the workerd repository + * 3. Compile it to TypeScript using `capnp-es` (requires `capnpc`) + * 4. Parse the schema to extract annotation metadata (enable flags, dates, implied relationships) + * 5. Generate both output files in `src/shared/` + * 6. Clean up the temporary directory + * + * ## Architecture + * + * ``` + * ┌─────────────────────────────────────────────────────────────────┐ + * │ workerd repository │ + * │ https://github.com/cloudflare/workerd/.../compatibility-date.capnp │ + * └─────────────────────────────────────────────────────────────────┘ + * │ + * │ download (at build time) + * ▼ + * ┌─────────────────────────────────────────────────────────────────┐ + * │ scripts/build-capnp-compat.mjs │ + * │ 1. Downloads .capnp to temp directory │ + * │ 2. Runs capnp-es to generate TypeScript types │ + * │ 3. Parses .capnp text to extract annotation metadata │ + * │ 4. Cleans up temp directory │ + * └─────────────────────────────────────────────────────────────────┘ + * │ + * ┌───────────────┴───────────────┐ + * ▼ ▼ + * ┌─────────────────────────┐ ┌─────────────────────────────────┐ + * │ compatibility-date.ts │ │ compatibility-flags-metadata.ts │ + * │ (capnp-es generated) │ │ (annotation metadata) │ + * │ │ │ │ + * │ - CompatibilityFlags │ │ - FLAG_METADATA[] │ + * │ class with typed │ │ - fieldName │ + * │ boolean getters │ │ - enableFlag │ + * │ │ │ - disableFlag │ + * │ │ │ - enableDate │ + * │ │ │ - impliedBy │ + * └─────────────────────────┘ └─────────────────────────────────┘ + * │ │ + * └───────────────┬───────────────┘ + * ▼ + * ┌─────────────────────────────────────────────────────────────────┐ + * │ compatibility-flags.ts │ + * │ │ + * │ getWorkerdFeatureFlags(date, flags) → FeatureFlagsMap │ + * │ │ + * │ - Uses FLAG_METADATA for resolution logic │ + * │ - Returns typed FeatureFlagsMap (from CompatibilityFlags) │ + * └─────────────────────────────────────────────────────────────────┘ + * ``` + */ +const WORKERD_CAPNP_URL = + "https://raw.githubusercontent.com/cloudflare/workerd/main/src/workerd/io/compatibility-date.capnp"; +const GENERATED_TS_PATH = "src/shared/compatibility-date.ts"; +const METADATA_PATH = "src/shared/compatibility-flags-metadata.ts"; + +/** + * Downloads the compatibility-date.capnp file from the workerd repository + * to a temporary directory. + * + * @param {string} tempDir - The temporary directory to download to + * @returns {Promise<{capnpPath: string, content: string}>} + */ +async function downloadCapnpFile(tempDir) { + console.log(`Downloading ${WORKERD_CAPNP_URL}...`); + + let response; + try { + response = await fetch(WORKERD_CAPNP_URL); + } catch (/** @type {any} */ error) { + const errorCode = error.cause?.code || error.code; + if ( + errorCode === "ENOTFOUND" || + errorCode === "ENETUNREACH" || + errorCode === "ECONNREFUSED" + ) { + console.error("\nNetwork error: Unable to connect to GitHub."); + console.error("Please check your internet connection and try again."); + console.error(`\nAttempted URL: ${WORKERD_CAPNP_URL}`); + } else { + console.error(`\nFailed to download: ${error.message}`); + } + throw error; + } + + if (!response.ok) { + const errorMsg = `Failed to download: ${response.status} ${response.statusText}`; + console.error(`\n${errorMsg}`); + console.error(`URL: ${WORKERD_CAPNP_URL}`); + if (response.status === 404) { + console.error( + "The compatibility-date.capnp file may have moved in the workerd repository." + ); + } + throw new Error(errorMsg); + } + + const content = await response.text(); + const capnpPath = join(tempDir, "compatibility-date.capnp"); + writeFileSync(capnpPath, content); + console.log(`Downloaded to ${capnpPath}`); + return { capnpPath, content }; +} + +/** + * Checks if capnpc is available on the system. + * @returns {boolean} + */ +function isCapnpcAvailable() { + try { + execSync("capnpc --version", { stdio: "ignore" }); + return true; + } catch { + return false; + } +} + +/** + * Compiles the .capnp file to TypeScript using capnp-es. + * This requires capnpc to be installed on the system. + * + * @param {string} capnpPath - Path to the .capnp file + * @param {string} tempDir - The temporary directory where generated file will be created + */ +function compileCapnpToTypeScript(capnpPath, tempDir) { + console.log(`Compiling ${capnpPath} with capnp-es...`); + + // Check if capnpc is available before attempting to compile + if (!isCapnpcAvailable()) { + console.error("\nError: capnpc (Cap'n Proto compiler) is not installed."); + console.error( + "The capnp-es tool requires capnpc to compile .capnp schema files.\n" + ); + console.error("To install capnpc:"); + console.error(" macOS: brew install capnp"); + console.error(" Ubuntu/Debian: sudo apt-get install capnproto"); + console.error(" Other: https://capnproto.org/install.html\n"); + throw new Error("capnpc is not installed"); + } + + try { + execSync(`npx capnp-es ${capnpPath} -ots`, { stdio: "inherit" }); + + // capnp-es generates the .ts file next to the .capnp file + const generatedInTemp = join(tempDir, "compatibility-date.ts"); + + // Read the generated file and prepend our header with documentation link + const generatedContent = readFileSync(generatedInTemp, "utf-8"); + const header = `/** + * Auto-generated by capnp-es from compatibility-date.capnp - DO NOT EDIT MANUALLY + * + * This file is regenerated when the workerd dependency is updated. + * Source: ${WORKERD_CAPNP_URL} + * + * @see {@link file://./../../../scripts/build-capnp-compat.mjs} for documentation + */ + +`; + writeFileSync(GENERATED_TS_PATH, header + generatedContent); + console.log(`Generated ${GENERATED_TS_PATH}`); + } catch (error) { + console.error("\nFailed to compile .capnp file with capnp-es."); + throw error; + } +} + +/** + * @typedef {object} FlagMetadata + * @property {string} fieldName + * @property {number} ordinal + * @property {string} [enableFlag] + * @property {string} [disableFlag] + * @property {string} [enableDate] + * @property {boolean} [experimental] + * @property {Array<{names: string[], date: string}>} [impliedBy] + */ + +/** + * Parses the compatibility-date.capnp file to extract flag metadata. + * This extracts field definitions and their annotations which are not + * included in the capnp-es generated TypeScript. + * + * @param {string} content - The content of the .capnp file + * @returns {FlagMetadata[]} + */ +function parseCapnpAnnotations(content) { + /** @type {FlagMetadata[]} */ + const flags = []; + + // Split into lines for processing + const lines = content.split("\n"); + + /** @type {FlagMetadata | null} */ + let currentField = null; + let inCompatibilityFlags = false; + + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + + // Check if we're entering the CompatibilityFlags struct + if (line.includes("struct CompatibilityFlags")) { + inCompatibilityFlags = true; + continue; + } + + // Skip lines until we're in CompatibilityFlags + if (!inCompatibilityFlags) continue; + + // End of struct + if (line === "}" && currentField === null) { + break; + } + + // Skip annotation definitions themselves + if (line.startsWith("annotation ")) { + continue; + } + + // Match field definition: fieldName @ordinal :Type + const fieldMatch = line.match(/^(\w+)\s+@(\d+)\s+:Bool/); + if (fieldMatch) { + // Save previous field if exists + if (currentField) { + flags.push(currentField); + } + + // Start new field + currentField = { + fieldName: fieldMatch[1], + ordinal: parseInt(fieldMatch[2]), + }; + continue; + } + + // If we have a current field, check for annotations + if (currentField) { + // Match $compatEnableFlag("flag_name") + const enableMatch = line.match(/\$compatEnableFlag\("([^"]+)"\)/); + if (enableMatch) { + currentField.enableFlag = enableMatch[1]; + } + + // Match $compatDisableFlag("flag_name") + const disableMatch = line.match(/\$compatDisableFlag\("([^"]+)"\)/); + if (disableMatch) { + currentField.disableFlag = disableMatch[1]; + } + + // Match $compatEnableDate("YYYY-MM-DD") + const dateMatch = line.match(/\$compatEnableDate\("([^"]+)"\)/); + if (dateMatch) { + currentField.enableDate = dateMatch[1]; + } + + // Match $experimental + if (line.includes("$experimental")) { + currentField.experimental = true; + } + + // Match $impliedByAfterDate(name = "flag", date = "YYYY-MM-DD") + const impliedSingleMatch = line.match( + /\$impliedByAfterDate\(name\s*=\s*"([^"]+)"\s*,\s*date\s*=\s*"([^"]+)"\)/ + ); + if (impliedSingleMatch) { + if (!currentField.impliedBy) { + currentField.impliedBy = []; + } + currentField.impliedBy.push({ + names: [impliedSingleMatch[1]], + date: impliedSingleMatch[2], + }); + } + + // Match $impliedByAfterDate(names = ["flag1", "flag2"], date = "YYYY-MM-DD") + const impliedMultiMatch = line.match( + /\$impliedByAfterDate\(names\s*=\s*\[([^\]]+)\]\s*,\s*date\s*=\s*"([^"]+)"\)/ + ); + if (impliedMultiMatch) { + const names = impliedMultiMatch[1] + .split(",") + .map((s) => s.trim().replace(/"/g, "")); + if (!currentField.impliedBy) { + currentField.impliedBy = []; + } + currentField.impliedBy.push({ + names, + date: impliedMultiMatch[2], + }); + } + + // If line ends with semicolon, this field is complete + if (line.endsWith(";")) { + flags.push(currentField); + currentField = null; + } + } + } + + // Add last field if exists + if (currentField) { + flags.push(currentField); + } + + return flags; +} + +/** + * Generates the metadata TypeScript file from parsed annotations. + * + * @param {FlagMetadata[]} flagMetadata + */ +function generateMetadataFile(flagMetadata) { + const tsContent = `/** + * Auto-generated from compatibility-date.capnp - DO NOT EDIT MANUALLY + * + * This file is regenerated when the workerd dependency is updated. + * Source: ${WORKERD_CAPNP_URL} + * + * @see {@link file://./../../../scripts/build-capnp-compat.mjs} for documentation + */ + +export interface FlagMetadata { + fieldName: string; + ordinal: number; + enableFlag?: string; + disableFlag?: string; + enableDate?: string; + experimental?: boolean; + impliedBy?: Array<{ names: string[]; date: string }>; +} + +export const FLAG_METADATA: FlagMetadata[] = ${JSON.stringify(flagMetadata, null, "\t")}; +`; + + writeFileSync(METADATA_PATH, tsContent); + console.log(`Generated ${METADATA_PATH} with ${flagMetadata.length} flags`); +} + +// Main execution +async function main() { + // Create a unique temporary directory for the .capnp file + const tempDir = mkdtempSync(join(tmpdir(), "capnp-compat-")); + console.log(`Using temporary directory: ${tempDir}`); + + try { + // Step 1: Download the .capnp file from workerd to temp directory + const { capnpPath, content: capnpContent } = + await downloadCapnpFile(tempDir); + + // Step 2: Compile to TypeScript using capnp-es (requires capnpc) + compileCapnpToTypeScript(capnpPath, tempDir); + + // Step 3: Parse annotations from the .capnp file + const flagMetadata = parseCapnpAnnotations(capnpContent); + console.log(`Parsed ${flagMetadata.length} compatibility flags`); + + // Step 4: Generate the metadata file + generateMetadataFile(flagMetadata); + + console.log("\nSuccessfully generated compatibility flags files:"); + console.log(` - ${GENERATED_TS_PATH} (capnp-es generated types)`); + console.log(` - ${METADATA_PATH} (annotation metadata)`); + } catch (/** @type {any} */ error) { + console.error("Error:", error.message); + process.exit(1); + } finally { + // Clean up the temporary directory + rmSync(tempDir, { recursive: true, force: true }); + console.log(`Cleaned up temporary directory: ${tempDir}`); + } +} + +main(); diff --git a/packages/miniflare/src/shared/compatibility-date.ts b/packages/miniflare/src/shared/compatibility-date.ts new file mode 100644 index 000000000000..1f34cd65dffd --- /dev/null +++ b/packages/miniflare/src/shared/compatibility-date.ts @@ -0,0 +1,1970 @@ +/** + * Auto-generated by capnp-es from compatibility-date.capnp - DO NOT EDIT MANUALLY + * + * This file is regenerated when the workerd dependency is updated. + * Source: https://raw.githubusercontent.com/cloudflare/workerd/main/src/workerd/io/compatibility-date.capnp + * + * @see {@link file://./../../../scripts/build-capnp-compat.mjs} for documentation + */ + +// This file has been automatically generated by capnp-es. +import * as $ from "capnp-es"; +export const _capnpFileId = BigInt("0x8b3d4aaa36221ec8"); +export const ImpliedByAfterDate_Which = { + NAME: 0, + NAMES: 1 +} as const; +export type ImpliedByAfterDate_Which = (typeof ImpliedByAfterDate_Which)[keyof typeof ImpliedByAfterDate_Which]; +/** +* Annotates a compatibility flag to indicate that it is implied by the enablement +* of the named flag(s) after the specified date. +* +*/ +export class ImpliedByAfterDate extends $.Struct { + static readonly NAME = ImpliedByAfterDate_Which.NAME; + static readonly NAMES = ImpliedByAfterDate_Which.NAMES; + static readonly _capnp = { + displayName: "ImpliedByAfterDate", + id: "8f8c1b68151b6cff", + size: new $.ObjectSize(8, 2), + }; + get name(): string { + $.utils.testWhich("name", $.utils.getUint16(0, this), 0, this); + return $.utils.getText(0, this); + } + get _isName(): boolean { + return $.utils.getUint16(0, this) === 0; + } + set name(value: string) { + $.utils.setUint16(0, 0, this); + $.utils.setText(0, value, this); + } + _adoptNames(value: $.Orphan<$.List>): void { + $.utils.setUint16(0, 1, this); + $.utils.adopt(value, $.utils.getPointer(0, this)); + } + _disownNames(): $.Orphan<$.List> { + return $.utils.disown(this.names); + } + get names(): $.List { + $.utils.testWhich("names", $.utils.getUint16(0, this), 1, this); + return $.utils.getList(0, $.TextList, this); + } + _hasNames(): boolean { + return !$.utils.isNull($.utils.getPointer(0, this)); + } + _initNames(length: number): $.List { + $.utils.setUint16(0, 1, this); + return $.utils.initList(0, $.TextList, length, this); + } + get _isNames(): boolean { + return $.utils.getUint16(0, this) === 1; + } + set names(value: $.List) { + $.utils.setUint16(0, 1, this); + $.utils.copyFrom(value, $.utils.getPointer(0, this)); + } + get date(): string { + return $.utils.getText(1, this); + } + set date(value: string) { + $.utils.setText(1, value, this); + } + toString(): string { return "ImpliedByAfterDate_" + super.toString(); } + which(): ImpliedByAfterDate_Which { + return $.utils.getUint16(0, this) as ImpliedByAfterDate_Which; + } +} +/** +* Flags that change the basic behavior of the runtime API, especially for +* backwards-compatibility with old bugs. +* +* Note: At one time, this was called "FeatureFlags", and many places in the codebase still call +* it that. We could do a mass-rename but there's some tricky spots involving JSON +* communications with other systems... I'm leaving it for now. +* +*/ +export class CompatibilityFlags extends $.Struct { + static readonly _capnp = { + displayName: "CompatibilityFlags", + id: "8f8c1b68151b6cef", + size: new $.ObjectSize(24, 0), + }; + /** +* Our original implementations of FormData made the mistake of turning files into strings. +* We hadn't implemented `File` yet, and we forgot. +* +*/ + get formDataParserSupportsFiles(): boolean { + return $.utils.getBit(0, this); + } + set formDataParserSupportsFiles(value: boolean) { + $.utils.setBit(0, value, this); + } + /** +* Our original implementation of fetch() incorrectly accepted URLs with any scheme (protocol). +* It happily sent any scheme to FL in the `X-Forwarded-Proto` header. FL would ignore any +* scheme it didn't recgonize, which effectively meant it treated all schemes other than `https` +* as `http`. +* +*/ + get fetchRefusesUnknownProtocols(): boolean { + return $.utils.getBit(1, this); + } + set fetchRefusesUnknownProtocols(value: boolean) { + $.utils.setBit(1, value, this); + } + /** +* Our original implementation of `esi:include` treated it as needing an end tag. +* We're worried that fixing this could break existing workers. +* +*/ + get esiIncludeIsVoidTag(): boolean { + return $.utils.getBit(2, this); + } + set esiIncludeIsVoidTag(value: boolean) { + $.utils.setBit(2, value, this); + } + get obsolete3(): boolean { + return $.utils.getBit(3, this); + } + set obsolete3(value: boolean) { + $.utils.setBit(3, value, this); + } + /** +* Our original implementation allowed URLs without schema and/or authority components and +* interpreted them relative to "https://fake-host/". +* +*/ + get durableObjectFetchRequiresSchemeAuthority(): boolean { + return $.utils.getBit(4, this); + } + set durableObjectFetchRequiresSchemeAuthority(value: boolean) { + $.utils.setBit(4, value, this); + } + /** +* The streams specification dictates that ArrayBufferViews that are passed in to the +* read() operation of a ReadableStreamBYOBReader are detached, making it impossible +* for user code to modify or observe the data as it is being read. Our original +* implementation did not do that. +* +*/ + get streamsByobReaderDetachesBuffer(): boolean { + return $.utils.getBit(5, this); + } + set streamsByobReaderDetachesBuffer(value: boolean) { + $.utils.setBit(5, value, this); + } + /** +* Controls the availability of the work in progress new ReadableStream() and +* new WritableStream() constructors backed by JavaScript underlying sources +* and sinks. +* +*/ + get streamsJavaScriptControllers(): boolean { + return $.utils.getBit(6, this); + } + set streamsJavaScriptControllers(value: boolean) { + $.utils.setBit(6, value, this); + } + /** +* Originally, JSG_PROPERTY registered getter/setters on an objects *instance* +* template as opposed to its prototype template. This broke subclassing at +* the JavaScript layer, preventing a subclass from correctly overriding the +* superclasses getters/setters. This flag controls the breaking change made +* to set those getters/setters on the prototype template instead. +* +*/ + get jsgPropertyOnPrototypeTemplate(): boolean { + return $.utils.getBit(7, this); + } + set jsgPropertyOnPrototypeTemplate(value: boolean) { + $.utils.setBit(7, value, this); + } + /** +* Turns off a bunch of redundant features for outgoing subrequests from Cloudflare. Historically, +* a number of standard Cloudflare CDN features unrelated to Workers would sometimes run on +* Workers subrequests despite not making sense there. For example, Cloudflare might automatically +* apply gzip compression when the origin server responds without it but the client supports it. +* This doesn't make sense to do when the response is going to be consumed by Workers, since the +* Workers Runtime is typically running on the same machine. When Workers itself returns a +* response to the client, Cloudflare's stack will have another chance to apply gzip, and it +* makes much more sense to do there. +* +*/ + get minimalSubrequests(): boolean { + return $.utils.getBit(8, this); + } + set minimalSubrequests(value: boolean) { + $.utils.setBit(8, value, this); + } + /** +* Tells FL not to use the Custom Origin Trust Store for grey-cloud / external subrequests. Fixes +* EW-5299. +* +*/ + get noCotsOnExternalFetch(): boolean { + return $.utils.getBit(9, this); + } + set noCotsOnExternalFetch(value: boolean) { + $.utils.setBit(9, value, this); + } + /** +* The original URL implementation based on kj::Url is not compliant with the +* WHATWG URL Standard, leading to a number of issues reported by users. Unfortunately, +* making it spec compliant is a breaking change. This flag controls the availability +* of the new spec-compliant URL implementation. +* +*/ + get specCompliantUrl(): boolean { + return $.utils.getBit(10, this); + } + set specCompliantUrl(value: boolean) { + $.utils.setBit(10, value, this); + } + get globalNavigator(): boolean { + return $.utils.getBit(11, this); + } + set globalNavigator(value: boolean) { + $.utils.setBit(11, value, this); + } + /** +* Many worker APIs that return JavaScript promises currently throw synchronous errors +* when exceptions occur. Per the Web Platform API specs, async functions should never +* throw synchronously. This flag changes the behavior so that async functions return +* rejections instead of throwing. +* +*/ + get captureThrowsAsRejections(): boolean { + return $.utils.getBit(12, this); + } + set captureThrowsAsRejections(value: boolean) { + $.utils.setBit(12, value, this); + } + /** +* R2 public beta bindings are the default. +* R2 internal beta bindings is back-compat. +* +*/ + get r2PublicBetaApi(): boolean { + return $.utils.getBit(13, this); + } + set r2PublicBetaApi(value: boolean) { + $.utils.setBit(13, value, this); + } + /** +* Used to gate access to durable object alarms to authorized workers, no longer used (alarms +* are now enabled for everyone). +* +*/ + get obsolete14(): boolean { + return $.utils.getBit(14, this); + } + set obsolete14(value: boolean) { + $.utils.setBit(14, value, this); + } + /** +* There is a bug in the original implementation of the kj::Maybe type wrapper +* that had it inappropriately interpret a nullptr result as acceptable as opposed +* to it being a type error. Enabling this flag switches on the correct behavior. +* Disabling the flag switches back to the original broken behavior. +* +*/ + get noSubstituteNull(): boolean { + return $.utils.getBit(15, this); + } + set noSubstituteNull(value: boolean) { + $.utils.setBit(15, value, this); + } + /** +* Controls whether the TransformStream constructor conforms to the stream standard or not. +* Must be used in combination with the streamsJavaScriptControllers flag. +* +*/ + get transformStreamJavaScriptControllers(): boolean { + return $.utils.getBit(16, this); + } + set transformStreamJavaScriptControllers(value: boolean) { + $.utils.setBit(16, value, this); + } + /** +* Controls if R2 bucket.list honors the `include` field as intended. It previously didn't +* and by default would result in http & custom metadata for an object always being returned +* in the list. +* +*/ + get r2ListHonorIncludeFields(): boolean { + return $.utils.getBit(17, this); + } + set r2ListHonorIncludeFields(value: boolean) { + $.utils.setBit(17, value, this); + } + /** +* Unfortunately, when the CommonJsModule type was implemented, it mistakenly exported the +* module namespace (an object like `{default: module.exports}`) rather than exporting only +* the module.exports. When this flag is enabled, the export is fixed. +* +*/ + get exportCommonJsDefaultNamespace(): boolean { + return $.utils.getBit(18, this); + } + set exportCommonJsDefaultNamespace(value: boolean) { + $.utils.setBit(18, value, this); + } + /** +* Obsolete flag. Has no effect. +* +*/ + get obsolete19(): boolean { + return $.utils.getBit(19, this); + } + set obsolete19(value: boolean) { + $.utils.setBit(19, value, this); + } + /** +* Enables WebSocket compression. Without this flag, all attempts to negotiate compression will +* be refused for scripts prior to the compat date, so WebSockets will never use compression. +* With this flag, the system will automatically negotiate the use of the permessage-deflate +* extension where appropriate. The Worker can also request specific compression settings by +* specifying a valid Sec-WebSocket-Extensions header, or setting the header to the empty string +* to explicitly request that no compression be used. +* +*/ + get webSocketCompression(): boolean { + return $.utils.getBit(20, this); + } + set webSocketCompression(value: boolean) { + $.utils.setBit(20, value, this); + } + /** +* Enables nodejs compat imports in the application. +* +*/ + get nodeJsCompat(): boolean { + return $.utils.getBit(21, this); + } + set nodeJsCompat(value: boolean) { + $.utils.setBit(21, value, this); + } + /** +* Used to enables TCP sockets in workerd. +* +*/ + get obsolete22(): boolean { + return $.utils.getBit(22, this); + } + set obsolete22(value: boolean) { + $.utils.setBit(22, value, this); + } + /** +* The original URL implementation based on kj::Url is not compliant with the +* WHATWG URL Standard, leading to a number of issues reported by users. Unfortunately, +* the specCompliantUrl flag did not contemplate the redirect usage. This flag is +* specifically about the usage in a redirect(). +* +*/ + get specCompliantResponseRedirect(): boolean { + return $.utils.getBit(23, this); + } + set specCompliantResponseRedirect(value: boolean) { + $.utils.setBit(23, value, this); + } + /** +* Experimental, do not use. +* This is a catch-all compatibility flag for experimental development within workerd +* that is not covered by another more-specific compatibility flag. It is the intention +* of this flag to always have the $experimental attribute. +* This is intended to guard new features that do not introduce any backwards-compatibility +* concerns (e.g. they only add a new API), and therefore do not need a compat flag of their own +* in the long term, but where we still want to guard access to the feature while it is in +* development. Don't use this for backwards-incompatible changes; give them their own flag. +* WARNING: Any feature blocked by this flag is subject to change at any time, including +* removal. Do not ignore this warning. +* +*/ + get workerdExperimental(): boolean { + return $.utils.getBit(24, this); + } + set workerdExperimental(value: boolean) { + $.utils.setBit(24, value, this); + } + /** +* Experimental, allows getting a durable object stub that ensures the object already exists. +* This is currently a work in progress mechanism that is not yet available for use in workerd. +* +*/ + get durableObjectGetExisting(): boolean { + return $.utils.getBit(25, this); + } + set durableObjectGetExisting(value: boolean) { + $.utils.setBit(25, value, this); + } + /** +* Enables the new headers.getSetCookie() API and the corresponding changes in behavior for +* the Header objects keys() and entries() iterators. +* +*/ + get httpHeadersGetSetCookie(): boolean { + return $.utils.getBit(26, this); + } + set httpHeadersGetSetCookie(value: boolean) { + $.utils.setBit(26, value, this); + } + /** +* Enables the tunneling of exceptions from a dynamic dispatch callee back into the caller. +* Previously any uncaught exception in the callee would be returned to the caller as an empty +* HTTP 500 response. +* +*/ + get dispatchExceptionTunneling(): boolean { + return $.utils.getBit(27, this); + } + set dispatchExceptionTunneling(value: boolean) { + $.utils.setBit(27, value, this); + } + /** +* Allows service bindings to call additional event handler methods on the target Worker. +* Initially only includes support for calling the queue() handler. +* WARNING: this flag exposes the V8 deserialiser to users via `Fetcher#queue()` `serializedBody`. +* Historically, this has required a trusted environment to be safe. If we decide to make this +* flag non-experimental, we must ensure we take appropriate precuations. +* +*/ + get serviceBindingExtraHandlers(): boolean { + return $.utils.getBit(28, this); + } + set serviceBindingExtraHandlers(value: boolean) { + $.utils.setBit(28, value, this); + } + /** +* This one operates a bit backwards. With the flag *enabled* no default cfBotManagement +* data will be included. The the flag *disable*, default cfBotManagement data will be +* included in the request.cf if the field is not present. +* +*/ + get noCfBotManagementDefault(): boolean { + return $.utils.getBit(29, this); + } + set noCfBotManagementDefault(value: boolean) { + $.utils.setBit(29, value, this); + } + /** +* When enabled, the delete() and has() methods of the standard URLSearchParams object +* (see url-standard.h) will have the recently added second value argument enabled. +* +*/ + get urlSearchParamsDeleteHasValueArg(): boolean { + return $.utils.getBit(30, this); + } + set urlSearchParamsDeleteHasValueArg(value: boolean) { + $.utils.setBit(30, value, this); + } + /** +* Perform additional error checking in the Web Compression API and throw an error if a +* DecompressionStream has trailing data or gets closed before the full compressed data has been +* provided. +* +*/ + get strictCompression(): boolean { + return $.utils.getBit(31, this); + } + set strictCompression(value: boolean) { + $.utils.setBit(31, value, this); + } + /** +* Enables compression/decompression support for the brotli compression algorithm. +* With the flag enabled workerd will support the "br" content encoding in the Request and +* Response APIs and compress or decompress data accordingly as with gzip. +* +*/ + get brotliContentEncoding(): boolean { + return $.utils.getBit(32, this); + } + set brotliContentEncoding(value: boolean) { + $.utils.setBit(32, value, this); + } + /** +* Perform additional error checking in the Web Crypto API to conform with the specification as +* well as reject key parameters that may be unsafe based on key length or public exponent. +* +*/ + get strictCrypto(): boolean { + return $.utils.getBit(33, this); + } + set strictCrypto(value: boolean) { + $.utils.setBit(33, value, this); + } + /** +* Enables the `workerd:rtti` module for querying runtime-type-information from JavaScript. +* +*/ + get rttiApi(): boolean { + return $.utils.getBit(34, this); + } + set rttiApi(value: boolean) { + $.utils.setBit(34, value, this); + } + /** +* The experimental webgpu API was removed. +* +*/ + get obsolete35(): boolean { + return $.utils.getBit(35, this); + } + set obsolete35(value: boolean) { + $.utils.setBit(35, value, this); + } + /** +* In the WebCrypto API, the `publicExponent` field of the algorithm of RSA keys would previously +* be an ArrayBuffer. Using this flag, publicExponent is a Uint8Array as mandated by the +* specification. +* +*/ + get cryptoPreservePublicExponent(): boolean { + return $.utils.getBit(36, this); + } + set cryptoPreservePublicExponent(value: boolean) { + $.utils.setBit(36, value, this); + } + /** +* Vectorize query option change to allow returning of metadata to be optional. Accompanying this: +* a return format change to move away from a nested object with the VectorizeVector. +* +*/ + get vectorizeQueryMetadataOptional(): boolean { + return $.utils.getBit(37, this); + } + set vectorizeQueryMetadataOptional(value: boolean) { + $.utils.setBit(37, value, this); + } + /** +* Enables the `workerd:unsafe` module for performing dangerous operations from JavaScript. +* Intended for local development and testing use cases. Currently just supports aborting all +* Durable Objects running in a `workerd` process. +* +*/ + get unsafeModule(): boolean { + return $.utils.getBit(38, this); + } + set unsafeModule(value: boolean) { + $.utils.setBit(38, value, this); + } + /** +* Enables JS RPC on the server side for Durable Object classes that do not explicitly extend +* `DurableObjects`. +* +* This flag is obsolete but supported temporarily to avoid breaking people who used it. All code +* should switch to using `extends DurableObject` as the way to enable RPC. +* +* As of this writing, it is still necessary to enable the general `experimental` flag to use RPC +* on both the client and server sides. +* +*/ + get jsRpc(): boolean { + return $.utils.getBit(39, this); + } + set jsRpc(value: boolean) { + $.utils.setBit(39, value, this); + } + /** +* Removes the non-implemented importScripts() function from the global scope. +* +*/ + get noImportScripts(): boolean { + return $.utils.getBit(40, this); + } + set noImportScripts(value: boolean) { + $.utils.setBit(40, value, this); + } + /** +* Enables the availability of the Node.js AsyncLocalStorage API independently of the full +* node.js compatibility option. +* +*/ + get nodeJsAls(): boolean { + return $.utils.getBit(41, this); + } + set nodeJsAls(value: boolean) { + $.utils.setBit(41, value, this); + } + /** +* Queues bindings serialize messages to JSON format by default (the previous default was v8 format) +* +*/ + get queuesJsonMessages(): boolean { + return $.utils.getBit(42, this); + } + set queuesJsonMessages(value: boolean) { + $.utils.setBit(42, value, this); + } + /** +* Enables Python Workers. Access to this flag is not restricted, instead bundles containing +* Python modules are restricted in EWC. +* +* WARNING: Python Workers are still an experimental feature and thus subject to change. +* +*/ + get pythonWorkers(): boolean { + return $.utils.getBit(43, this); + } + set pythonWorkers(value: boolean) { + $.utils.setBit(43, value, this); + } + /** +* Historically, the `Fetcher` type -- which is the type of Service Bindings, and also the parent +* type of Durable Object stubs -- had special methods `get()`, `put()`, and `delete()`, which +* were shortcuts for calling `fetch()` with the corresponding HTTP method. These methods were +* never documented. +* +* To make room for people to define their own RPC methods with these names, this compat flag +* makes them no longer defined. +* +*/ + get fetcherNoGetPutDelete(): boolean { + return $.utils.getBit(44, this); + } + set fetcherNoGetPutDelete(value: boolean) { + $.utils.setBit(44, value, this); + } + get unwrapCustomThenables(): boolean { + return $.utils.getBit(45, this); + } + set unwrapCustomThenables(value: boolean) { + $.utils.setBit(45, value, this); + } + /** +* Whether the type `Fetcher` type -- which is the type of Service Bindings, and also the parent +* type of Durable Object stubs -- support RPC. If so, this type will have a wildcard method, so +* it will appear that all possible property names are present on any fetcher instance. This could +* break code that tries to infer types based on the presence or absence of methods. +* +*/ + get fetcherRpc(): boolean { + return $.utils.getBit(46, this); + } + set fetcherRpc(value: boolean) { + $.utils.setBit(46, value, this); + } + /** +* Sadly, the original implementation of ReadableStream (now called "internal" streams), did not +* properly implement the result of ReadableStreamBYOBReader's read method. When done = true, +* per the spec, the result `value` must be an empty ArrayBufferView whose underlying ArrayBuffer +* is the same as the one passed to the read method. Our original implementation returned +* undefined instead. This flag changes the behavior to match the spec and to match the behavior +* implemented by the JS-backed ReadableStream implementation. +* +*/ + get internalStreamByobReturn(): boolean { + return $.utils.getBit(47, this); + } + set internalStreamByobReturn(value: boolean) { + $.utils.setBit(47, value, this); + } + /** +* The original implementation of the Blob mime type normalization when extracting a blob +* from the Request or Response body is not compliant with the standard. Unfortunately, +* making it compliant is a breaking change. This flag controls the availability of the +* new spec-compliant Blob mime type normalization. +* +*/ + get blobStandardMimeType(): boolean { + return $.utils.getBit(48, this); + } + set blobStandardMimeType(value: boolean) { + $.utils.setBit(48, value, this); + } + /** +* Ensures that WHATWG standard URL parsing is used in the fetch API implementation. +* +*/ + get fetchStandardUrl(): boolean { + return $.utils.getBit(49, this); + } + set fetchStandardUrl(value: boolean) { + $.utils.setBit(49, value, this); + } + /** +* Implies nodeJSCompat with the following additional modifications: +* * Node.js Compat built-ins may be imported/required with or without the node: prefix +* * Node.js Compat the globals Buffer and process are available everywhere +* +*/ + get nodeJsCompatV2(): boolean { + return $.utils.getBit(50, this); + } + set nodeJsCompatV2(value: boolean) { + $.utils.setBit(50, value, this); + } + /** +* Controls what happens when a Worker hosted on Cloudflare uses the global `fetch()` function to +* request a hostname that is within the Worker's own Cloudflare zone (domain). +* +* Historically, such requests would be routed to the zone's origin server, ignoring any Workers +* mapped to the URL and also bypassing Cloudflare security settings. This behavior made sense +* when Workers was first introduced as a way to rewrite requests before passing them along to +* the origin, and bindings didn't exist: the only way to forward the request to origin was to +* use global fetch(), and if it didn't bypass Workers, you'd end up looping back to the same +* Worker. +* +* However, this behavior has a problem: it opens the door for SSRF attacks. Imagine a Worker is +* designed to fetch a resource from a user-provided URL. An attacker could provide a URL that +* points back to the Worker's own zone, and possibly cause the Worker to fetch a resource from +* origin that isn't meant to be reachable by the public. +* +* Traditionally, this kind of attack is considered a bug in the application: an application that +* fetches untrusted URLs must verify that the URL doesn't refer to a private resource that only +* the application itself is meant to access. However, applications can easily get this wrong. +* Meanwhile, by using bindings, we can make this class of problem go away. +* +* When global_fetch_strictly_public is enabled, the global `fetch()` function (when invoked on +* Cloudflare Workers) will strictly route requests as if they were made on the public internet. +* Thus, requests to a Worker's own zone will loop back to the "front door" of Cloudflare and +* will be treated like a request from the internet, possibly even looping back to the same Worker +* again. If an application wishes to send requests to its origin, it must configure an "origin +* binding". An origin binding behaves like a service binding (it has a `fetch()` method) but +* sends requests to the zone's origin servers, bypassing Cloudflare. E.g. the Worker would write +* `env.ORIGIN.fetch(req)` to send a request to its origin. +* +* Note: This flag only impacts behavior on Cloudflare. It has no effect when using workerd. +* Under workerd, the config file can control where global `fetch()` goes by configuring the +* worker's `globalOutbound` implicit binding. By default, under workerd, global `fetch()` has +* always been configured to accept publicly-routable internet hosts only; hostnames which map +* to private IP addresses (as defined in e.g. RFC 1918) will be rejected. Thus, workerd has +* always been SSRF-safe by default. +* +*/ + get globalFetchStrictlyPublic(): boolean { + return $.utils.getBit(51, this); + } + set globalFetchStrictlyPublic(value: boolean) { + $.utils.setBit(51, value, this); + } + /** +* Enables of the new module registry implementation. +* +*/ + get newModuleRegistry(): boolean { + return $.utils.getBit(52, this); + } + set newModuleRegistry(value: boolean) { + $.utils.setBit(52, value, this); + } + /** +* Enables the use of no-store headers from requests +* +*/ + get cacheOptionEnabled(): boolean { + return $.utils.getBit(53, this); + } + set cacheOptionEnabled(value: boolean) { + $.utils.setBit(53, value, this); + } + /** +* Enables bypassing FL by translating pipeline tunnel configuration to subpipeline. +* This flag is used only by the internal repo and not directly by workerd. +* +*/ + get kvDirectBinding(): boolean { + return $.utils.getBit(54, this); + } + set kvDirectBinding(value: boolean) { + $.utils.setBit(54, value, this); + } + /** +* Enables fetching hosts with a custom port from workers. +* For orange clouded sites only standard ports are allowed (https://developers.cloudflare.com/fundamentals/reference/network-ports/#network-ports-compatible-with-cloudflares-proxy). +* For grey clouded sites all ports are allowed. +* +*/ + get allowCustomPorts(): boolean { + return $.utils.getBit(55, this); + } + set allowCustomPorts(value: boolean) { + $.utils.setBit(55, value, this); + } + /** +* For local development purposes only, increase the message size limit to 128MB. +* This is not expected ever to be made available in production, as large messages are inefficient. +* +*/ + get increaseWebsocketMessageSize(): boolean { + return $.utils.getBit(56, this); + } + set increaseWebsocketMessageSize(value: boolean) { + $.utils.setBit(56, value, this); + } + /** +* When using the original WritableStream implementation ("internal" streams), the +* abort() operation would be handled lazily, meaning that the queue of pending writes +* would not be cleared until the next time the queue was processed. This behavior leads +* to a situtation where the stream can hang if the consumer stops consuming. When set, +* this flag changes the behavior to clear the queue immediately upon abort. +* +*/ + get internalWritableStreamAbortClearsQueue(): boolean { + return $.utils.getBit(57, this); + } + set internalWritableStreamAbortClearsQueue(value: boolean) { + $.utils.setBit(57, value, this); + } + /** +* Enables Python Workers and uses the bundle from the Pyodide source directory directly. For testing only. +* +* Note that the baseline snapshot hash here refers to the one used in +* `baseline-from-gcs.ew-test-bin.c++`. We don't intend to ever load it in production. +* +*/ + get pythonWorkersDevPyodide(): boolean { + return $.utils.getBit(58, this); + } + set pythonWorkersDevPyodide(value: boolean) { + $.utils.setBit(58, value, this); + } + /** +* Enables node:zlib implementation while it is in-development. +* Once the node:zlib implementation is complete, this will be automatically enabled when +* nodejs_compat or nodejs_compat_v2 are enabled. +* +*/ + get nodeJsZlib(): boolean { + return $.utils.getBit(59, this); + } + set nodeJsZlib(value: boolean) { + $.utils.setBit(59, value, this); + } + /** +* Enables routing to a replica on the client-side. +* Doesn't mean requests *will* be routed to a replica, only that they can be. +* +*/ + get replicaRouting(): boolean { + return $.utils.getBit(60, this); + } + set replicaRouting(value: boolean) { + $.utils.setBit(60, value, this); + } + /** +* Was used to enable the withSession(bookmarkOrConstraint) method that allows users +* to use read-replication Sessions API for D1. This is now enabled for everyone. +* +*/ + get obsolete61(): boolean { + return $.utils.getBit(61, this); + } + set obsolete61(value: boolean) { + $.utils.setBit(61, value, this); + } + /** +* Historically, it has been possible to resolve a promise from an incorrect request +* IoContext. This leads to issues with promise continuations being scheduled to run +* in the wrong IoContext leading to errors and difficult to diagnose bugs. With this +* compatibility flag we arrange to have such promise continuations scheduled to run +* in the correct IoContext if it is still alive, or dropped on the floor with a warning +* if the correct IoContext is not still alive. +* +*/ + get handleCrossRequestPromiseResolution(): boolean { + return $.utils.getBit(62, this); + } + set handleCrossRequestPromiseResolution(value: boolean) { + $.utils.setBit(62, value, this); + } + get obsolete63(): boolean { + return $.utils.getBit(63, this); + } + set obsolete63(value: boolean) { + $.utils.setBit(63, value, this); + } + /** +* A change was made that set the Symbol.toStringTag on all jsg::Objects in order to +* fix several spec compliance bugs. Unfortunately it turns out that was more breaking +* than expected. This flag restores the original behavior for compat dates before +* 2024-09-26 +* +*/ + get setToStringTag(): boolean { + return $.utils.getBit(64, this); + } + set setToStringTag(value: boolean) { + $.utils.setBit(64, value, this); + } + /** +* HTTP methods are expected to be upper-cased. Per the fetch spec, if the methods +* is specified as `get`, `post`, `put`, `delete`, `head`, or `options`, implementations +* are expected to uppercase the method. All other method names would generally be +* expected to throw as unrecognized (e.g. `patch` would be an error while `PATCH` is +* accepted). This is a bit restrictive, even if it is in the spec. This flag modifies +* the behavior to uppercase all methods prior to parsing to that the method is always +* recognized if it is a known method. +* +*/ + get upperCaseAllHttpMethods(): boolean { + return $.utils.getBit(65, this); + } + set upperCaseAllHttpMethods(value: boolean) { + $.utils.setBit(65, value, this); + } + get obsolete66(): boolean { + return $.utils.getBit(66, this); + } + set obsolete66(value: boolean) { + $.utils.setBit(66, value, this); + } + /** +* When enabled, use of top-level await syntax in require() calls will be disallowed. +* The ecosystem and runtimes are moving to a state where top level await in modules +* is being strongly discouraged. +* +*/ + get noTopLevelAwaitInRequire(): boolean { + return $.utils.getBit(67, this); + } + set noTopLevelAwaitInRequire(value: boolean) { + $.utils.setBit(67, value, this); + } + /** +* A bug in the original implementation of TransformStream failed to apply backpressure +* correctly. The fix, however, can break existing implementations that don't account +* for the bug so we need to put the fix behind a compat flag. +* +*/ + get fixupTransformStreamBackpressure(): boolean { + return $.utils.getBit(68, this); + } + set fixupTransformStreamBackpressure(value: boolean) { + $.utils.setBit(68, value, this); + } + get obsolete69(): boolean { + return $.utils.getBit(69, this); + } + set obsolete69(value: boolean) { + $.utils.setBit(69, value, this); + } + /** +* Enables the use of cache: no-cache in the fetch api. +* +*/ + get cacheNoCache(): boolean { + return $.utils.getBit(70, this); + } + set cacheNoCache(value: boolean) { + $.utils.setBit(70, value, this); + } + get pythonWorkers20250116(): boolean { + return $.utils.getBit(71, this); + } + set pythonWorkers20250116(value: boolean) { + $.utils.setBit(71, value, this); + } + /** +* Enables cache settings specified request in fetch api cf object to override cache rules. (only for user owned or grey-clouded sites) +* +*/ + get requestCfOverridesCacheRules(): boolean { + return $.utils.getBit(72, this); + } + set requestCfOverridesCacheRules(value: boolean) { + $.utils.setBit(72, value, this); + } + /** +* Enables delete operations on memory cache if enabled. +* +*/ + get memoryCacheDelete(): boolean { + return $.utils.getBit(73, this); + } + set memoryCacheDelete(value: boolean) { + $.utils.setBit(73, value, this); + } + /** +* Creates a unique ExportedHandler for each call to `export default` thus allowing a unique ctx +* per invocation. +* +* OBSOLETE: We decided to apply this change even to old workers as we've found some old workers +* that assumed this behavior all along, and so this change actually fixes bugs for them. +* It seems unlikely to break anyone because there's no way a Worker could depend on any two +* requests hitting the same isolate, so how could they depend on any two requests having the +* same `ctx` object? At worst they might store some sort of cache on it which becomes +* ineffective, but their Worker would still work, and anyway this would be a very weird thing +* for someone to do. +* +*/ + get obsolete74(): boolean { + return $.utils.getBit(74, this); + } + set obsolete74(value: boolean) { + $.utils.setBit(74, value, this); + } + /** +* Just in case someone somewhere somehow actually relied on every event receiving the same `ctx` +* object, this restores the original behavior. We do not recommend this. +* +*/ + get reuseCtxAcrossNonclassEvents(): boolean { + return $.utils.getBit(92, this); + } + set reuseCtxAcrossNonclassEvents(value: boolean) { + $.utils.setBit(92, value, this); + } + /** +* If enabled, does not require all waitUntil'ed promises to resolve successfully before reporting +* succeeded/failed messages/batches back from a queue consumer to the Queues service. This +* prevents a slow waitUntil'ed promise from slowing down consumption of messages from a queue, +* which has been a recurring problem for the prior behavior (which did wait for all waitUntil'ed +* tasks to complete. +* This intentionally doesn't have a compatEnableDate yet until so we can let some users opt-in to +* try it before enabling it for all new scripts, but will eventually need one. +* +*/ + get queueConsumerNoWaitForWaitUntil(): boolean { + return $.utils.getBit(75, this); + } + set queueConsumerNoWaitForWaitUntil(value: boolean) { + $.utils.setBit(75, value, this); + } + /** +* Automatically populate process.env from text bindings only +* when nodejs_compat is being used. +* +*/ + get populateProcessEnv(): boolean { + return $.utils.getBit(76, this); + } + set populateProcessEnv(value: boolean) { + $.utils.setBit(76, value, this); + } + /** +* Enables cache settings specified request in cache api cf object to override cache rules. (only for user owned or grey-clouded sites) +* +*/ + get cacheApiRequestCfOverridesCacheRules(): boolean { + return $.utils.getBit(77, this); + } + set cacheApiRequestCfOverridesCacheRules(value: boolean) { + $.utils.setBit(77, value, this); + } + /** +* When allowed, `import { env, exports } from 'cloudflare:workers'` will provide access +* to the per-request environment/bindings. This flag also disables importable exports +* (the exports proxy) since both features are conceptually related. +* +*/ + get disableImportableEnv(): boolean { + return $.utils.getBit(78, this); + } + set disableImportableEnv(value: boolean) { + $.utils.setBit(78, value, this); + } + /** +* Enables routing to asset-worker over a user worker when an appropriate +* `assets.not_found_handling` configuration option is set and `Sec-Fetch-Mode: navigate` header +* is present. This flag is used only by @cloudflare/workers-shared (within workers-sdk) and not +* directly by workerd. +* +*/ + get assetsSecFetchModeNavigateHeaderPrefersAssetServing(): boolean { + return $.utils.getBit(79, this); + } + set assetsSecFetchModeNavigateHeaderPrefersAssetServing(value: boolean) { + $.utils.setBit(79, value, this); + } + /** +* when enabled, exports compability flags for FL to Cache API requests. +* +*/ + get cacheApiCompatFlags(): boolean { + return $.utils.getBit(80, this); + } + set cacheApiCompatFlags(value: boolean) { + $.utils.setBit(80, value, this); + } + /** +* when enabled, enables Durable Object support for Python Workers. +* +*/ + get obsolete81(): boolean { + return $.utils.getBit(81, this); + } + set obsolete81(value: boolean) { + $.utils.setBit(81, value, this); + } + /** +* Obsolete flag. Has no effect. +* +*/ + get obsolete82(): boolean { + return $.utils.getBit(82, this); + } + set obsolete82(value: boolean) { + $.utils.setBit(82, value, this); + } + /** +* The original URLPattern implementation is not compliant with the +* WHATWG URLPattern Standard, leading to a number of issues reported by users. Unfortunately, +* making it spec compliant is a breaking change. This flag controls the availability +* of the new spec-compliant URLPattern implementation. +* +*/ + get specCompliantUrlpattern(): boolean { + return $.utils.getBit(83, this); + } + set specCompliantUrlpattern(value: boolean) { + $.utils.setBit(83, value, this); + } + /** +* Enables WeakRefs and FinalizationRegistry API. +* WebAssembly based projects often rely on this API for wasm memory cleanup +* +*/ + get jsWeakRef(): boolean { + return $.utils.getBit(84, this); + } + set jsWeakRef(value: boolean) { + $.utils.setBit(84, value, this); + } + /** +* When enabled, the AbortSignal of the incoming request is not passed through to subrequests. +* As a result, outgoing subrequests will not be cancelled when the incoming request is. +* +*/ + get requestSignalPassthrough(): boolean { + return $.utils.getBit(85, this); + } + set requestSignalPassthrough(value: boolean) { + $.utils.setBit(85, value, this); + } + /** +* Enables Navigator.language API. +* +*/ + get enableNavigatorLanguage(): boolean { + return $.utils.getBit(86, this); + } + set enableNavigatorLanguage(value: boolean) { + $.utils.setBit(86, value, this); + } + /** +* Enables the experimental Web File System API. +* WARNING: This API is still in development and may change or be removed in the future. +* +*/ + get webFileSystem(): boolean { + return $.utils.getBit(87, this); + } + set webFileSystem(value: boolean) { + $.utils.setBit(87, value, this); + } + /** +* Enables experimental support for passing AbortSignal over RPC. +* +*/ + get abortSignalRpc(): boolean { + return $.utils.getBit(88, this); + } + set abortSignalRpc(value: boolean) { + $.utils.setBit(88, value, this); + } + /** +* Enables eval() and new Function() during startup. +* +*/ + get allowEvalDuringStartup(): boolean { + return $.utils.getBit(89, this); + } + set allowEvalDuringStartup(value: boolean) { + $.utils.setBit(89, value, this); + } + /** +* Enables Request.signal for incoming requests. +* This feature is still experimental and the compat flag has no default enable date. +* +*/ + get enableRequestSignal(): boolean { + return $.utils.getBit(90, this); + } + set enableRequestSignal(value: boolean) { + $.utils.setBit(90, value, this); + } + /** +* Causes the Worker to handle incoming connect events by simply passing them through to the +* Worker's globalOutbound (typically, the internet). +* +* As of this writing, Workers cannot yet receive raw socket connections, because no API has been +* defined for doing so. But a Worker can be configured to be the `globalOutbound` for another +* Worker, causing the first Worker to intercept all outbound network requests that the second +* Worker makes by calling `fetch()` or `connect()`. Since there's no way for the first Worker +* to actually handle the `connect()` requests, this implies the second Worker cannot make any +* raw TCP connections in this configuration. This is intended: often, the first Worker +* implements some sort of security rules governing what kinds of requests the second Worker can +* send to the internet, and if `connect()` requests were allowed to go directly to the internet, +* that could be used to bypass said security checks. +* +* However, sometimes outbound workers are used for reasons other than security, and in fact the +* outbound Worker does not really care to block `connect()` requests. Until such a time as we +* create an actual API for proxying connections, such outbound workers can set this compat flag +* to opt into allowing connect requests to pass through. +* +*/ + get connectPassThrough(): boolean { + return $.utils.getBit(91, this); + } + set connectPassThrough(value: boolean) { + $.utils.setBit(91, value, this); + } + /** +* The AsyncLocalStorage frame can capture values that are bound to the +* current IoContext. This is not always in the users control since we use +* the ALS storage frame to propagate internal trace spans as well as +* user-provided values. This flag, when set, binds the snapshot / bound +* functions to the current IoContext and will throw an error if the bound +* functions are called outside of the IoContext in which they were created. +* +*/ + get bindAsyncLocalStorageSnapshot(): boolean { + return $.utils.getBit(93, this); + } + set bindAsyncLocalStorageSnapshot(value: boolean) { + $.utils.setBit(93, value, this); + } + /** +* In the original module registry implementation, import attributes that are not recognized +* would be ignored. This is not compliant with the spec which strongly recommends that runtimes +* throw an error when unknown import attributes are encountered. In the new module registry +* implementation the recommended behavior is what is implemented. With this compat flag +* enabled, the original module registry implementation will follow the recommended behavior. +* +*/ + get throwOnUnrecognizedImportAssertion(): boolean { + return $.utils.getBit(94, this); + } + set throwOnUnrecognizedImportAssertion(value: boolean) { + $.utils.setBit(94, value, this); + } + /** +* Enables support for Python workflows. +* This is still in development and may change in the future. +* +*/ + get pythonWorkflows(): boolean { + return $.utils.getBit(95, this); + } + set pythonWorkflows(value: boolean) { + $.utils.setBit(95, value, this); + } + /** +* By default, Workerd will always expose "linux" as the process.platform. +* This flag enables support for process.platform to expose the actual system platform. +* This is unsupported, as this feature will never ever be supported as non-experimental and is a +* temporary WPT test path only. +* +*/ + get unsupportedProcessActualPlatform(): boolean { + return $.utils.getBit(96, this); + } + set unsupportedProcessActualPlatform(value: boolean) { + $.utils.setBit(96, value, this); + } + /** +* Switches from the partial process implementation with only "nextTick", "env", "exit", +* "getBuiltinModule", "platform" and "features" property implementations, to the full-featured +* Node.js-compatibile process implementation with all process properties either stubbed or +* implemented. It is required to use this flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsProcessV2(): boolean { + return $.utils.getBit(97, this); + } + set enableNodeJsProcessV2(value: boolean) { + $.utils.setBit(97, value, this); + } + /** +* The original implementation of EventTarget was not correctly setting the `this` value +* for event handlers. This flag enables the correct behavior, which is compliant with the spec. +* +*/ + get setEventTargetThis(): boolean { + return $.utils.getBit(98, this); + } + set setEventTargetThis(value: boolean) { + $.utils.setBit(98, value, this); + } + /** +* The original version of the headers sent to edgeworker were truncated to a single +* value for specific header names, such as To and Cc. With this compat flag we will send +* the full header values to the worker script. +* +*/ + get enableForwardableEmailFullHeaders(): boolean { + return $.utils.getBit(99, this); + } + set enableForwardableEmailFullHeaders(value: boolean) { + $.utils.setBit(99, value, this); + } + /** +* Enables Node.js http related modules such as node:http and node:https +* +*/ + get enableNodejsHttpModules(): boolean { + return $.utils.getBit(100, this); + } + set enableNodejsHttpModules(value: boolean) { + $.utils.setBit(100, value, this); + } + /** +* Enables a "pedantic mode" for WPT compliance. Multiple changes are grouped under +* this flag that are known to be required to pass more web platform tests but which +* otherwise are likely not to be strictly necessary for most users. +* +*/ + get pedanticWpt(): boolean { + return $.utils.getBit(101, this); + } + set pedanticWpt(value: boolean) { + $.utils.setBit(101, value, this); + } + /** +* Enables exposure of the MessagePort and MessageChannel classes on the global scope. +* +*/ + get exposeGlobalMessageChannel(): boolean { + return $.utils.getBit(102, this); + } + set exposeGlobalMessageChannel(value: boolean) { + $.utils.setBit(102, value, this); + } + /** +* Enables Node.js http server related modules such as node:_http_server +* It is required to use this flag with `enable_nodejs_http_modules` since +* it enables the usage of http related node.js modules, and this flag enables +* the methods exposed by the node.js http modules. +* Regarding the recommendation for using import { env, waitUntil } from 'cloudflare:workers'; +* `disallow_importable_env` compat flag should not be set if you are using this +* and need access to the env since that will prevent access. +* +*/ + get enableNodejsHttpServerModules(): boolean { + return $.utils.getBit(103, this); + } + set enableNodejsHttpServerModules(value: boolean) { + $.utils.setBit(103, value, this); + } + /** +* Disables the global handlers for Python workers and enforces their use via default entrypoint +* classes. +* +*/ + get pythonNoGlobalHandlers(): boolean { + return $.utils.getBit(104, this); + } + set pythonNoGlobalHandlers(value: boolean) { + $.utils.setBit(104, value, this); + } + /** +* Enables the Node.js fs module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsFsModule(): boolean { + return $.utils.getBit(105, this); + } + set enableNodeJsFsModule(value: boolean) { + $.utils.setBit(105, value, this); + } + /** +* Enables the Node.js os module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsOsModule(): boolean { + return $.utils.getBit(106, this); + } + set enableNodeJsOsModule(value: boolean) { + $.utils.setBit(106, value, this); + } + /** +* Disables adding `/session/metadata/vendor` to the Python Worker's sys.path. So Workers using +* this flag will have to place their vendored modules in a `python_modules` directory. +* +*/ + get pythonWorkersForceNewVendorPath(): boolean { + return $.utils.getBit(107, this); + } + set pythonWorkersForceNewVendorPath(value: boolean) { + $.utils.setBit(107, value, this); + } + /** +* Removes the Node.js compatibility layer for EOL versions of Node.js. +* When the flag is enabled, APIs that have reached End-of-Life in Node.js +* will be removed for workers. When disabled, the APIs are present (but +* might still be non-functional stubs) +* This flag is intended to be a roll-up flag. That is, as additional APIs +* reach EOL, new compat flags will be added for those that will have +* `impliedByAfterDate(name = "removeNodeJsCompatEOL", ...` annotations. +* +*/ + get removeNodejsCompatEOL(): boolean { + return $.utils.getBit(108, this); + } + set removeNodejsCompatEOL(value: boolean) { + $.utils.setBit(108, value, this); + } + /** +* This flag enables additional checks in the control plane to validate that workflows are +* defined and used correctly +* +*/ + get enableWorkflowScriptValidation(): boolean { + return $.utils.getBit(109, this); + } + set enableWorkflowScriptValidation(value: boolean) { + $.utils.setBit(109, value, this); + } + /** +* Enables the generation of dedicated snapshots on Python Worker upload. The snapshot will be +* stored inside the resulting WorkerBundle of the Worker. The snapshot will be taken after the +* top-level execution of the Worker. +* +*/ + get pythonDedicatedSnapshot(): boolean { + return $.utils.getBit(110, this); + } + set pythonDedicatedSnapshot(value: boolean) { + $.utils.setBit(110, value, this); + } + /** +* Strips all Typescript types from loaded files. +* If loaded files contain unsupported typescript construct beyond type annotations (e.g. enums), +* or is not a syntactically valid Typescript, the worker will fail to load. +* +*/ + get typescriptStripTypes(): boolean { + return $.utils.getBit(111, this); + } + set typescriptStripTypes(value: boolean) { + $.utils.setBit(111, value, this); + } + /** +* Enables the Node.js http2 module stubs. +* +*/ + get enableNodeJsHttp2Module(): boolean { + return $.utils.getBit(112, this); + } + set enableNodeJsHttp2Module(value: boolean) { + $.utils.setBit(112, value, this); + } + /** +* Enables eval() and new Function() always, even during request handling. +* ***This flag should *never* be enabled by default.*** +* The name of the enable flag is intentionally long and scary-sounding to +* discourage casual use. +* * "insecure" because code-gen during request handling can lead to security issues. +* * "inefficient" because repeated code-gen during request handling can be slow. +* * "logged" because each use would likely be logged in production for security +* auditing so users should avoid including PII and other sensitive data in dynamically +* generated and evaluated code. +* This flag is experimental and may be removed in the future. It is added for +* testing purposes. +* +*/ + get experimentalAllowEvalAlways(): boolean { + return $.utils.getBit(113, this); + } + set experimentalAllowEvalAlways(value: boolean) { + $.utils.setBit(113, value, this); + } + /** +* This flag specifies that when automatic redirects are enabled, and a redirect points to a URL +* at a different origin, if the original request contained an Authorization header, that header +* is removed before following the redirect. This behavior is required by the current version of +* the Fetch API specification. +* +* This requirement was added to the Fetch spec in 2022, well after Cloudflare Workers +* originally implemented it. Hence, Workers did not originally implement this requirement. This +* requirement is backwards-incompatible, and so the new behavior is guarded by a compatibility +* flag. +* +* Note that the old behavior was not inherently insecure, and indeed could be desirable in many +* circumstances. For example, if an API that requires authorization wishes to change its +* hostname, it might wish to redirect to the new hostname while having the client send along +* their credentials. Under the new fetch behavior, such a redirect will break clients, and this +* has legitimately broken real use cases. However, it's true that the old behavior could be a +* "gotcha" leading to security problems when combined with other mistakes. Hence, the spec was +* changed, and Workers must follow the spec. +* +*/ + get stripAuthorizationOnCrossOriginRedirect(): boolean { + return $.utils.getBit(114, this); + } + set stripAuthorizationOnCrossOriginRedirect(value: boolean) { + $.utils.setBit(114, value, this); + } + /** +* Enables enhanced error serialization for errors serialized using structuredClone / +* v8 serialization. More error types are supported, and own properties are included. +* Note that when enabled, deserialization of the errors will not preserve the original +* stack by default. +* +*/ + get enhancedErrorSerialization(): boolean { + return $.utils.getBit(115, this); + } + set enhancedErrorSerialization(value: boolean) { + $.utils.setBit(115, value, this); + } + /** +* Enables Queuing on the `.send(message: EmailMessage)` function on send_email binding if there's +* a temporary error on email delivery. +* Note that by enabling this, user-provided Message-IDs are stripped and +* Email Workers will generate and use its own. +* +*/ + get emailSendingQueuing(): boolean { + return $.utils.getBit(116, this); + } + set emailSendingQueuing(value: boolean) { + $.utils.setBit(116, value, this); + } + /** +* Removes APIs that reached end-of-life in Node.js 22.x. When using the +* removeNodejsCompatEOL flag, this will default enable on/after 2027-04-30. +* +*/ + get removeNodejsCompatEOLv22(): boolean { + return $.utils.getBit(117, this); + } + set removeNodejsCompatEOLv22(value: boolean) { + $.utils.setBit(117, value, this); + } + /** +* Removes APIs that reached end-of-life in Node.js 23.x. This will default +* enable when the removeNodejsCompatEOLv24 flag is enabled after 2025-09-01. +* Went EOL on 2025-06-01 +* +*/ + get removeNodejsCompatEOLv23(): boolean { + return $.utils.getBit(118, this); + } + set removeNodejsCompatEOLv23(value: boolean) { + $.utils.setBit(118, value, this); + } + /** +* Removes APIs that reached end-of-life in Node.js 24.x. When using the +* removeNodejsCompatEOL flag, this will default enable on/after 2028-04-30. +* +*/ + get removeNodejsCompatEOLv24(): boolean { + return $.utils.getBit(119, this); + } + set removeNodejsCompatEOLv24(value: boolean) { + $.utils.setBit(119, value, this); + } + /** +* Enables the Node.js console module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsConsoleModule(): boolean { + return $.utils.getBit(120, this); + } + set enableNodeJsConsoleModule(value: boolean) { + $.utils.setBit(120, value, this); + } + /** +* Enables the Node.js non-functional stub vm module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsVmModule(): boolean { + return $.utils.getBit(121, this); + } + set enableNodeJsVmModule(value: boolean) { + $.utils.setBit(121, value, this); + } + /** +* $impliedByAfterDate(name = "nodeJsCompat", date = "2025-10-01"); +* Enables the Node.js perf_hooks module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsPerfHooksModule(): boolean { + return $.utils.getBit(122, this); + } + set enableNodeJsPerfHooksModule(value: boolean) { + $.utils.setBit(122, value, this); + } + /** +* Enables PerformanceEntry, PerformanceMark, PerformanceMeasure, PerformanceResourceTiming, +* PerformanceObserver and PerformanceObserverEntryList global classes. +* +*/ + get enableGlobalPerformanceClasses(): boolean { + return $.utils.getBit(123, this); + } + set enableGlobalPerformanceClasses(value: boolean) { + $.utils.setBit(123, value, this); + } + /** +* Enables the Node.js non-functional stub domain module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsDomainModule(): boolean { + return $.utils.getBit(124, this); + } + set enableNodeJsDomainModule(value: boolean) { + $.utils.setBit(124, value, this); + } + /** +* $impliedByAfterDate(name = "nodeJsCompat", date = "2025-10-15"); +* Enables the Node.js non-functional stub v8 module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsV8Module(): boolean { + return $.utils.getBit(125, this); + } + set enableNodeJsV8Module(value: boolean) { + $.utils.setBit(125, value, this); + } + /** +* $impliedByAfterDate(name = "nodeJsCompat", date = "2025-10-15"); +* Enables the Node.js non-functional stub tty module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsTtyModule(): boolean { + return $.utils.getBit(126, this); + } + set enableNodeJsTtyModule(value: boolean) { + $.utils.setBit(126, value, this); + } + /** +* Enables the Node.js deprecated punycode module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsPunycodeModule(): boolean { + return $.utils.getBit(127, this); + } + set enableNodeJsPunycodeModule(value: boolean) { + $.utils.setBit(127, value, this); + } + /** +* Enables the Node.js non-functional stub cluster module. It is required to use this flag with +* nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsClusterModule(): boolean { + return $.utils.getBit(128, this); + } + set enableNodeJsClusterModule(value: boolean) { + $.utils.setBit(128, value, this); + } + /** +* Enables the Node.js non-functional stub child_process module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsChildProcessModule(): boolean { + return $.utils.getBit(129, this); + } + set enableNodeJsChildProcessModule(value: boolean) { + $.utils.setBit(129, value, this); + } + /** +* Enables the Node.js non-functional stub worker_threads module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsWorkerThreadsModule(): boolean { + return $.utils.getBit(130, this); + } + set enableNodeJsWorkerThreadsModule(value: boolean) { + $.utils.setBit(130, value, this); + } + /** +* Enables the Node.js non-functional stub _stream_wrap module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsStreamWrapModule(): boolean { + return $.utils.getBit(131, this); + } + set enableNodeJsStreamWrapModule(value: boolean) { + $.utils.setBit(131, value, this); + } + /** +* Enables the Node.js non-functional stub wasi module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsWasiModule(): boolean { + return $.utils.getBit(132, this); + } + set enableNodeJsWasiModule(value: boolean) { + $.utils.setBit(132, value, this); + } + /** +* Enables the Node.js non-functional stub dgram module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsDgramModule(): boolean { + return $.utils.getBit(133, this); + } + set enableNodeJsDgramModule(value: boolean) { + $.utils.setBit(133, value, this); + } + /** +* Enables the Node.js non-functional stub inspector module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsInspectorModule(): boolean { + return $.utils.getBit(134, this); + } + set enableNodeJsInspectorModule(value: boolean) { + $.utils.setBit(134, value, this); + } + /** +* Enables the Node.js non-functional stub trace_events module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsTraceEventsModule(): boolean { + return $.utils.getBit(135, this); + } + set enableNodeJsTraceEventsModule(value: boolean) { + $.utils.setBit(135, value, this); + } + /** +* Enables the Node.js non-functional stub readline module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsReadlineModule(): boolean { + return $.utils.getBit(136, this); + } + set enableNodeJsReadlineModule(value: boolean) { + $.utils.setBit(136, value, this); + } + /** +* Enables the Node.js non-functional stub repl module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsReplModule(): boolean { + return $.utils.getBit(137, this); + } + set enableNodeJsReplModule(value: boolean) { + $.utils.setBit(137, value, this); + } + /** +* Enables the Node.js non-functional stub sqlite module. It is required to use this +* flag with nodejs_compat (or nodejs_compat_v2). +* +*/ + get enableNodeJsSqliteModule(): boolean { + return $.utils.getBit(138, this); + } + set enableNodeJsSqliteModule(value: boolean) { + $.utils.setBit(138, value, this); + } + /** +* Enable the ctx.exports API. +* +*/ + get enableCtxExports(): boolean { + return $.utils.getBit(139, this); + } + set enableCtxExports(value: boolean) { + $.utils.setBit(139, value, this); + } + /** +* Don't include the Python sdk from the runtime, use a vendored copy. +* +*/ + get pythonExternalSDK(): boolean { + return $.utils.getBit(140, this); + } + set pythonExternalSDK(value: boolean) { + $.utils.setBit(140, value, this); + } + /** +* Enables the fast jsg::Struct optimization. With this enabled, JSG_STRUCTS +* will use a more efficient creation pattern that reduces construction time. +* However, optional fields will be explicitly set to undefined rather than +* being omitted, which is an observable behavior change. +* +*/ + get fastJsgStruct(): boolean { + return $.utils.getBit(141, this); + } + set fastJsgStruct(value: boolean) { + $.utils.setBit(141, value, this); + } + /** +* Enables the use of cache: reload in the fetch api. +* +*/ + get cacheReload(): boolean { + return $.utils.getBit(142, this); + } + set cacheReload(value: boolean) { + $.utils.setBit(142, value, this); + } + /** +* Enables breaking changes to Node.js streams done with the release of Node.js v24. +* +*/ + get streamsNodejsV24Compat(): boolean { + return $.utils.getBit(143, this); + } + set streamsNodejsV24Compat(value: boolean) { + $.utils.setBit(143, value, this); + } + get pythonCheckRngState(): boolean { + return $.utils.getBit(144, this); + } + set pythonCheckRngState(value: boolean) { + $.utils.setBit(144, value, this); + } + /** +* When set, tells JSG to make the prototype of all jsg::Objects immutable. +* TODO(soon): Add the default on date once the flag is verified to be +* generally safe. +* +*/ + get shouldSetImmutablePrototype(): boolean { + return $.utils.getBit(145, this); + } + set shouldSetImmutablePrototype(value: boolean) { + $.utils.setBit(145, value, this); + } + /** +* Enables passing sync and async iterables as the body of fetch Request or Response. +* Previously, sync iterables like Arrays would be accepted but stringified, and async +* iterables would be treated as regular objects and not iterated over at all. With this +* flag enabled, sync and async iterables will be properly iterated over and their values +* used as the body of the request or response. +* The actual compat flag enables the specific AsyncGeneratorIgnoringStrings type wrapper +* that allows this behavior and allows sync Generator and AsyncGenerator objects to be +* included in kj::OneOf declarations safely with strings and other types. When enabled, +* strings are ignored but Arrays will be treated as iterables and not stringified as before. +* +*/ + get fetchIterableTypeSupport(): boolean { + return $.utils.getBit(146, this); + } + set fetchIterableTypeSupport(value: boolean) { + $.utils.setBit(146, value, this); + } + /** +* Enables support for null and undefined values in EnvModule's getCurrentEnv() and +* getCurrentExports() methods. When enabled, if the async context contains null or +* undefined values, they will be returned instead of falling through to the worker +* env/exports. This allows more explicit control over env and exports values in async +* contexts. +* +*/ + get envModuleNullableSupport(): boolean { + return $.utils.getBit(147, this); + } + set envModuleNullableSupport(value: boolean) { + $.utils.setBit(147, value, this); + } + /** +* Enables precise timers with 3ms granularity. This provides more accurate timing for performance +* measurements and time-sensitive operations. +* +*/ + get preciseTimers(): boolean { + return $.utils.getBit(148, this); + } + set preciseTimers(value: boolean) { + $.utils.setBit(148, value, this); + } + /** +* Further adapts the fetch iterable type support to adjust for toString/toPrimitive +* overrides on sync iterable objects. Specifically, if an object passed as the body +* of a fetch Request or Response is sync iterable but has a custom toString or +* toPrimitive method, we will skip treating it as a sync iterable and instead allow +* it to fall through to being handled as a stringified object. +* +*/ + get fetchIterableTypeSupportOverrideAdjustment(): boolean { + return $.utils.getBit(149, this); + } + set fetchIterableTypeSupportOverrideAdjustment(value: boolean) { + $.utils.setBit(149, value, this); + } + /** +* Instructs the readAllText method in streams to strip the leading UTF8 BOM if present. +* +*/ + get stripBomInReadAllText(): boolean { + return $.utils.getBit(150, this); + } + set stripBomInReadAllText(value: boolean) { + $.utils.setBit(150, value, this); + } + /** +* Permits various stub types (e.g. ServiceStub aka Fetcher, DurableObjectClass) to be stored in +* long-term Durable Object storage without any mechanism for the stub target to audit or revoke +* incoming connections. +* +* This feature exists for experimental use only, and will be removed once we have a properly +* auditable and revocable storage mechanism. +* +*/ + get allowIrrevocableStubStorage(): boolean { + return $.utils.getBit(151, this); + } + set allowIrrevocableStubStorage(value: boolean) { + $.utils.setBit(151, value, this); + } + /** +* Changes the ownership semantics of RPC stubs embedded in the parameters of an RPC call. +* +* When the RPC system was first introduced, RPC stubs that were embedded in the params or return +* value of some other call had their ownership transferred. That is, the original stub was +* implicitly disposed, with a duplicate stub being delivered to the destination. +* +* This turns out to compose poorly with another rule: in the callee, any stubs received in the +* params of a call are automatically disposed when the call returns. These two rules combine to +* mean that if you proxy a call -- i.e. the implementation of an RPC just makes another RPC call +* passing along the same params -- then any stubs in the params get disposed twice. Worse, if +* the eventual recipient of the stub wants to keep a duplicate past the end of the call, this +* may not work because the copy of the stub in the proxy layer gets disposed anyway, breaking the +* connection. +* +* For this reason, the pure-JS implementation of Cap'n Web switched to saying that stubs in params +* do NOT transfer ownership -- they are simply duplicated. This compat flag fixes the Workers +* Runtime built-in RPC to match Cap'n Web behavior. +* +* In particular, this fixes: https://github.com/cloudflare/capnweb/issues/110 +* +*/ + get rpcParamsDupStubs(): boolean { + return $.utils.getBit(152, this); + } + set rpcParamsDupStubs(value: boolean) { + $.utils.setBit(152, value, this); + } + /** +* When enabled, setTimeout, setInterval, clearTimeout, and clearInterval +* are available on globalThis as Node.js-compatible versions from node:timers. +* setTimeout and setInterval return Timeout objects with methods like +* refresh(), ref(), unref(), and hasRef(). +* This flag requires nodejs_compat or nodejs_compat_v2 to be enabled. +* +*/ + get enableNodejsGlobalTimers(): boolean { + return $.utils.getBit(153, this); + } + set enableNodejsGlobalTimers(value: boolean) { + $.utils.setBit(153, value, this); + } + toString(): string { return "CompatibilityFlags_" + super.toString(); } +} diff --git a/packages/miniflare/src/shared/compatibility-flags-metadata.ts b/packages/miniflare/src/shared/compatibility-flags-metadata.ts new file mode 100644 index 000000000000..276938f53ada --- /dev/null +++ b/packages/miniflare/src/shared/compatibility-flags-metadata.ts @@ -0,0 +1,1243 @@ +/** + * Auto-generated from compatibility-date.capnp - DO NOT EDIT MANUALLY + * + * This file is regenerated when the workerd dependency is updated. + * Source: https://raw.githubusercontent.com/cloudflare/workerd/main/src/workerd/io/compatibility-date.capnp + * + * @see {@link file://./../../../scripts/build-capnp-compat.mjs} for documentation + */ + +export interface FlagMetadata { + fieldName: string; + ordinal: number; + enableFlag?: string; + disableFlag?: string; + enableDate?: string; + experimental?: boolean; + impliedBy?: Array<{ names: string[]; date: string }>; +} + +export const FLAG_METADATA: FlagMetadata[] = [ + { + "fieldName": "formDataParserSupportsFiles", + "ordinal": 0, + "enableFlag": "formdata_parser_supports_files", + "enableDate": "2021-11-03", + "disableFlag": "formdata_parser_converts_files_to_strings" + }, + { + "fieldName": "fetchRefusesUnknownProtocols", + "ordinal": 1, + "enableFlag": "fetch_refuses_unknown_protocols", + "enableDate": "2021-11-10", + "disableFlag": "fetch_treats_unknown_protocols_as_http" + }, + { + "fieldName": "esiIncludeIsVoidTag", + "ordinal": 2, + "enableFlag": "html_rewriter_treats_esi_include_as_void_tag" + }, + { + "fieldName": "obsolete3", + "ordinal": 3 + }, + { + "fieldName": "durableObjectFetchRequiresSchemeAuthority", + "ordinal": 4, + "enableFlag": "durable_object_fetch_requires_full_url", + "enableDate": "2021-11-10", + "disableFlag": "durable_object_fetch_allows_relative_url" + }, + { + "fieldName": "streamsByobReaderDetachesBuffer", + "ordinal": 5, + "enableFlag": "streams_byob_reader_detaches_buffer", + "enableDate": "2021-11-10", + "disableFlag": "streams_byob_reader_does_not_detach_buffer" + }, + { + "fieldName": "streamsJavaScriptControllers", + "ordinal": 6, + "enableFlag": "streams_enable_constructors", + "enableDate": "2022-11-30", + "disableFlag": "streams_disable_constructors" + }, + { + "fieldName": "jsgPropertyOnPrototypeTemplate", + "ordinal": 7, + "enableFlag": "workers_api_getters_setters_on_prototype", + "enableDate": "2022-01-31", + "disableFlag": "workers_api_getters_setters_on_instance" + }, + { + "fieldName": "minimalSubrequests", + "ordinal": 8, + "enableFlag": "minimal_subrequests", + "enableDate": "2022-04-05", + "disableFlag": "no_minimal_subrequests" + }, + { + "fieldName": "noCotsOnExternalFetch", + "ordinal": 9, + "enableFlag": "no_cots_on_external_fetch", + "enableDate": "2022-03-08", + "disableFlag": "cots_on_external_fetch" + }, + { + "fieldName": "specCompliantUrl", + "ordinal": 10, + "enableFlag": "url_standard", + "enableDate": "2022-10-31", + "disableFlag": "url_original" + }, + { + "fieldName": "globalNavigator", + "ordinal": 11, + "enableFlag": "global_navigator", + "enableDate": "2022-03-21", + "disableFlag": "no_global_navigator" + }, + { + "fieldName": "captureThrowsAsRejections", + "ordinal": 12, + "enableFlag": "capture_async_api_throws", + "enableDate": "2022-10-31", + "disableFlag": "do_not_capture_async_api_throws" + }, + { + "fieldName": "r2PublicBetaApi", + "ordinal": 13, + "enableFlag": "r2_public_beta_bindings", + "disableFlag": "r2_internal_beta_bindings" + }, + { + "fieldName": "obsolete14", + "ordinal": 14, + "enableFlag": "durable_object_alarms" + }, + { + "fieldName": "noSubstituteNull", + "ordinal": 15, + "enableFlag": "dont_substitute_null_on_type_error", + "enableDate": "2022-06-01", + "disableFlag": "substitute_null_on_type_error" + }, + { + "fieldName": "transformStreamJavaScriptControllers", + "ordinal": 16, + "enableFlag": "transformstream_enable_standard_constructor", + "enableDate": "2022-11-30", + "disableFlag": "transformstream_disable_standard_constructor" + }, + { + "fieldName": "r2ListHonorIncludeFields", + "ordinal": 17, + "enableFlag": "r2_list_honor_include", + "enableDate": "2022-08-04" + }, + { + "fieldName": "exportCommonJsDefaultNamespace", + "ordinal": 18, + "enableFlag": "export_commonjs_default", + "enableDate": "2022-10-31", + "disableFlag": "export_commonjs_namespace" + }, + { + "fieldName": "obsolete19", + "ordinal": 19, + "enableFlag": "durable_object_rename", + "experimental": true + }, + { + "fieldName": "webSocketCompression", + "ordinal": 20, + "enableFlag": "web_socket_compression", + "enableDate": "2023-08-15", + "disableFlag": "no_web_socket_compression" + }, + { + "fieldName": "nodeJsCompat", + "ordinal": 21, + "enableFlag": "nodejs_compat", + "disableFlag": "no_nodejs_compat" + }, + { + "fieldName": "obsolete22", + "ordinal": 22, + "enableFlag": "tcp_sockets_support" + }, + { + "fieldName": "specCompliantResponseRedirect", + "ordinal": 23, + "enableDate": "2023-03-14", + "enableFlag": "response_redirect_url_standard", + "disableFlag": "response_redirect_url_original" + }, + { + "fieldName": "workerdExperimental", + "ordinal": 24, + "enableFlag": "experimental", + "experimental": true + }, + { + "fieldName": "durableObjectGetExisting", + "ordinal": 25, + "enableFlag": "durable_object_get_existing", + "experimental": true + }, + { + "fieldName": "httpHeadersGetSetCookie", + "ordinal": 26, + "enableFlag": "http_headers_getsetcookie", + "disableFlag": "no_http_headers_getsetcookie", + "enableDate": "2023-03-01" + }, + { + "fieldName": "dispatchExceptionTunneling", + "ordinal": 27, + "enableDate": "2023-03-01", + "enableFlag": "dynamic_dispatch_tunnel_exceptions", + "disableFlag": "dynamic_dispatch_treat_exceptions_as_500" + }, + { + "fieldName": "serviceBindingExtraHandlers", + "ordinal": 28, + "enableFlag": "service_binding_extra_handlers", + "experimental": true + }, + { + "fieldName": "noCfBotManagementDefault", + "ordinal": 29, + "enableFlag": "no_cf_botmanagement_default", + "disableFlag": "cf_botmanagement_default", + "enableDate": "2023-08-01" + }, + { + "fieldName": "urlSearchParamsDeleteHasValueArg", + "ordinal": 30, + "enableFlag": "urlsearchparams_delete_has_value_arg", + "disableFlag": "no_urlsearchparams_delete_has_value_arg", + "enableDate": "2023-07-01" + }, + { + "fieldName": "strictCompression", + "ordinal": 31, + "enableFlag": "strict_compression_checks", + "disableFlag": "no_strict_compression_checks", + "enableDate": "2023-08-01" + }, + { + "fieldName": "brotliContentEncoding", + "ordinal": 32, + "enableFlag": "brotli_content_encoding", + "enableDate": "2024-04-29", + "disableFlag": "no_brotli_content_encoding" + }, + { + "fieldName": "strictCrypto", + "ordinal": 33, + "enableFlag": "strict_crypto_checks", + "disableFlag": "no_strict_crypto_checks", + "enableDate": "2023-08-01" + }, + { + "fieldName": "rttiApi", + "ordinal": 34, + "enableFlag": "rtti_api", + "experimental": true + }, + { + "fieldName": "obsolete35", + "ordinal": 35, + "enableFlag": "webgpu", + "experimental": true + }, + { + "fieldName": "cryptoPreservePublicExponent", + "ordinal": 36, + "enableFlag": "crypto_preserve_public_exponent", + "disableFlag": "no_crypto_preserve_public_exponent", + "enableDate": "2023-12-01" + }, + { + "fieldName": "vectorizeQueryMetadataOptional", + "ordinal": 37, + "enableFlag": "vectorize_query_metadata_optional", + "enableDate": "2023-11-08", + "disableFlag": "vectorize_query_original" + }, + { + "fieldName": "unsafeModule", + "ordinal": 38, + "enableFlag": "unsafe_module", + "experimental": true + }, + { + "fieldName": "jsRpc", + "ordinal": 39, + "enableFlag": "js_rpc", + "experimental": true + }, + { + "fieldName": "noImportScripts", + "ordinal": 40, + "enableFlag": "no_global_importscripts", + "disableFlag": "global_importscripts", + "enableDate": "2024-03-04" + }, + { + "fieldName": "nodeJsAls", + "ordinal": 41, + "enableFlag": "nodejs_als", + "disableFlag": "no_nodejs_als" + }, + { + "fieldName": "queuesJsonMessages", + "ordinal": 42, + "enableFlag": "queues_json_messages", + "disableFlag": "no_queues_json_messages", + "enableDate": "2024-03-18" + }, + { + "fieldName": "pythonWorkers", + "ordinal": 43, + "enableFlag": "python_workers", + "impliedBy": [ + { + "names": [ + "pythonWorkersDevPyodide" + ], + "date": "2000-01-01" + } + ] + }, + { + "fieldName": "fetcherNoGetPutDelete", + "ordinal": 44, + "enableFlag": "fetcher_no_get_put_delete", + "disableFlag": "fetcher_has_get_put_delete", + "enableDate": "2024-03-26" + }, + { + "fieldName": "unwrapCustomThenables", + "ordinal": 45, + "enableFlag": "unwrap_custom_thenables", + "disableFlag": "no_unwrap_custom_thenables", + "enableDate": "2024-04-01" + }, + { + "fieldName": "fetcherRpc", + "ordinal": 46, + "enableFlag": "rpc", + "disableFlag": "no_rpc", + "enableDate": "2024-04-03" + }, + { + "fieldName": "internalStreamByobReturn", + "ordinal": 47, + "enableFlag": "internal_stream_byob_return_view", + "disableFlag": "internal_stream_byob_return_undefined", + "enableDate": "2024-05-13" + }, + { + "fieldName": "blobStandardMimeType", + "ordinal": 48, + "enableFlag": "blob_standard_mime_type", + "disableFlag": "blob_legacy_mime_type", + "enableDate": "2024-06-03" + }, + { + "fieldName": "fetchStandardUrl", + "ordinal": 49, + "enableFlag": "fetch_standard_url", + "disableFlag": "fetch_legacy_url", + "enableDate": "2024-06-03" + }, + { + "fieldName": "nodeJsCompatV2", + "ordinal": 50, + "enableFlag": "nodejs_compat_v2", + "disableFlag": "no_nodejs_compat_v2", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2024-09-23" + } + ] + }, + { + "fieldName": "globalFetchStrictlyPublic", + "ordinal": 51, + "enableFlag": "global_fetch_strictly_public", + "disableFlag": "global_fetch_private_origin" + }, + { + "fieldName": "newModuleRegistry", + "ordinal": 52, + "enableFlag": "new_module_registry", + "disableFlag": "legacy_module_registry", + "experimental": true + }, + { + "fieldName": "cacheOptionEnabled", + "ordinal": 53, + "enableFlag": "cache_option_enabled", + "disableFlag": "cache_option_disabled", + "enableDate": "2024-11-11" + }, + { + "fieldName": "kvDirectBinding", + "ordinal": 54, + "enableFlag": "kv_direct_binding", + "experimental": true + }, + { + "fieldName": "allowCustomPorts", + "ordinal": 55, + "enableFlag": "allow_custom_ports", + "disableFlag": "ignore_custom_ports", + "enableDate": "2024-09-02" + }, + { + "fieldName": "increaseWebsocketMessageSize", + "ordinal": 56, + "enableFlag": "increase_websocket_message_size", + "experimental": true + }, + { + "fieldName": "internalWritableStreamAbortClearsQueue", + "ordinal": 57, + "enableFlag": "internal_writable_stream_abort_clears_queue", + "disableFlag": "internal_writable_stream_abort_does_not_clear_queue", + "enableDate": "2024-09-02" + }, + { + "fieldName": "pythonWorkersDevPyodide", + "ordinal": 58, + "enableFlag": "python_workers_development", + "experimental": true + }, + { + "fieldName": "nodeJsZlib", + "ordinal": 59, + "enableFlag": "nodejs_zlib", + "disableFlag": "no_nodejs_zlib", + "impliedBy": [ + { + "names": [ + "nodeJsCompat", + "nodeJsCompatV2" + ], + "date": "2024-09-23" + } + ] + }, + { + "fieldName": "replicaRouting", + "ordinal": 60, + "enableFlag": "replica_routing", + "experimental": true + }, + { + "fieldName": "obsolete61", + "ordinal": 61, + "enableFlag": "enable_d1_with_sessions_api", + "experimental": true + }, + { + "fieldName": "handleCrossRequestPromiseResolution", + "ordinal": 62, + "enableFlag": "handle_cross_request_promise_resolution", + "disableFlag": "no_handle_cross_request_promise_resolution", + "enableDate": "2024-10-14" + }, + { + "fieldName": "obsolete63", + "ordinal": 63, + "experimental": true + }, + { + "fieldName": "setToStringTag", + "ordinal": 64, + "enableFlag": "set_tostring_tag", + "disableFlag": "do_not_set_tostring_tag", + "enableDate": "2024-09-26" + }, + { + "fieldName": "upperCaseAllHttpMethods", + "ordinal": 65, + "enableFlag": "upper_case_all_http_methods", + "disableFlag": "no_upper_case_all_http_methods", + "enableDate": "2024-10-14" + }, + { + "fieldName": "obsolete66", + "ordinal": 66, + "enableFlag": "python_external_packages" + }, + { + "fieldName": "noTopLevelAwaitInRequire", + "ordinal": 67, + "enableFlag": "disable_top_level_await_in_require", + "disableFlag": "enable_top_level_await_in_require", + "enableDate": "2024-12-02" + }, + { + "fieldName": "fixupTransformStreamBackpressure", + "ordinal": 68, + "enableFlag": "fixup-transform-stream-backpressure", + "disableFlag": "original-transform-stream-backpressure", + "enableDate": "2024-12-16" + }, + { + "fieldName": "obsolete69", + "ordinal": 69, + "enableFlag": "tail_worker_user_spans", + "experimental": true + }, + { + "fieldName": "cacheNoCache", + "ordinal": 70, + "enableFlag": "cache_no_cache_enabled", + "disableFlag": "cache_no_cache_disabled", + "impliedBy": [ + { + "names": [ + "cacheOptionEnabled" + ], + "date": "2025-08-07" + } + ] + }, + { + "fieldName": "pythonWorkers20250116", + "ordinal": 71, + "enableFlag": "python_workers_20250116", + "disableFlag": "no_python_workers_20250116", + "impliedBy": [ + { + "names": [ + "pythonWorkers" + ], + "date": "2025-09-29" + } + ] + }, + { + "fieldName": "requestCfOverridesCacheRules", + "ordinal": 72, + "enableFlag": "request_cf_overrides_cache_rules", + "disableFlag": "no_request_cf_overrides_cache_rules", + "enableDate": "2025-04-02" + }, + { + "fieldName": "memoryCacheDelete", + "ordinal": 73, + "enableFlag": "memory_cache_delete", + "experimental": true + }, + { + "fieldName": "queueConsumerNoWaitForWaitUntil", + "ordinal": 75, + "enableFlag": "queue_consumer_no_wait_for_wait_until", + "disableFlag": "queue_consumer_wait_for_wait_until" + }, + { + "fieldName": "populateProcessEnv", + "ordinal": 76, + "enableFlag": "nodejs_compat_populate_process_env", + "disableFlag": "nodejs_compat_do_not_populate_process_env", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-04-01" + } + ] + }, + { + "fieldName": "cacheApiRequestCfOverridesCacheRules", + "ordinal": 77, + "enableFlag": "cache_api_request_cf_overrides_cache_rules", + "disableFlag": "no_cache_api_request_cf_overrides_cache_rules", + "enableDate": "2025-05-19" + }, + { + "fieldName": "disableImportableEnv", + "ordinal": 78, + "enableFlag": "disallow_importable_env", + "disableFlag": "allow_importable_env" + }, + { + "fieldName": "assetsSecFetchModeNavigateHeaderPrefersAssetServing", + "ordinal": 79, + "enableFlag": "assets_navigation_prefers_asset_serving", + "disableFlag": "assets_navigation_has_no_effect", + "enableDate": "2025-04-01" + }, + { + "fieldName": "cacheApiCompatFlags", + "ordinal": 80, + "enableFlag": "cache_api_compat_flags", + "disableFlag": "no_cache_api_compat_flags", + "enableDate": "2025-04-19" + }, + { + "fieldName": "obsolete81", + "ordinal": 81, + "enableFlag": "python_workers_durable_objects", + "experimental": true + }, + { + "fieldName": "obsolete82", + "ordinal": 82, + "enableFlag": "streaming_tail_worker", + "experimental": true + }, + { + "fieldName": "specCompliantUrlpattern", + "ordinal": 83, + "enableFlag": "urlpattern_standard", + "enableDate": "2025-05-01", + "disableFlag": "urlpattern_original" + }, + { + "fieldName": "jsWeakRef", + "ordinal": 84, + "enableFlag": "enable_weak_ref", + "enableDate": "2025-05-05", + "disableFlag": "disable_weak_ref" + }, + { + "fieldName": "requestSignalPassthrough", + "ordinal": 85, + "enableFlag": "request_signal_passthrough", + "disableFlag": "no_request_signal_passthrough" + }, + { + "fieldName": "enableNavigatorLanguage", + "ordinal": 86, + "enableFlag": "enable_navigator_language", + "enableDate": "2025-05-19", + "disableFlag": "disable_navigator_language" + }, + { + "fieldName": "webFileSystem", + "ordinal": 87, + "enableFlag": "enable_web_file_system", + "experimental": true + }, + { + "fieldName": "abortSignalRpc", + "ordinal": 88, + "enableFlag": "enable_abortsignal_rpc", + "experimental": true + }, + { + "fieldName": "allowEvalDuringStartup", + "ordinal": 89, + "enableFlag": "allow_eval_during_startup", + "enableDate": "2025-06-01", + "disableFlag": "disallow_eval_during_startup" + }, + { + "fieldName": "enableRequestSignal", + "ordinal": 90, + "enableFlag": "enable_request_signal", + "disableFlag": "disable_request_signal" + }, + { + "fieldName": "connectPassThrough", + "ordinal": 91, + "enableFlag": "connect_pass_through", + "experimental": true + }, + { + "fieldName": "bindAsyncLocalStorageSnapshot", + "ordinal": 93, + "enableFlag": "bind_asynclocalstorage_snapshot_to_request", + "disableFlag": "do_not_bind_asynclocalstorage_snapshot_to-request", + "enableDate": "2025-06-16" + }, + { + "fieldName": "throwOnUnrecognizedImportAssertion", + "ordinal": 94, + "enableFlag": "throw_on_unrecognized_import_assertion", + "disableFlag": "ignore_unrecognized_import_assertion", + "enableDate": "2025-06-16" + }, + { + "fieldName": "pythonWorkflows", + "ordinal": 95, + "enableFlag": "python_workflows", + "disableFlag": "disable_python_workflows", + "impliedBy": [ + { + "names": [ + "pythonWorkers" + ], + "date": "2025-09-20" + } + ] + }, + { + "fieldName": "unsupportedProcessActualPlatform", + "ordinal": 96, + "enableFlag": "unsupported_process_actual_platform", + "experimental": true + }, + { + "fieldName": "enableNodeJsProcessV2", + "ordinal": 97, + "enableFlag": "enable_nodejs_process_v2", + "disableFlag": "disable_nodejs_process_v2", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-15" + } + ] + }, + { + "fieldName": "setEventTargetThis", + "ordinal": 98, + "enableFlag": "set_event_target_this", + "disableFlag": "no_set_event_target_this", + "enableDate": "2025-08-01" + }, + { + "fieldName": "enableForwardableEmailFullHeaders", + "ordinal": 99, + "enableFlag": "set_forwardable_email_full_headers", + "disableFlag": "set_forwardable_email_single_headers", + "enableDate": "2025-08-01" + }, + { + "fieldName": "enableNodejsHttpModules", + "ordinal": 100, + "enableFlag": "enable_nodejs_http_modules", + "disableFlag": "disable_nodejs_http_modules", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-08-15" + } + ] + }, + { + "fieldName": "pedanticWpt", + "ordinal": 101, + "enableFlag": "pedantic_wpt", + "disableFlag": "non_pedantic_wpt" + }, + { + "fieldName": "exposeGlobalMessageChannel", + "ordinal": 102, + "enableFlag": "expose_global_message_channel", + "disableFlag": "no_expose_global_message_channel", + "enableDate": "2025-08-15" + }, + { + "fieldName": "enableNodejsHttpServerModules", + "ordinal": 103, + "enableFlag": "enable_nodejs_http_server_modules", + "disableFlag": "disable_nodejs_http_server_modules", + "impliedBy": [ + { + "names": [ + "enableNodejsHttpModules" + ], + "date": "2025-09-01" + } + ] + }, + { + "fieldName": "pythonNoGlobalHandlers", + "ordinal": 104, + "enableFlag": "python_no_global_handlers", + "disableFlag": "disable_python_no_global_handlers", + "enableDate": "2025-08-14" + }, + { + "fieldName": "enableNodeJsFsModule", + "ordinal": 105, + "enableFlag": "enable_nodejs_fs_module", + "disableFlag": "disable_nodejs_fs_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-15" + } + ] + }, + { + "fieldName": "enableNodeJsOsModule", + "ordinal": 106, + "enableFlag": "enable_nodejs_os_module", + "disableFlag": "disable_nodejs_os_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-15" + } + ] + }, + { + "fieldName": "pythonWorkersForceNewVendorPath", + "ordinal": 107, + "enableFlag": "python_workers_force_new_vendor_path", + "enableDate": "2025-08-11" + }, + { + "fieldName": "removeNodejsCompatEOL", + "ordinal": 108, + "enableFlag": "remove_nodejs_compat_eol", + "disableFlag": "add_nodejs_compat_eol", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-01" + } + ] + }, + { + "fieldName": "enableWorkflowScriptValidation", + "ordinal": 109, + "enableFlag": "enable_validate_workflow_entrypoint", + "disableFlag": "disable_validate_workflow_entrypoint", + "enableDate": "2025-09-20" + }, + { + "fieldName": "pythonDedicatedSnapshot", + "ordinal": 110, + "enableFlag": "python_dedicated_snapshot", + "disableFlag": "disable_python_dedicated_snapshot", + "impliedBy": [ + { + "names": [ + "pythonWorkers20250116" + ], + "date": "2025-10-16" + } + ] + }, + { + "fieldName": "typescriptStripTypes", + "ordinal": 111, + "enableFlag": "typescript_strip_types", + "experimental": true + }, + { + "fieldName": "enableNodeJsHttp2Module", + "ordinal": 112, + "enableFlag": "enable_nodejs_http2_module", + "disableFlag": "disable_nodejs_http2_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-01" + } + ] + }, + { + "fieldName": "experimentalAllowEvalAlways", + "ordinal": 113, + "enableFlag": "allow_insecure_inefficient_logged_eval", + "experimental": true + }, + { + "fieldName": "stripAuthorizationOnCrossOriginRedirect", + "ordinal": 114, + "enableFlag": "strip_authorization_on_cross_origin_redirect", + "disableFlag": "retain_authorization_on_cross_origin_redirect", + "enableDate": "2025-09-01" + }, + { + "fieldName": "enhancedErrorSerialization", + "ordinal": 115, + "enableFlag": "enhanced_error_serialization", + "disableFlag": "legacy_error_serialization", + "experimental": true + }, + { + "fieldName": "emailSendingQueuing", + "ordinal": 116, + "enableFlag": "enable_email_sending_queuing", + "disableFlag": "disable_email_sending_queuing" + }, + { + "fieldName": "removeNodejsCompatEOLv22", + "ordinal": 117, + "enableFlag": "remove_nodejs_compat_eol_v22", + "disableFlag": "add_nodejs_compat_eol_v22", + "impliedBy": [ + { + "names": [ + "removeNodejsCompatEOL" + ], + "date": "2027-04-30" + } + ] + }, + { + "fieldName": "removeNodejsCompatEOLv23", + "ordinal": 118, + "enableFlag": "remove_nodejs_compat_eol_v23", + "disableFlag": "add_nodejs_compat_eol_v23", + "impliedBy": [ + { + "names": [ + "removeNodejsCompatEOLv24" + ], + "date": "2025-09-01" + } + ] + }, + { + "fieldName": "removeNodejsCompatEOLv24", + "ordinal": 119, + "enableFlag": "remove_nodejs_compat_eol_v24", + "disableFlag": "add_nodejs_compat_eol_v24", + "impliedBy": [ + { + "names": [ + "removeNodejsCompatEOL" + ], + "date": "2028-04-30" + } + ] + }, + { + "fieldName": "enableNodeJsConsoleModule", + "ordinal": 120, + "enableFlag": "enable_nodejs_console_module", + "disableFlag": "disable_nodejs_console_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-09-21" + } + ] + }, + { + "fieldName": "enableNodeJsVmModule", + "ordinal": 121, + "enableFlag": "enable_nodejs_vm_module", + "disableFlag": "disable_nodejs_vm_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-10-01" + } + ] + }, + { + "fieldName": "enableNodeJsPerfHooksModule", + "ordinal": 122, + "enableFlag": "enable_nodejs_perf_hooks_module", + "disableFlag": "disable_nodejs_perf_hooks_module", + "experimental": true + }, + { + "fieldName": "enableGlobalPerformanceClasses", + "ordinal": 123, + "enableFlag": "enable_global_performance_classes", + "disableFlag": "disable_global_performance_classes", + "experimental": true + }, + { + "fieldName": "enableNodeJsDomainModule", + "ordinal": 124, + "enableFlag": "enable_nodejs_domain_module", + "disableFlag": "disable_nodejs_domain_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-12-04" + } + ] + }, + { + "fieldName": "enableNodeJsV8Module", + "ordinal": 125, + "enableFlag": "enable_nodejs_v8_module", + "disableFlag": "disable_nodejs_v8_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsTtyModule", + "ordinal": 126, + "enableFlag": "enable_nodejs_tty_module", + "disableFlag": "disable_nodejs_tty_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsPunycodeModule", + "ordinal": 127, + "enableFlag": "enable_nodejs_punycode_module", + "disableFlag": "disable_nodejs_punycode_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-12-04" + } + ] + }, + { + "fieldName": "enableNodeJsClusterModule", + "ordinal": 128, + "enableFlag": "enable_nodejs_cluster_module", + "disableFlag": "disable_nodejs_cluster_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-12-04" + } + ] + }, + { + "fieldName": "enableNodeJsChildProcessModule", + "ordinal": 129, + "enableFlag": "enable_nodejs_child_process_module", + "disableFlag": "disable_nodejs_child_process_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsWorkerThreadsModule", + "ordinal": 130, + "enableFlag": "enable_nodejs_worker_threads_module", + "disableFlag": "disable_nodejs_worker_threads_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsStreamWrapModule", + "ordinal": 131, + "enableFlag": "enable_nodejs_stream_wrap_module", + "disableFlag": "disable_nodejs_stream_wrap_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2026-01-29" + } + ] + }, + { + "fieldName": "enableNodeJsWasiModule", + "ordinal": 132, + "enableFlag": "enable_nodejs_wasi_module", + "disableFlag": "disable_nodejs_wasi_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-12-04" + } + ] + }, + { + "fieldName": "enableNodeJsDgramModule", + "ordinal": 133, + "enableFlag": "enable_nodejs_dgram_module", + "disableFlag": "disable_nodejs_dgram_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2026-01-29" + } + ] + }, + { + "fieldName": "enableNodeJsInspectorModule", + "ordinal": 134, + "enableFlag": "enable_nodejs_inspector_module", + "disableFlag": "disable_nodejs_inspector_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2026-01-29" + } + ] + }, + { + "fieldName": "enableNodeJsTraceEventsModule", + "ordinal": 135, + "enableFlag": "enable_nodejs_trace_events_module", + "disableFlag": "disable_nodejs_trace_events_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2025-12-04" + } + ] + }, + { + "fieldName": "enableNodeJsReadlineModule", + "ordinal": 136, + "enableFlag": "enable_nodejs_readline_module", + "disableFlag": "disable_nodejs_readline_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsReplModule", + "ordinal": 137, + "enableFlag": "enable_nodejs_repl_module", + "disableFlag": "disable_nodejs_repl_module", + "experimental": true + }, + { + "fieldName": "enableNodeJsSqliteModule", + "ordinal": 138, + "enableFlag": "enable_nodejs_sqlite_module", + "disableFlag": "disable_nodejs_sqlite_module", + "impliedBy": [ + { + "names": [ + "nodeJsCompat" + ], + "date": "2026-01-29" + } + ] + }, + { + "fieldName": "enableCtxExports", + "ordinal": 139, + "enableFlag": "enable_ctx_exports", + "disableFlag": "disable_ctx_exports", + "enableDate": "2025-11-17" + }, + { + "fieldName": "pythonExternalSDK", + "ordinal": 140, + "enableFlag": "enable_python_external_sdk", + "disableFlag": "disable_python_external_sdk", + "experimental": true + }, + { + "fieldName": "fastJsgStruct", + "ordinal": 141, + "enableFlag": "enable_fast_jsg_struct", + "disableFlag": "disable_fast_jsg_struct", + "enableDate": "2025-12-03" + }, + { + "fieldName": "cacheReload", + "ordinal": 142, + "enableFlag": "cache_reload_enabled", + "disableFlag": "cache_reload_disabled", + "experimental": true + }, + { + "fieldName": "streamsNodejsV24Compat", + "ordinal": 143, + "enableFlag": "enable_streams_nodejs_v24_compat", + "disableFlag": "disable_streams_nodejs_v24_compat" + }, + { + "fieldName": "shouldSetImmutablePrototype", + "ordinal": 145, + "enableFlag": "immutable_api_prototypes", + "disableFlag": "mutable_api_prototypes" + }, + { + "fieldName": "fetchIterableTypeSupport", + "ordinal": 146, + "enableFlag": "fetch_iterable_type_support", + "disableFlag": "no_fetch_iterable_type_support" + }, + { + "fieldName": "envModuleNullableSupport", + "ordinal": 147, + "enableFlag": "env_module_nullable_support", + "disableFlag": "no_env_module_nullable_support" + }, + { + "fieldName": "preciseTimers", + "ordinal": 148, + "enableFlag": "precise_timers", + "disableFlag": "no_precise_timers", + "experimental": true + }, + { + "fieldName": "fetchIterableTypeSupportOverrideAdjustment", + "ordinal": 149, + "enableFlag": "fetch_iterable_type_support_override_adjustment", + "disableFlag": "no_fetch_iterable_type_support_override_adjustment", + "impliedBy": [ + { + "names": [ + "fetchIterableTypeSupport" + ], + "date": "2026-01-15" + } + ] + }, + { + "fieldName": "stripBomInReadAllText", + "ordinal": 150, + "enableFlag": "strip_bom_in_read_all_text", + "disableFlag": "do_not_strip_bom_in_read_all_text", + "enableDate": "2026-01-13", + "impliedBy": [ + { + "names": [ + "pedanticWpt" + ], + "date": "2026-01-13" + } + ] + }, + { + "fieldName": "allowIrrevocableStubStorage", + "ordinal": 151, + "enableFlag": "allow_irrevocable_stub_storage", + "experimental": true + }, + { + "fieldName": "rpcParamsDupStubs", + "ordinal": 152, + "enableFlag": "rpc_params_dup_stubs", + "disableFlag": "rpc_params_transfer_stubs", + "enableDate": "2026-01-20" + }, + { + "fieldName": "enableNodejsGlobalTimers", + "ordinal": 153, + "enableFlag": "enable_nodejs_global_timers", + "disableFlag": "no_nodejs_global_timers", + "experimental": true + } +]; diff --git a/packages/miniflare/src/shared/compatibility-flags.ts b/packages/miniflare/src/shared/compatibility-flags.ts new file mode 100644 index 000000000000..91933b732bd5 --- /dev/null +++ b/packages/miniflare/src/shared/compatibility-flags.ts @@ -0,0 +1,241 @@ +/** + * Compatibility flags resolution for workerd. + * + * This module replicates the logic from workerd's C++ implementation in + * src/workerd/io/compatibility-date.c++ (compileCompatibilityFlags function). + * + * It computes which compatibility flags are enabled based on: + * 1. The compatibility date (flags are enabled by default after their enable date) + * 2. Explicit compatibility flags (can enable flags early or disable them) + * 3. Implied flags (some flags automatically enable others after a certain date) + * + * The types are generated from compatibility-date.capnp using capnp-es. + * + * @see {@link file://./../../../scripts/build-capnp-compat.mjs} for documentation + * @module + */ + +import { FLAG_METADATA } from "./compatibility-flags-metadata"; +import type { CompatibilityFlags } from "./compatibility-date"; +import type { FlagMetadata } from "./compatibility-flags-metadata"; + +/** + * Type representing all compatibility flag field names. + * Derived from the capnp-es generated CompatibilityFlags class. + */ +type CompatibilityFlagName = { + [K in keyof CompatibilityFlags]: CompatibilityFlags[K] extends boolean + ? K + : never; +}[keyof CompatibilityFlags]; + +/** + * Type for the result of getWorkerdFeatureFlags. + * Maps all boolean flag fields from CompatibilityFlags to boolean values. + */ +export type FeatureFlagsMap = { + [K in CompatibilityFlagName]: boolean; +}; + +/** + * Parses a compatibility date string (YYYY-MM-DD) into a comparable integer. + * + * @param dateStr - Date string in YYYY-MM-DD format + * @returns Integer representation (e.g., "2024-01-15" -> 20240115) + * @throws Error if the date format is invalid + */ +function parseCompatDate(dateStr: string): number { + const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/); + if (!match) { + throw new Error( + `Invalid compatibility date format: "${dateStr}". Expected YYYY-MM-DD format.` + ); + } + const [, year, month, day] = match; + return ( + parseInt(year, 10) * 10000 + parseInt(month, 10) * 100 + parseInt(day, 10) + ); +} + +/** + * Creates a map from enable/disable flag names to their field metadata. + * This allows quick lookup when processing explicit compatibility flags. + */ +function createFlagNameMap(): Map { + const map = new Map(); + + for (const flag of FLAG_METADATA) { + if (flag.enableFlag) { + map.set(flag.enableFlag, flag); + } + if (flag.disableFlag) { + map.set(flag.disableFlag, flag); + } + } + + return map; +} + +/** + * Computes which workerd feature flags are enabled based on compatibility date + * and explicit compatibility flags. + * + * This function replicates the behavior of workerd's compileCompatibilityFlags() + * C++ function, implementing: + * - Date-based flag enablement (flags enabled after their compatEnableDate) + * - Explicit flag overrides (compatEnableFlag / compatDisableFlag) + * - Implied flag cascading (impliedByAfterDate annotations) + * + * @param compatibilityDate - Compatibility date string (YYYY-MM-DD format) + * @param compatibilityFlags - Array of explicit compatibility flag names + * @returns Record mapping field names to boolean enabled state + * @throws Error if date format is invalid or unknown flags are provided + * + * ## Flag Resolution Logic + * + * ### Phase 1: Basic Resolution + * + * For each flag: + * + * 1. Check if enabled by date: `compatibilityDate >= flag.enableDate` + * 2. Check if explicitly enabled: `flag.enableFlag in compatibilityFlags` + * 3. Check if explicitly disabled: `flag.disableFlag in compatibilityFlags` (overrides everything) + * + * ### Phase 2: Implied Flags (Cascading) + * + * Uses fixed-point iteration to resolve transitive implications: + * + * - If flag A is enabled AND `compatibilityDate >= implication.date`, enable flag B + * - Continues until no more flags are enabled + * - Example: `nodejs_compat` (enabled) + date >= "2024-09-23" → enables `nodejs_compat_v2` + * + * ## Comparison with Workerd + * + * ### Similarities + * + * - Same resolution algorithm + * - Same flag definitions from compatibility-date.capnp + * - Implied flag cascading with fixed-point iteration + * - Returns all flags with boolean values + * + * ### Differences + * + * - TypeScript vs C++ (easier to maintain and extend) + * - No validation modes (always allows any date) + * - No experimental flag filtering (always allows experimental flags) + * - Throws errors instead of using error reporters + * - Uses `capnp-es` for type generation + text parsing for annotations + * + * @example + * ```typescript + * const flags = getWorkerdFeatureFlags("2024-09-23", ["nodejs_compat"]); + * // Returns: { + * // nodeJsCompat: true, + * // nodeJsCompatV2: true, // implied by nodejs_compat after 2024-09-23 + * // formDataParserSupportsFiles: true, // enabled by date + * // ... + * // } + * ``` + */ +export function getWorkerdFeatureFlags( + compatibilityDate: string, + compatibilityFlags: string[] +): FeatureFlagsMap { + // Parse the compatibility date + const parsedDate = parseCompatDate(compatibilityDate); + + // Create a set for quick lookup of explicit flags + const flagSet = new Set(compatibilityFlags); + + // Create a map for quick lookup by flag name + const flagNameMap = createFlagNameMap(); + + // Validate that all provided flags are recognized + for (const flagName of compatibilityFlags) { + if (!flagNameMap.has(flagName)) { + throw new Error( + `Unknown compatibility flag: "${flagName}". This flag is not defined in the compatibility schema.` + ); + } + } + + // Initialize result object with all flags set to false + const result: Record = {}; + for (const flag of FLAG_METADATA) { + result[flag.fieldName] = false; + } + + // Phase 1: Resolve all flags based on date and explicit flags + for (const flag of FLAG_METADATA) { + let enabled = false; + + // Check if enabled by date + if (flag.enableDate) { + const flagDate = parseCompatDate(flag.enableDate); + enabled = parsedDate >= flagDate; + } + + // Check explicit enable flag (overrides date-based default) + if (flag.enableFlag && flagSet.has(flag.enableFlag)) { + enabled = true; + } + + // Check explicit disable flag (overrides everything) + if (flag.disableFlag && flagSet.has(flag.disableFlag)) { + enabled = false; + } + + result[flag.fieldName] = enabled; + } + + // Phase 2: Process implied flags (cascading) + // Continue iterating until no more changes occur (fixed-point iteration) + let changed = true; + let iterations = 0; + const MAX_ITERATIONS = 100; // Safety limit to prevent infinite loops + + while (changed && iterations < MAX_ITERATIONS) { + changed = false; + iterations++; + + for (const flag of FLAG_METADATA) { + // Skip if already enabled + if (result[flag.fieldName]) { + continue; + } + + // Check if this flag should be implied by others + if (flag.impliedBy) { + for (const implication of flag.impliedBy) { + // Check if all implying flags are enabled + // Note: implication.names contains field names (camelCase), not enable flag names + const allEnabled = implication.names.every((implierFieldName) => { + // The field name is directly used in the result object + return result[implierFieldName] === true; + }); + + // Check if the date requirement is met + const implDate = parseCompatDate(implication.date); + const dateOk = parsedDate >= implDate; + + // If both conditions are met, enable this flag + if (allEnabled && dateOk) { + result[flag.fieldName] = true; + changed = true; + break; // No need to check other implications for this flag + } + } + } + } + } + + if (iterations >= MAX_ITERATIONS) { + throw new Error( + "Maximum iterations exceeded while resolving implied flags. This may indicate a circular dependency in the compatibility flag definitions." + ); + } + + // The result object is built from FLAG_METADATA which contains all fields + // from the CompatibilityFlags struct, so we can safely cast it + return result as FeatureFlagsMap; +} diff --git a/packages/miniflare/test/shared/compatibility-flags.spec.ts b/packages/miniflare/test/shared/compatibility-flags.spec.ts new file mode 100644 index 000000000000..032173deb5a7 --- /dev/null +++ b/packages/miniflare/test/shared/compatibility-flags.spec.ts @@ -0,0 +1,159 @@ +import { describe, expect, it } from "vitest"; +import { getWorkerdFeatureFlags } from "../../src/shared/compatibility-flags"; + +describe("getWorkerdFeatureFlags", () => { + it("should enable flags based on compatibility date", () => { + const result = getWorkerdFeatureFlags("2022-01-01", []); + + // These flags should be enabled because the date is after their enable date + expect(result.formDataParserSupportsFiles).toBe(true); // enabled 2021-11-03 + expect(result.fetchRefusesUnknownProtocols).toBe(true); // enabled 2021-11-10 + expect(result.durableObjectFetchRequiresSchemeAuthority).toBe(true); // enabled 2021-11-10 + }); + + it("should not enable flags before their enable date", () => { + const result = getWorkerdFeatureFlags("2021-01-01", []); + + // These flags should not be enabled because the date is before their enable date + expect(result.formDataParserSupportsFiles).toBe(false); // enabled 2021-11-03 + expect(result.fetchRefusesUnknownProtocols).toBe(false); // enabled 2021-11-10 + }); + + it("should enable flags explicitly via compatibility flags", () => { + const result = getWorkerdFeatureFlags("2020-01-01", [ + "formdata_parser_supports_files", + ]); + + // This flag should be enabled even though the date is before its enable date + expect(result.formDataParserSupportsFiles).toBe(true); + }); + + it("should disable flags explicitly via disable flags", () => { + const result = getWorkerdFeatureFlags("2022-01-01", [ + "formdata_parser_converts_files_to_strings", + ]); + + // This flag should be disabled even though the date is after its enable date + expect(result.formDataParserSupportsFiles).toBe(false); + }); + + it("should handle implied flags (nodejs_compat implies nodejs_compat_v2 after date)", () => { + const result = getWorkerdFeatureFlags("2024-09-23", ["nodejs_compat"]); + + expect(result.nodeJsCompat).toBe(true); + expect(result.nodeJsCompatV2).toBe(true); // implied by nodejs_compat after 2024-09-23 + }); + + it("should not imply flags before the implication date", () => { + const result = getWorkerdFeatureFlags("2024-09-22", ["nodejs_compat"]); + + expect(result.nodeJsCompat).toBe(true); + expect(result.nodeJsCompatV2).toBe(false); // not yet implied (date is 2024-09-22) + }); + + it("should handle multiple implied flags (nodejs_zlib)", () => { + const result = getWorkerdFeatureFlags("2024-09-23", ["nodejs_compat"]); + + expect(result.nodeJsCompat).toBe(true); + expect(result.nodeJsZlib).toBe(true); // implied by nodejs_compat after 2024-09-23 + }); + + it("should throw on invalid date format", () => { + expect(() => getWorkerdFeatureFlags("invalid-date", [])).toThrow( + /Invalid compatibility date format/ + ); + expect(() => getWorkerdFeatureFlags("2024/01/01", [])).toThrow( + /Invalid compatibility date format/ + ); + expect(() => getWorkerdFeatureFlags("01-01-2024", [])).toThrow( + /Invalid compatibility date format/ + ); + }); + + it("should throw on unknown compatibility flag", () => { + expect(() => + getWorkerdFeatureFlags("2023-01-01", ["unknown_flag"]) + ).toThrow(/Unknown compatibility flag/); + }); + + it("should return all flags with boolean values", () => { + const result = getWorkerdFeatureFlags("2023-01-01", []); + + // Should have many flags (100+) + expect(Object.keys(result).length).toBeGreaterThan(100); + + // All values should be boolean + for (const [, value] of Object.entries(result)) { + expect(typeof value).toBe("boolean"); + } + }); + + it("should allow experimental flags when requested", () => { + const result = getWorkerdFeatureFlags("2023-01-01", ["experimental"]); + + // The "experimental" flag is marked as experimental + expect(result.workerdExperimental).toBe(true); + }); + + it("should handle disable flag overriding date-based enablement", () => { + // First verify the flag is enabled by date + const resultBefore = getWorkerdFeatureFlags("2022-01-01", []); + expect(resultBefore.fetchRefusesUnknownProtocols).toBe(true); + + // Now disable it explicitly + const resultAfter = getWorkerdFeatureFlags("2022-01-01", [ + "fetch_treats_unknown_protocols_as_http", + ]); + expect(resultAfter.fetchRefusesUnknownProtocols).toBe(false); + }); + + it("should handle both enable and disable flags correctly", () => { + // Enable flag should override date + const result1 = getWorkerdFeatureFlags("2020-01-01", [ + "fetch_refuses_unknown_protocols", + ]); + expect(result1.fetchRefusesUnknownProtocols).toBe(true); + + // Disable flag should override everything (even explicit enable) + const result2 = getWorkerdFeatureFlags("2022-01-01", [ + "fetch_refuses_unknown_protocols", + "fetch_treats_unknown_protocols_as_http", + ]); + expect(result2.fetchRefusesUnknownProtocols).toBe(false); + }); + + it("should handle flags without enable dates", () => { + const result = getWorkerdFeatureFlags("2023-01-01", []); + + // These flags don't have enable dates, should be false by default + expect(result.esiIncludeIsVoidTag).toBe(false); + + // But can be enabled explicitly + const result2 = getWorkerdFeatureFlags("2023-01-01", [ + "html_rewriter_treats_esi_include_as_void_tag", + ]); + expect(result2.esiIncludeIsVoidTag).toBe(true); + }); + + it("should handle complex implied flag chains", () => { + // nodeJsCompatV2 is implied by nodeJsCompat after 2024-09-23 + // Other flags are also implied by nodeJsCompat or nodeJsCompatV2 + const result = getWorkerdFeatureFlags("2024-09-23", ["nodejs_compat"]); + + expect(result.nodeJsCompat).toBe(true); + expect(result.nodeJsCompatV2).toBe(true); // implied + expect(result.nodeJsZlib).toBe(true); // implied by both nodejs_compat and nodejs_compat_v2 + }); + + it("should handle future dates", () => { + // Should allow any date (no future date rejection) + const result = getWorkerdFeatureFlags("2099-12-31", []); + + // All date-based flags should be enabled + expect(result.formDataParserSupportsFiles).toBe(true); + expect(result.fetchRefusesUnknownProtocols).toBe(true); + expect( + Object.values(result).filter((v) => v === true).length + ).toBeGreaterThan(50); + }); +}); diff --git a/packages/miniflare/tsconfig.json b/packages/miniflare/tsconfig.json index c02c0e03b9ac..d732c0029014 100644 --- a/packages/miniflare/tsconfig.json +++ b/packages/miniflare/tsconfig.json @@ -31,7 +31,8 @@ "src/**/*.ts", "test/**/*.ts", "types", - "test/fixtures/unsafe-plugin/**/*.ts" + "test/fixtures/unsafe-plugin/**/*.ts", + "scripts/build-capnp-compat.mjs" ], "exclude": ["src/workers/**/*.ts", "src/workers/node.d.ts", "test/fixtures"] }