From fe3b32ee7168c927b61378633781bdb0f6e738ec Mon Sep 17 00:00:00 2001 From: overlookmotel <557937+overlookmotel@users.noreply.github.com> Date: Mon, 9 Mar 2026 17:22:39 +0000 Subject: [PATCH] feat(linter/plugins): add `oxlint-plugin-eslint` package (#20009) Closes #19746. Add `oxlint-plugin-eslint` plugin which exports all ESLint's built-in rules as a JS plugin which can be used with Oxlint. The code for rules is lazy-loaded on first use, so just using the plugin doesn't load the code for all the rules, only the ones which the user actually uses. However, the `meta` property of each rule is provided eagerly, so that `registerPlugin` in Oxlint can load the plugin without triggering the getters which lazy-load rule code. All ESLint's rule code is bundled into this plugin package, so the package has no dependencies. This has a few advantages: 1. We control the version of ESLint which the rules are taken from. 2. Avoids "works on my machine" unreproducible bug reports due to differing ESLint versions. 3. Avoids relying on ESLint's `eslint/use-at-your-own-risk` export, which could change without warning. At present, the plugin contains *all* ESLint's rules, including ones which are natively implemented in Oxlint. We might choose to scale it back to include only the rules which Oxlint doesn't natively implement. But then we need to consider what happens when we implement more rules in future: Do they disappear from this package without warning? Would that be considered a breaking change? If it would, how long do we keep them in this package? I propose that in the interests of getting this useful feature released without having to decide all these issues, we just go with it as is for now. JS plugins are not yet stable, so at present we can still make changes later on if we need to, without breaking any promises. --- .github/workflows/release_apps.yml | 8 + apps/oxlint/.gitignore | 2 + apps/oxlint/package.json | 2 + apps/oxlint/scripts/build.ts | 12 +- apps/oxlint/scripts/generate-plugin-eslint.ts | 170 ++++++++++++++++++ apps/oxlint/src-js/plugin-eslint/index.ts | 10 ++ .../fixtures/plugin_eslint/.oxlintrc.json | 16 ++ .../fixtures/plugin_eslint/files/index.js | 6 + .../fixtures/plugin_eslint/output.snap.md | 27 +++ apps/oxlint/tsdown.config.ts | 34 ++++ npm/oxlint-plugin-eslint/CHANGELOG.md | 3 + npm/oxlint-plugin-eslint/README.md | 36 ++++ npm/oxlint-plugin-eslint/package.json | 33 ++++ oxc_release.toml | 1 + oxfmtrc.jsonc | 1 + pnpm-lock.yaml | 19 ++ 16 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 apps/oxlint/scripts/generate-plugin-eslint.ts create mode 100644 apps/oxlint/src-js/plugin-eslint/index.ts create mode 100644 apps/oxlint/test/fixtures/plugin_eslint/.oxlintrc.json create mode 100644 apps/oxlint/test/fixtures/plugin_eslint/files/index.js create mode 100644 apps/oxlint/test/fixtures/plugin_eslint/output.snap.md create mode 100644 npm/oxlint-plugin-eslint/CHANGELOG.md create mode 100644 npm/oxlint-plugin-eslint/README.md create mode 100644 npm/oxlint-plugin-eslint/package.json diff --git a/.github/workflows/release_apps.yml b/.github/workflows/release_apps.yml index 29046fd5527ca..e1010939bc984 100644 --- a/.github/workflows/release_apps.yml +++ b/.github/workflows/release_apps.yml @@ -277,6 +277,7 @@ jobs: env: package_path: npm/oxlint plugins_package_path: npm/oxlint-plugins + plugin_eslint_package_path: npm/oxlint-plugin-eslint npm_dir: npm/oxlint-release PUBLISH_FLAGS: "--provenance --access public --no-git-checks" steps: @@ -306,12 +307,17 @@ jobs: run: | cp apps/oxlint/dist-pkg-plugins/* ${plugins_package_path}/ + - name: Copy dist files to oxlint-plugin-eslint npm package + run: | + cp -r apps/oxlint/dist-pkg-plugin-eslint/. ${plugin_eslint_package_path}/ + - run: npm install -g npm@latest # For trusted publishing support - name: Check Publish run: | node .github/scripts/check-npm-packages.js "${npm_dir}/*" "${package_path}" node .github/scripts/check-npm-packages.js "${plugins_package_path}" + node .github/scripts/check-npm-packages.js "${plugin_eslint_package_path}" - name: Trusted Publish run: | @@ -322,6 +328,8 @@ jobs: pnpm publish ${package_path}/ ${PUBLISH_FLAGS} # Publish `@oxlint/plugins` package pnpm publish ${plugins_package_path}/ ${PUBLISH_FLAGS} + # Publish `oxlint-plugin-eslint` package + pnpm publish ${plugin_eslint_package_path}/ ${PUBLISH_FLAGS} build-oxfmt: needs: check diff --git a/apps/oxlint/.gitignore b/apps/oxlint/.gitignore index 8b33a8b91eb50..e1e1120765b11 100644 --- a/apps/oxlint/.gitignore +++ b/apps/oxlint/.gitignore @@ -1,4 +1,6 @@ /node_modules/ /dist/ /dist-pkg-plugins/ +/dist-pkg-plugin-eslint/ +/src-js/generated/plugin-eslint/ *.node diff --git a/apps/oxlint/package.json b/apps/oxlint/package.json index f2ce7925384e7..1930de83d9b57 100644 --- a/apps/oxlint/package.json +++ b/apps/oxlint/package.json @@ -46,6 +46,7 @@ "@types/json-schema": "^7.0.15", "@types/json-stable-stringify-without-jsonify": "^1.0.2", "@types/node": "catalog:", + "@types/serialize-javascript": "^5.0.4", "@typescript-eslint/parser": "^8.54.0", "@typescript-eslint/scope-manager": "^8.54.0", "ajv": "6.14.0", @@ -58,6 +59,7 @@ "json-stable-stringify-without-jsonify": "^1.0.1", "oxc-parser": "^0.117.0", "rolldown": "catalog:", + "serialize-javascript": "^7.0.4", "tsdown": "catalog:", "tsx": "^4.21.0", "type-fest": "^5.2.0", diff --git a/apps/oxlint/scripts/build.ts b/apps/oxlint/scripts/build.ts index d47b5afa9c627..2f451b4368ded 100755 --- a/apps/oxlint/scripts/build.ts +++ b/apps/oxlint/scripts/build.ts @@ -3,16 +3,26 @@ import { execSync } from "node:child_process"; import { copyFileSync, readdirSync, rmSync } from "node:fs"; import { join } from "node:path"; +import generatePluginEslint from "./generate-plugin-eslint.ts"; const oxlintDirPath = join(import.meta.dirname, ".."), srcDirPath = join(oxlintDirPath, "src-js"), distDirPath = join(oxlintDirPath, "dist"), - distPkgPluginsDirPath = join(oxlintDirPath, "dist-pkg-plugins"); + distPkgPluginsDirPath = join(oxlintDirPath, "dist-pkg-plugins"), + distPkgPluginEslintDirPath = join(oxlintDirPath, "dist-pkg-plugin-eslint"); // Delete `dist-pkg-plugins` directory console.log("Deleting `dist-pkg-plugins` directory..."); rmSync(distPkgPluginsDirPath, { recursive: true, force: true }); +// Delete `dist-pkg-plugin-eslint` directory +console.log("Deleting `dist-pkg-plugin-eslint` directory..."); +rmSync(distPkgPluginEslintDirPath, { recursive: true, force: true }); + +// Generate plugin-eslint files +console.log("Generating oxlint-plugin-eslint files..."); +generatePluginEslint(); + // Build with tsdown console.log("Building with tsdown..."); execSync("pnpm tsdown", { stdio: "inherit", cwd: oxlintDirPath }); diff --git a/apps/oxlint/scripts/generate-plugin-eslint.ts b/apps/oxlint/scripts/generate-plugin-eslint.ts new file mode 100644 index 0000000000000..db8cfc8905eef --- /dev/null +++ b/apps/oxlint/scripts/generate-plugin-eslint.ts @@ -0,0 +1,170 @@ +/** + * Generates the `oxlint-plugin-eslint` package source files. + * + * This script produces: + * + * 1. `rules/.cjs` - One file for each ESLint core rule, that re-exports the rule's `create` function. + * 2. `index.ts` - Exports all rules as a `Record`. + * This is the `rules` property of the `oxlint-plugin-eslint` plugin. + * 3. `rule_names.ts` - Exports a list of all rule names, which is used in TSDown config. + * + * `index.ts` uses a split eager/lazy strategy so that `registerPlugin` can read each rule's `meta` + * without loading the rule module itself: + * + * - `meta` is serialized and inlined at build time. + * `registerPlugin` needs it at plugin registration time (for `fixable`, `hasSuggestions`, `schema`, + * `defaultOptions`, `messages`), so it must be available immediately without requiring the rule module. + * + * - `create` is deferred via a cached `require` call. + * The rule module is only loaded the first time `create` is called (i.e. when the rule actually runs at lint time). + * A top-level variable per rule caches the loaded function so subsequent calls skip the `require` call. + * + * Build-time validations: + * - Each rule object must only have `meta` and `create` properties. + * - `meta` values are walked to ensure they contain no functions + * (which would be serialized as executable code by `serialize-javascript`). + */ + +import { readdirSync, mkdirSync, writeFileSync, rmSync } from "node:fs"; +import { join as pathJoin, basename, relative as pathRelative } from "node:path"; +import { createRequire } from "node:module"; +import { execFileSync } from "node:child_process"; +import serialize from "serialize-javascript"; + +import type { CreateRule } from "../src-js/plugins/load.ts"; +import type { RuleMeta } from "../src-js/plugins/rule_meta.ts"; + +const require = createRequire(import.meta.url); + +const oxlintDirPath = pathJoin(import.meta.dirname, ".."); +const rootDirPath = pathJoin(oxlintDirPath, "../.."); +const eslintRulesDir = pathJoin(require.resolve("eslint/package.json"), "../lib/rules"); +const generatedDirPath = pathJoin(oxlintDirPath, "src-js/generated/plugin-eslint"); +const generatedRulesDirPath = pathJoin(generatedDirPath, "rules"); + +export default function generatePluginEslint(): void { + // Get all ESLint rule names (exclude `index.js` which is the registry, not a rule) + const ruleNames = readdirSync(eslintRulesDir) + .filter((filename) => filename.endsWith(".js") && filename !== "index.js") + .map((filename) => basename(filename, ".js")) + .sort(); + + // oxlint-disable-next-line no-console + console.log(`Found ${ruleNames.length} ESLint rules`); + + // Wipe and recreate generated directories + rmSync(generatedDirPath, { recursive: true, force: true }); + mkdirSync(generatedRulesDirPath, { recursive: true }); + + // Generate a CJS wrapper file for each rule + for (const ruleName of ruleNames) { + const relPath = pathRelative(generatedRulesDirPath, pathJoin(eslintRulesDir, `${ruleName}.js`)); + const content = `module.exports = require(${JSON.stringify(relPath)}).create;\n`; + writeFileSync(pathJoin(generatedRulesDirPath, `${ruleName}.cjs`), content); + } + + // Generate the plugin rules index. + // `meta` is inlined so it's available at registration time without loading the rule module. + // `create` is deferred via a cached `require` so the rule module is only loaded on first use. + const indexLines = [ + ` + import { createRequire } from "node:module"; + + import type { CreateRule } from "../../plugins/load.ts"; + + type CreateFn = CreateRule["create"]; + + var require = createRequire(import.meta.url); + `, + ]; + + // Generate a `let` declaration for each rule's cached `create` function. + // These are initially `null` and populated on first call. + for (let i = 0; i < ruleNames.length; i++) { + indexLines.push(`var create${i}: CreateFn | null = null;`); + } + + indexLines.push("", "export default {"); + + for (let i = 0; i < ruleNames.length; i++) { + const ruleName = ruleNames[i]; + const rulePath = pathJoin(eslintRulesDir, `${ruleName}.js`); + const rule: CreateRule = require(rulePath); + + // Validate that the rule only has expected top-level properties. + // If ESLint adds new properties in a future version, we want to find out at build time. + const unexpectedKeys = Object.keys(rule).filter((key) => key !== "meta" && key !== "create"); + if (unexpectedKeys.length > 0) { + throw new Error( + `Unexpected properties on rule \`${ruleName}\`: ${unexpectedKeys.join(", ")}. ` + + "Expected only `meta` and `create`.", + ); + } + + // Reduce `meta` to only the properties Oxlint uses, with consistent shape and property order. + // We discard e.g. `deprecated` and `docs` properties. This reduces code size. + // Default values match what `registerPlugin` assumes when a property is absent. + const { meta } = rule; + const reducedMeta: RuleMeta = { + messages: meta?.messages ?? undefined, + fixable: meta?.fixable ?? null, + hasSuggestions: meta?.hasSuggestions ?? false, + schema: meta?.schema ?? undefined, + defaultOptions: meta?.defaultOptions ?? undefined, + }; + + // Check for function values in `reducedMeta`, which would be unexpected and likely a bug. + // `serialize-javascript` would serialize them as executable code, so catch this at build time. + assertNoFunctions(reducedMeta, `eslint/lib/rules/${ruleName}.js`, "meta"); + + const metaCode = serialize(reducedMeta, { unsafe: true }); + + indexLines.push(` + ${JSON.stringify(ruleName)}: { + meta: ${metaCode}, + create(context) { + if (create${i} === null) create${i} = require("./rules/${ruleName}.cjs") as CreateFn; + return create${i}(context); + }, + }, + `); + } + indexLines.push("} satisfies Record;\n"); + + const indexFilePath = pathJoin(generatedDirPath, "index.ts"); + writeFileSync(indexFilePath, indexLines.join("\n")); + + // Format generated index file with oxfmt to clean up unnecessary quotes around property names. + // This isn't necessary, as it gets minified and bundled anyway, but it makes generated code easier to read + // when debugging. + execFileSync("pnpm", ["exec", "oxfmt", "--write", indexFilePath], { cwd: rootDirPath }); + + // Generate the rule_names.ts file for use in tsdown config + const ruleNamesCode = [ + "export default [", + ...ruleNames.map((name) => ` ${JSON.stringify(name)},`), + "] as const;\n", + ].join("\n"); + + writeFileSync(pathJoin(generatedDirPath, "rule_names.ts"), ruleNamesCode); + + // oxlint-disable-next-line no-console + console.log("Generated plugin-eslint files."); +} + +/** + * Walk an object tree and throw if any function values are found. + */ +function assertNoFunctions(value: unknown, rulePath: string, path: string): void { + if (typeof value === "function") { + throw new Error( + `Unexpected function value in \`${path}\` of rule \`${rulePath}\`. ` + + "Rule meta objects must be static data.", + ); + } + if (typeof value === "object" && value !== null) { + for (const [key, child] of Object.entries(value)) { + assertNoFunctions(child, rulePath, `${path}.${key}`); + } + } +} diff --git a/apps/oxlint/src-js/plugin-eslint/index.ts b/apps/oxlint/src-js/plugin-eslint/index.ts new file mode 100644 index 0000000000000..30ac5b728380d --- /dev/null +++ b/apps/oxlint/src-js/plugin-eslint/index.ts @@ -0,0 +1,10 @@ +// oxlint-disable-next-line typescript/ban-ts-comment +// @ts-ignore - file is generated and not checked in to git +import rules from "../generated/plugin-eslint/index.ts"; + +export default { + meta: { + name: "eslint-js", + }, + rules, +}; diff --git a/apps/oxlint/test/fixtures/plugin_eslint/.oxlintrc.json b/apps/oxlint/test/fixtures/plugin_eslint/.oxlintrc.json new file mode 100644 index 0000000000000..0a690e7f6a4b6 --- /dev/null +++ b/apps/oxlint/test/fixtures/plugin_eslint/.oxlintrc.json @@ -0,0 +1,16 @@ +{ + "categories": { + "correctness": "off" + }, + "jsPlugins": ["../../../dist-pkg-plugin-eslint/index.js"], + "rules": { + "eslint-js/array-bracket-newline": ["error", "consistent"], + "eslint-js/no-restricted-syntax": [ + "error", + { + "selector": "ThrowStatement > CallExpression[callee.name=/Error$/]", + "message": "Use `new` keyword when throwing an `Error`." + } + ] + } +} diff --git a/apps/oxlint/test/fixtures/plugin_eslint/files/index.js b/apps/oxlint/test/fixtures/plugin_eslint/files/index.js new file mode 100644 index 0000000000000..3609baa5199f3 --- /dev/null +++ b/apps/oxlint/test/fixtures/plugin_eslint/files/index.js @@ -0,0 +1,6 @@ +// Violation: array-bracket-newline (opening has newline, closing does not) +const a = [ + 1, 2, 3]; + +// Violation: no-restricted-syntax (throw Error without `new`) +throw TypeError("bad"); diff --git a/apps/oxlint/test/fixtures/plugin_eslint/output.snap.md b/apps/oxlint/test/fixtures/plugin_eslint/output.snap.md new file mode 100644 index 0000000000000..bfc113c2baf3f --- /dev/null +++ b/apps/oxlint/test/fixtures/plugin_eslint/output.snap.md @@ -0,0 +1,27 @@ +# Exit code +1 + +# stdout +``` + x eslint-js(array-bracket-newline): A linebreak is required before ']'. + ,-[files/index.js:3:10] + 2 | const a = [ + 3 | 1, 2, 3]; + : ^ + 4 | + `---- + + x eslint-js(no-restricted-syntax): Use `new` keyword when throwing an `Error`. + ,-[files/index.js:6:7] + 5 | // Violation: no-restricted-syntax (throw Error without `new`) + 6 | throw TypeError("bad"); + : ^^^^^^^^^^^^^^^^ + `---- + +Found 0 warnings and 2 errors. +Finished in Xms on 1 file with 2 rules using X threads. +``` + +# stderr +``` +``` diff --git a/apps/oxlint/tsdown.config.ts b/apps/oxlint/tsdown.config.ts index f842aa3da5360..6048fe385fc0f 100644 --- a/apps/oxlint/tsdown.config.ts +++ b/apps/oxlint/tsdown.config.ts @@ -2,6 +2,9 @@ import fs from "node:fs"; import { join as pathJoin, relative as pathRelative, dirname } from "node:path"; import { defineConfig } from "tsdown"; import { parseSync, Visitor } from "oxc-parser"; +// oxlint-disable-next-line typescript/ban-ts-comment +// @ts-ignore - file is generated and not checked in to git +import ruleNames from "./src-js/generated/plugin-eslint/rule_names.ts"; import type { Plugin } from "rolldown"; @@ -65,6 +68,25 @@ const pluginsPkgConfig = defineConfig({ define: definedGlobals, }); +// Base config for `oxlint-plugin-eslint` package +const pluginEslintPkgConfig = defineConfig({ + ...commonConfig, + outDir: "dist-pkg-plugin-eslint", + minify: minifyConfig, + // `build.ts` deletes the directory before TSDown runs. + // This allows generating the ESM and CommonJS builds in the same directory. + clean: false, + dts: false, +}); + +// Build entries for `oxlint-plugin-eslint` rule files. +// Each rule is a separate CJS file, lazy-loaded on demand. +const pluginEslintRulesEntries: Record = {}; +for (const ruleName of ruleNames) { + pluginEslintRulesEntries[`rules/${ruleName}`] = + `src-js/generated/plugin-eslint/rules/${ruleName}.cjs`; +} + // Plugins. // Only remove debug assertions in release build. const plugins = [createReplaceGlobalsPlugin()]; @@ -108,6 +130,18 @@ export default defineConfig([ format: "commonjs", dts: false, }, + + // `oxlint-plugin-eslint` package + { + ...pluginEslintPkgConfig, + entry: { index: "src-js/plugin-eslint/index.ts" }, + format: "esm", + }, + { + ...pluginEslintPkgConfig, + entry: pluginEslintRulesEntries, + format: "commonjs", + }, ]); /** diff --git a/npm/oxlint-plugin-eslint/CHANGELOG.md b/npm/oxlint-plugin-eslint/CHANGELOG.md new file mode 100644 index 0000000000000..e30b69168493c --- /dev/null +++ b/npm/oxlint-plugin-eslint/CHANGELOG.md @@ -0,0 +1,3 @@ +# Changelog + +All notable changes to this package will be documented in this file. diff --git a/npm/oxlint-plugin-eslint/README.md b/npm/oxlint-plugin-eslint/README.md new file mode 100644 index 0000000000000..e8553d24ff4b7 --- /dev/null +++ b/npm/oxlint-plugin-eslint/README.md @@ -0,0 +1,36 @@ +# oxlint-plugin-eslint + +ESLint's built-in rules as an Oxlint plugin. + +This package exports all of ESLint's built-in rules as a JS plugin that Oxlint users can use. + +Allows using ESLint rules that Oxlint doesn't implement natively yet. + +More details in [Oxlint docs](https://oxc.rs/docs/guide/usage/linter/js-plugins). + +## Usage + +Install the package: + +```sh +npm install --save-dev oxlint-plugin-eslint +``` + +Add to your Oxlint config: + +```json +{ + "jsPlugins": ["oxlint-plugin-eslint"], + "rules": { + "eslint-js/no-restricted-syntax": [ + "error", + { + "selector": "ThrowStatement > CallExpression[callee.name=/Error$/]", + "message": "Use `new` keyword when throwing an `Error`." + } + ] + } +} +``` + +All rules are prefixed with `eslint-js/`, to distinguish from Oxlint's Rust implementation of ESLint rules. diff --git a/npm/oxlint-plugin-eslint/package.json b/npm/oxlint-plugin-eslint/package.json new file mode 100644 index 0000000000000..b4bbc9370b815 --- /dev/null +++ b/npm/oxlint-plugin-eslint/package.json @@ -0,0 +1,33 @@ +{ + "name": "oxlint-plugin-eslint", + "version": "1.52.0", + "description": "ESLint's built-in rules as an Oxlint plugin", + "keywords": [ + "eslint", + "linter", + "oxlint", + "plugin" + ], + "homepage": "https://oxc.rs/docs/guide/usage/linter/js-plugins", + "bugs": "https://github.com/oxc-project/oxc/issues", + "license": "MIT", + "author": "Boshen and oxc contributors", + "repository": { + "type": "git", + "url": "git+https://github.com/oxc-project/oxc.git", + "directory": "npm/oxlint-plugin-eslint" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "files": [ + "index.js", + "rules", + "README.md" + ], + "type": "module", + "main": "index.js", + "engines": { + "node": "^20.19.0 || >=22.12.0" + } +} diff --git a/oxc_release.toml b/oxc_release.toml index b2fc598852416..7ecfa2259b077 100644 --- a/oxc_release.toml +++ b/oxc_release.toml @@ -21,6 +21,7 @@ versioned_files = [ "apps/oxlint/package.json", "crates/oxc_linter/Cargo.toml", "npm/oxlint/package.json", + "npm/oxlint-plugin-eslint/package.json", "npm/oxlint-plugins/package.json", ] diff --git a/oxfmtrc.jsonc b/oxfmtrc.jsonc index ec7ab6bbe8604..df032436d9a8e 100644 --- a/oxfmtrc.jsonc +++ b/oxfmtrc.jsonc @@ -7,6 +7,7 @@ "!apps/oxlint/test/fixtures/**", "apps/oxlint/test/fixtures/bom/files/**", "apps/oxlint/test/fixtures/fixes/files/**", + "apps/oxlint/test/fixtures/plugin_eslint/files/**", "apps/oxlint/test/fixtures/suggestions/files/**", "apps/oxlint/test/fixtures/sourceCode_token_methods/files/**", "apps/oxlint/test/fixtures/tokens/files/**", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 12b00f2419b8d..e45ddb935b030 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -132,6 +132,9 @@ importers: '@types/node': specifier: 'catalog:' version: 24.1.0 + '@types/serialize-javascript': + specifier: ^5.0.4 + version: 5.0.4 '@typescript-eslint/parser': specifier: ^8.54.0 version: 8.56.1(eslint@9.39.3(jiti@2.6.1))(typescript@5.9.3) @@ -168,6 +171,9 @@ importers: rolldown: specifier: 'catalog:' version: 1.0.0-rc.8 + serialize-javascript: + specifier: ^7.0.4 + version: 7.0.4 tsdown: specifier: 'catalog:' version: 0.21.1(@arethetypeswrong/core@0.18.2)(publint@0.3.18)(typescript@5.9.3) @@ -287,6 +293,8 @@ importers: specifier: '>=0.15.0' version: 0.16.0 + npm/oxlint-plugin-eslint: {} + npm/oxlint-plugins: {} npm/runtime: {} @@ -2643,6 +2651,9 @@ packages: '@types/node@25.0.2': resolution: {integrity: sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==} + '@types/serialize-javascript@5.0.4': + resolution: {integrity: sha512-Z2R7UKFuNWCP8eoa2o9e5rkD3hmWxx/1L0CYz0k2BZzGh0PhEVMp9kfGiqEml/0IglwNERXZ2hwNzIrSz/KHTA==} + '@types/superagent@8.1.9': resolution: {integrity: sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==} @@ -4168,6 +4179,10 @@ packages: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} + serialize-javascript@7.0.4: + resolution: {integrity: sha512-DuGdB+Po43Q5Jxwpzt1lhyFSYKryqoNjQSA9M92tyw0lyHIOur+XCalOUe0KTJpyqzT8+fQ5A0Jf7vCx/NKmIg==} + engines: {node: '>=20.0.0'} + seroval-plugins@1.3.3: resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==} engines: {node: '>=10'} @@ -6620,6 +6635,8 @@ snapshots: dependencies: undici-types: 7.16.0 + '@types/serialize-javascript@5.0.4': {} + '@types/superagent@8.1.9': dependencies: '@types/cookiejar': 2.1.5 @@ -8315,6 +8332,8 @@ snapshots: transitivePeerDependencies: - supports-color + serialize-javascript@7.0.4: {} + seroval-plugins@1.3.3(seroval@1.3.2): dependencies: seroval: 1.3.2