diff --git a/UPSTREAM b/UPSTREAM index 29bcf51e..4c49fc11 100644 --- a/UPSTREAM +++ b/UPSTREAM @@ -8,3 +8,4 @@ scratch-storage 80b258d scratch-parser 7244904 scratch-audio 50b7ade eslint-config-scratch 87ee420 +scratch-webpack-configuration 31aad55 diff --git a/package.json b/package.json index 325a21c4..f3daaf09 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "start": "pnpm run gui start", "prepare": "husky", "build:dist": "cross-env NODE_ENV=production pnpm run build:full", - "build:full": "pnpm l10n build && pnpm audio build && pnpm storage build && pnpm render build && pnpm block build && pnpm vm build && pnpm paint build && node packages/gui/scripts/prepublish.mjs && pnpm gui build", + "build:full": "pnpm infra build && pnpm l10n build && pnpm audio build && pnpm storage build && pnpm render build && pnpm block build && pnpm vm build && pnpm paint build && node packages/gui/scripts/prepublish.mjs && pnpm gui build", "build": "pnpm block build && pnpm gui build", "test": "pnpm gui test:unit && pnpm block test && pnpm vm test", "performance": "pnpm vm performance", @@ -32,7 +32,8 @@ "storage": "pnpm --filter clipcc-storage", "paint": "pnpm --filter clipcc-paint", "parser": "pnpm --filter clipcc-parser", - "audio": "pnpm --filter clipcc-audio" + "audio": "pnpm --filter clipcc-audio", + "infra": "pnpm --filter clipcc-infra" }, "devDependencies": { "@changesets/changelog-git": "^0.2.1", diff --git a/packages/audio/package.json b/packages/audio/package.json index 0ea78238..b2d9b86c 100644 --- a/packages/audio/package.json +++ b/packages/audio/package.json @@ -2,7 +2,7 @@ "name": "clipcc-audio", "version": "3.2.0", "description": "audio engine for scratch 3.0", - "main": "dist.js", + "main": "dist/dist.js", "browser": "./src/index.js", "types": "./dist/types/index.d.ts", "scripts": { @@ -35,6 +35,7 @@ "@babel/eslint-parser": "7.28.6", "@babel/preset-env": "7.29.0", "babel-eslint": "10.0.3", + "clipcc-infra": "workspace:*", "eslint": "^9.39.2", "eslint-config-clipcc": "workspace:*", "tap": "21.0.1", diff --git a/packages/audio/webpack.config.js b/packages/audio/webpack.config.js index 69eb3b01..bfdec426 100644 --- a/packages/audio/webpack.config.js +++ b/packages/audio/webpack.config.js @@ -1,45 +1,16 @@ -const path = require('path'); +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); - -module.exports = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: 'cheap-module-source-map', +const config = new WebpackConfigBuilder({ + ...manifest, entry: { - dist: './src/index.js' - }, - output: { - path: __dirname, - library: 'AudioEngine', - libraryTarget: 'umd', - filename: '[name].js' - }, - module: { - rules: [{ - include: [ - path.resolve('src') - ], - test: /\.([cm]?ts|tsx)$/, - loader: 'ts-loader', - options: { - transpileOnly: true, - allowTsInNodeModules: true - } - }, { - test: /\.js$/, - include: path.resolve(__dirname, 'src'), - loader: 'babel-loader', - options: { - presets: [['@babel/preset-env', {targets: {browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']}}]] - } - }] + dist: manifest.entry }, externals: { 'audio-context': true, '@turbowarp/nanolog': true, 'startaudiocontext': true - }, - plugins: [ - new NodePolyfillPlugin() - ] -}; + } +}).get(); + +module.exports = config; diff --git a/packages/audio/webpack.manifest.js b/packages/audio/webpack.manifest.js new file mode 100644 index 00000000..b45b997e --- /dev/null +++ b/packages/audio/webpack.manifest.js @@ -0,0 +1,20 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ + +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); + +/** @satisfies {WebpackManifest} */ +const manifest = { + entry: './src/index.js', + libraryName: 'AudioEngine', + rootPath: __dirname, + plugins: [ + new NodePolyfillPlugin({ + includeAliases: ['events'] + }) + ] +}; + +module.exports = manifest; diff --git a/packages/block/eslint.config.mjs b/packages/block/eslint.config.mjs index 2700ff88..e6f677ec 100644 --- a/packages/block/eslint.config.mjs +++ b/packages/block/eslint.config.mjs @@ -62,6 +62,7 @@ export default [ 'scripts/**/*.js', 'tests/**/*.js', 'webpack.config.js', + 'webpack.manifest.js', 'jest.config.js' ], languageOptions: { diff --git a/packages/block/package.json b/packages/block/package.json index 8d3fefa8..49b5cb6f 100644 --- a/packages/block/package.json +++ b/packages/block/package.json @@ -28,6 +28,7 @@ "@jest/environment": "^30.3.0", "@jest/environment-jsdom-abstract": "^30.3.0", "@jest/globals": "^30.3.0", + "clipcc-infra": "workspace:*", "clipcc-l10n": "workspace:~", "copy-webpack-plugin": "^14.0.0", "eslint": "^9.39.2", @@ -38,7 +39,6 @@ "jest-canvas-mock": "^2.5.2", "jsdom": "^28.1.0", "lodash.defaultsdeep": "^4.6.1", - "raw-loader": "^4.0.2", "source-map-loader": "^5.0.0", "ts-jest": "^29.4.5", "ts-loader": "^9.5.4", diff --git a/packages/block/webpack.config.js b/packages/block/webpack.config.js index 12bf3b29..4baa854f 100644 --- a/packages/block/webpack.config.js +++ b/packages/block/webpack.config.js @@ -1,90 +1,67 @@ const path = require('path'); -const defaultsDeep = require('lodash.defaultsdeep'); const CopyWebpackPlugin = require('copy-webpack-plugin'); +const WebpackConfigBuilder = require('../infra'); +const manifest = require('./webpack.manifest'); -const baseConfig = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: process.env.NODE_ENV === 'production' ? false : 'eval-cheap-module-source-map', - entry: './src/index.ts', - output: { - library: 'ScratchBlocks', - filename: '[name].js' - }, - resolve: { - extensions: ['.ts', '.js'] - }, - module: { - rules: [{ - test: /\.css$/, - use: 'raw-loader', - include: path.resolve(__dirname, 'src') - }, { - test: /\.ts$/, - use: 'ts-loader', - exclude: /node_modules/ - }, { - test: /_compressed\.js$/, - enforce: 'pre', - use: 'source-map-loader', - include: /blockly/ - }] - }, - ignoreWarnings: [/Failed to parse source map/] + +const createConfig = (overrideManifest) => { + const config = new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest + }).get(); + + config.ignoreWarnings = [/Failed to parse source map/]; + + return config; }; +// Playground +const playground = createConfig({ + distPath: './build', + playground: 8071, + target: 'web', + plugins: [ + new CopyWebpackPlugin({ + patterns: [{ + from: path.resolve(require.resolve('blockly'), '../media'), + to: 'media' + }, { + from: 'media', + to: 'media', + force: true + }, { + from: 'tests/playground.html', + to: 'index.html' + }, { + from: 'tests/toolbox.json', + to: 'toolbox.json' + }, { + from: 'msg/messages.js' + }] + }) + ] +}); +playground.devServer.static = false; + +// Node-compatible +const node = createConfig({ + distPath: './dist/node', + target: 'node', + externals: { + bufferutil: true, + 'utf-8-validate': true, + canvas: true + } +}); + +// Web-compatible +const web = createConfig({ + distPath: './dist/web', + target: 'web' +}); + module.exports = [ - // Playground - defaultsDeep({}, baseConfig, { - target: 'web', - devServer: { - static: false, - host: '0.0.0.0', - port: process.env.PORT || 8071 - }, - output: { - libraryTarget: 'umd', - path: path.resolve(__dirname, 'build') - }, - plugins: [ - new CopyWebpackPlugin({ - patterns: [{ - from: path.resolve(require.resolve('blockly'), '../media'), - to: 'media' - }, { - from: 'media', - to: 'media', - force: true - }, { - from: 'tests/playground.html', - to: 'index.html' - }, { - from: 'tests/toolbox.json', - to: 'toolbox.json' - }, { - from: 'msg/messages.js' - }] - }) - ] - }), - // Node-compatible - defaultsDeep({}, baseConfig, { - target: 'node', - output: { - libraryTarget: 'commonjs2', - path: path.resolve(__dirname, 'dist', 'node') - }, - externals: { - bufferutil: true, - 'utf-8-validate': true, - canvas: true - } - }), - // Web-comptible - defaultsDeep({}, baseConfig, { - target: 'web', - output: { - libraryTarget: 'umd', - path: path.resolve(__dirname, 'dist', 'web') - } - }) + playground, + node, + web ]; diff --git a/packages/block/webpack.manifest.js b/packages/block/webpack.manifest.js new file mode 100644 index 00000000..77ba7402 --- /dev/null +++ b/packages/block/webpack.manifest.js @@ -0,0 +1,25 @@ +// @ts-check +/** + * @import { WebpackManifest } from '../infra'; + */ + +/** @satisfies {WebpackManifest} */ +const manifest = { + libraryName: 'ScratchBlocks', + rootPath: __dirname, + devTool: process.env.NODE_ENV === 'production' ? false : 'eval-cheap-module-source-map', + entry: './src/index.ts', + enableTs: true, + rules: [{ + test: /\.css$/, + type: 'asset/source', + include: 'src' + }, { + test: /_compressed\.js$/, + enforce: 'pre', + use: 'source-map-loader', + include: /blockly/ + }] +}; + +module.exports = manifest; diff --git a/packages/gui/package.json b/packages/gui/package.json index dbf1c3e1..c58124f5 100644 --- a/packages/gui/package.json +++ b/packages/gui/package.json @@ -104,6 +104,7 @@ "babel-jest": "23.6.0", "babel-loader": "^10.1.0", "chromedriver": "146.0.1", + "clipcc-infra": "workspace:*", "cross-fetch": "^3.1.8", "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.7", diff --git a/packages/gui/webpack.config.js b/packages/gui/webpack.config.js index 7e21dbfd..608864b3 100644 --- a/packages/gui/webpack.config.js +++ b/packages/gui/webpack.config.js @@ -1,342 +1,190 @@ -const defaultsDeep = require('lodash.defaultsdeep'); const path = require('path'); const webpack = require('webpack'); const {version} = require('../../package.json'); +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); + // Plugins const CopyWebpackPlugin = require('copy-webpack-plugin'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin'); const STATIC_PATH = process.env.STATIC_PATH || '/static'; -const base = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: 'cheap-module-source-map', - devServer: { - static: path.resolve(__dirname, 'build'), - host: '0.0.0.0', - port: process.env.PORT || 8601 - }, - output: { - library: 'GUI', - filename: '[name].js', - chunkFilename: 'chunks/[name].js' - }, - resolve: { - extensions: ['.ts', '.js', '.tsx', '.jsx'], - alias: { - 'text-encoding': 'fastestsmallesttextencoderdecoder', - 'clipcc-vm': path.resolve(__dirname, '../vm/src/index.js'), - 'clipcc-block': path.resolve(__dirname, '../block/src/index.ts'), - 'clipcc-render': path.resolve(__dirname, '../render/src/index.js'), - 'clipcc-audio': path.resolve(__dirname, '../audio/src/index.js') - } - }, - snapshot: { - managedPaths: [ - /^.+?[\\/]node_modules[\\/](?!scratch-(blocks|l10n|paint|render|storage|vm))[\\/]/ - ] - }, - module: { - rules: [{ - include: [ - path.resolve(__dirname, 'src'), - path.resolve(__dirname, '../vm/src'), - path.resolve(__dirname, '../block/src'), - path.resolve(__dirname, '../audio/src') - ], - test: /\.([cm]?ts|tsx)$/, - loader: 'ts-loader', - options: { - transpileOnly: true, - allowTsInNodeModules: true - } - }, - { - test: /\.jsx?$/, - loader: 'babel-loader', - exclude: { - and: [/node_modules/], - not: [ - /node_modules[\\/](scratch|clipcc)-[^\\/]+[\\/]src/ - ] - }, - options: { - // Explicitly disable babelrc so we don't catch various config - // in much lower dependencies. - babelrc: false, - plugins: [ - ['react-intl', { - messagesDir: './translations/messages/' - }]], - presets: ['@babel/preset-env', '@babel/preset-react'] - } - }, - { - test: /\.css$/, - exclude: path.resolve(__dirname, '../block/src'), - use: [{ - loader: 'style-loader' - }, { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[name]_[local]_[hash:base64:5]', - exportLocalsConvention: 'camelCase' - }, - importLoaders: 1 - } - }, { - loader: 'postcss-loader', - options: { - postcssOptions: { - plugins: [ - 'postcss-import', - 'autoprefixer' - ] - } - } - }] - }, { - test: /\.css$/, - include: path.resolve(__dirname, '../block/src'), - type: 'asset/source' - }, { - test: /\.hex$/, - type: 'asset/inline', - generator: { - dataUrl: content => `data:text/plain;base64,${content.toString('base64')}` - } - }, { - resourceQuery: '?arrayBuffer', - type: 'javascript/auto', - use: 'arraybuffer-loader' - }, { - resourceQuery: /raw/, - type: 'asset/source' - }] +const createConfig = overrideManifest => { + const config = new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest + }).get(); + + return config; +}; + +if (process.env.ANALYZE) { + // eslint-disable-next-line global-require + const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); + manifest.plugins.push(new BundleAnalyzerPlugin()); +} + +/** @type {webpack.Configuration} */ +const configs = []; + +const fallbackAssetRule = { + test: /\.(svg|png|wav|gif|jpg)$/, + resourceQuery: {not: [/raw/]}, + type: 'asset/inline' +}; + +// to run editor examples +const playground = createConfig({ + entry: { + gui: './src/playground/index.jsx', + blocksonly: './src/playground/blocks-only.jsx', + lifecycle: './src/playground/lifecycle-test.jsx', + compatibilitytesting: './src/playground/compatibility-testing.jsx', + player: './src/playground/player.jsx' }, + distPath: path.resolve(__dirname, 'build'), + rules: [...manifest.rules, fallbackAssetRule], optimization: { - minimizer: [ - new TerserPlugin({ - include: /\.min\.js$/ - }), - new ImageMinimizerPlugin({ - minimizer: { - implementation: ImageMinimizerPlugin.imageminMinify, - options: { - plugins: [ - ['gifsicle', {interlaced: true}], - ['jpegtran', {progressive: true}], - ['optipng', {optimizationLevel: 5}] - ] - } + splitChunks: { + chunks: 'async', + minChunks: 2, + maxInitialRequests: 5, + cacheGroups: { + default: false, + defaultVendors: false, + lib: { + test: /[\\/]node_modules[\\/]/, + name: 'lib.min', + chunks: 'initial', + priority: 10, + reuseExistingChunk: true, + enforce: true } - }) - ] + } + }, + runtimeChunk: { + name: 'lib.min' + } }, plugins: [ - new NodePolyfillPlugin(), + ...manifest.plugins, + new webpack.DefinePlugin({ + 'process.env.DEBUG': Boolean(process.env.DEBUG), + 'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`, + 'clipcc.VERSION': version, + 'clipcc.BUILD_TIME': Date.now() + }), + new HtmlWebpackPlugin({ + chunks: ['lib.min', 'gui'], + template: 'src/playground/index.ejs', + title: 'ClipCC GUI' + }), + new HtmlWebpackPlugin({ + chunks: ['lib.min', 'blocksonly'], + template: 'src/playground/index.ejs', + filename: 'blocks-only.html', + title: 'ClipCC GUI: Blocks Only Example' + }), + new HtmlWebpackPlugin({ + chunks: ['lib.min', 'compatibilitytesting'], + template: 'src/playground/index.ejs', + filename: 'compatibility-testing.html', + title: 'ClipCC GUI: Compatibility Testing' + }), + new HtmlWebpackPlugin({ + chunks: ['lib.min', 'player'], + template: 'src/playground/index.ejs', + filename: 'player.html', + title: 'ClipCC GUI: Player Example' + }), + new HtmlWebpackPlugin({ + chunks: ['lib.min', 'lifecycle'], + template: 'src/playground/index.ejs', + filename: 'lifecycle.html', + title: 'ClipCC GUI: Lifecycle Test' + }), new CopyWebpackPlugin({ patterns: [ { - from: '../block/media', - to: 'static/blocks-media/default' - }, + from: 'static', + to: 'static' + } + ] + }), + new CopyWebpackPlugin({ + patterns: [ { - from: '../block/media', - to: 'static/blocks-media/high-contrast' - }, + from: 'extensions/**', + to: 'static', + context: 'src/examples' + } + ] + }), + new CopyWebpackPlugin({ + patterns: [ { - from: 'src/lib/themes/high-contrast/blocks-media', - to: 'static/blocks-media/high-contrast', - force: true + from: 'extension-worker.{js,js.map}', + context: '../vm/dist/web' } ] }) ] -}; - -if (!process.env.CI) { - base.plugins.push(new webpack.ProgressPlugin()); -} +}); -if (process.env.ANALYZE) { - // eslint-disable-next-line global-require - const {BundleAnalyzerPlugin} = require('webpack-bundle-analyzer'); - base.plugins.push(new BundleAnalyzerPlugin()); -} +configs.push(playground); -if (base.mode === 'development') { - base.module.rules.push({ - test: /blocks-msgs\.js$/, - include: [ - path.resolve(__dirname, '../l10n/locales') - ], - use: [{ - loader: path.resolve(__dirname, 'scripts/block-message-loader.js') - }, { - loader: 'babel-loader' - }], - enforce: 'pre' - }); -} - -module.exports = [ - // to run editor examples - defaultsDeep({}, base, { +if (process.env.NODE_ENV === 'production' || process.env.BUILD_MODE === 'dist') { + const lib = createConfig({ + target: 'web', + publicPath: `${STATIC_PATH}/`, entry: { - gui: './src/playground/index.jsx', - blocksonly: './src/playground/blocks-only.jsx', - lifecycle: './src/playground/lifecycle-test.jsx', - compatibilitytesting: './src/playground/compatibility-testing.jsx', - player: './src/playground/player.jsx' - }, - output: { - path: path.resolve(__dirname, 'build'), - filename: '[name].js' - }, - module: { - rules: base.module.rules.concat([ - { - test: /\.(svg|png|wav|gif|jpg)$/, - resourceQuery: {not: [/raw/]}, - type: 'asset/inline' - } - ]) + 'scratch-gui': './src/index.js' }, - optimization: { - splitChunks: { - chunks: 'async', - minChunks: 2, - maxInitialRequests: 5, - cacheGroups: { - default: false, - defaultVendors: false, - lib: { - test: /[\\/]node_modules[\\/]/, - name: 'lib.min', - chunks: 'initial', - priority: 10, - reuseExistingChunk: true, - enforce: true - } - } - }, - runtimeChunk: { - name: 'lib.min' - } + externals: { + 'react': 'react', + 'react-dom': 'react-dom' }, - plugins: base.plugins.concat([ - new webpack.DefinePlugin({ - 'process.env.DEBUG': Boolean(process.env.DEBUG), - 'process.env.GA_ID': `"${process.env.GA_ID || 'UA-000000-01'}"`, - 'clipcc.VERSION': version, - 'clipcc.BUILD_TIME': Date.now() - }), - new HtmlWebpackPlugin({ - chunks: ['lib.min', 'gui'], - template: 'src/playground/index.ejs', - title: 'ClipCC GUI' - }), - new HtmlWebpackPlugin({ - chunks: ['lib.min', 'blocksonly'], - template: 'src/playground/index.ejs', - filename: 'blocks-only.html', - title: 'ClipCC GUI: Blocks Only Example' - }), - new HtmlWebpackPlugin({ - chunks: ['lib.min', 'compatibilitytesting'], - template: 'src/playground/index.ejs', - filename: 'compatibility-testing.html', - title: 'ClipCC GUI: Compatibility Testing' - }), - new HtmlWebpackPlugin({ - chunks: ['lib.min', 'player'], - template: 'src/playground/index.ejs', - filename: 'player.html', - title: 'ClipCC GUI: Player Example' - }), - new HtmlWebpackPlugin({ - chunks: ['lib.min', 'lifecycle'], - template: 'src/playground/index.ejs', - filename: 'lifecycle.html', - title: 'ClipCC GUI: Lifecycle Test' - }), + rules: [...manifest.rules, fallbackAssetRule], + plugins: [ new CopyWebpackPlugin({ patterns: [ { - from: 'static', - to: 'static' - } - ] - }), - new CopyWebpackPlugin({ - patterns: [ - { - from: 'extensions/**', - to: 'static', - context: 'src/examples' + from: 'extension-worker.{js,js.map}', + context: '../vm/dist/web' } ] }), + // Include library JSON files for scratch-desktop to use for downloading new CopyWebpackPlugin({ patterns: [ { - from: 'extension-worker.{js,js.map}', - context: '../vm/dist/web' + from: 'src/lib/libraries/*.json', + to: 'libraries/[name][ext]' } ] }) - ]) - }) -].concat( - process.env.NODE_ENV === 'production' || process.env.BUILD_MODE === 'dist' ? ( - // export as library - defaultsDeep({}, base, { - target: 'web', - entry: { - 'scratch-gui': './src/index.js' - }, - output: { - libraryTarget: 'umd', - path: path.resolve('dist'), - publicPath: `${STATIC_PATH}/` - }, - externals: { - 'react': 'react', - 'react-dom': 'react-dom' - }, - module: { - rules: base.module.rules.concat([ - { - test: /\.(svg|png|wav|gif|jpg)$/, - resourceQuery: {not: [/raw/]}, - type: 'asset/inline' - } - ]) - }, - plugins: base.plugins.concat([ - new CopyWebpackPlugin({ - patterns: [ - { - from: 'extension-worker.{js,js.map}', - context: '../vm/dist/web' - } - ] - }), - // Include library JSON files for scratch-desktop to use for downloading - new CopyWebpackPlugin({ - patterns: [ - { - from: 'src/lib/libraries/*.json', - to: 'libraries/[name][ext]' + ], + optimization: { + minimizer: [ + new ImageMinimizerPlugin({ + minimizer: { + implementation: ImageMinimizerPlugin.imageminMinify, + options: { + plugins: [ + ['gifsicle', {interlaced: true}], + ['jpegtran', {progressive: true}], + ['optipng', {optimizationLevel: 5}] + ] } - ] + } }) - ]) - })) : [] -); + ] + } + }); + configs.push(lib); +} + +module.exports = configs; diff --git a/packages/gui/webpack.manifest.js b/packages/gui/webpack.manifest.js new file mode 100644 index 00000000..6040d326 --- /dev/null +++ b/packages/gui/webpack.manifest.js @@ -0,0 +1,80 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ +const webpack = require('webpack'); +const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); + +/** @satisfies {WebpackManifest} */ +const base = { + libraryName: 'GUI', + playground: 8601, + rootPath: __dirname, + entry: './src/index.js', + + enableReact: true, + enableTs: true, + + sourcePaths: [ + path.dirname(require.resolve('react-tabs/package.json')) // for react-tabs' stylesheets + ], + workspacePackages: [ + 'clipcc-vm', + 'clipcc-block', + 'clipcc-paint', + 'clipcc-render' + ], + snapshot: { + managedPaths: [ + /^.+?[\\/]node_modules[\\/](?!scratch-(blocks|l10n|paint|render|storage|vm))[\\/]/ + ] + }, + /** @type {NonNullable} */ + rules: [], + /** @type {NonNullable} */ + plugins: [ + new NodePolyfillPlugin({ + includeAliases: ['events', 'buffer'] + }), + new CopyWebpackPlugin({ + patterns: [ + { + from: '../block/media', + to: 'static/blocks-media/default' + }, + { + from: '../block/media', + to: 'static/blocks-media/high-contrast' + }, + { + from: 'src/lib/themes/high-contrast/blocks-media', + to: 'static/blocks-media/high-contrast', + force: true + } + ] + }) + ] +}; + +if (process.env.NODE_ENV !== 'production') { + base.rules.push({ + test: /blocks-msgs\.js$/, + include: [ + path.resolve(__dirname, '../l10n/locales') + ], + use: [{ + loader: path.resolve(__dirname, 'scripts/block-message-loader.js') + }, { + loader: 'babel-loader' + }], + enforce: 'pre' + }); +} + +if (!process.env.CI) { + base.plugins.push(new webpack.ProgressPlugin()); +} + +module.exports = base; diff --git a/packages/infra/.editorconfig b/packages/infra/.editorconfig new file mode 100644 index 00000000..3f4f10e8 --- /dev/null +++ b/packages/infra/.editorconfig @@ -0,0 +1,17 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 +indent_size = 4 +trim_trailing_whitespace = true + +[*.js] +indent_style = space + +[package.json] +indent_size = 2 + +[renovate.json5] +indent_size = 2 diff --git a/packages/infra/.gitattributes b/packages/infra/.gitattributes new file mode 100644 index 00000000..6313b56c --- /dev/null +++ b/packages/infra/.gitattributes @@ -0,0 +1 @@ +* text=auto eol=lf diff --git a/packages/infra/.gitignore b/packages/infra/.gitignore new file mode 100644 index 00000000..c6bba591 --- /dev/null +++ b/packages/infra/.gitignore @@ -0,0 +1,130 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* +.pnpm-debug.log* + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage +*.lcov + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Snowpack dependency directory (https://snowpack.dev/) +web_modules/ + +# TypeScript cache +*.tsbuildinfo + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional stylelint cache +.stylelintcache + +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# parcel-bundler cache (https://parceljs.org/) +.cache +.parcel-cache + +# Next.js build output +.next +out + +# Nuxt.js build / generate output +.nuxt +dist + +# Gatsby files +.cache/ +# Comment in the public line in if your project uses Gatsby and not Next.js +# https://nextjs.org/blog/next-9-1#public-directory-support +# public + +# vuepress build output +.vuepress/dist + +# vuepress v2.x temp and cache directory +.temp +.cache + +# Docusaurus cache and generated files +.docusaurus + +# Serverless directories +.serverless/ + +# FuseBox cache +.fusebox/ + +# DynamoDB Local files +.dynamodb/ + +# TernJS port file +.tern-port + +# Stores VSCode versions used for testing VSCode extensions +.vscode-test + +# yarn v2 +.yarn/cache +.yarn/unplugged +.yarn/build-state.yml +.yarn/install-state.gz +.pnp.* diff --git a/packages/infra/LICENSE b/packages/infra/LICENSE new file mode 100644 index 00000000..ab7b64e4 --- /dev/null +++ b/packages/infra/LICENSE @@ -0,0 +1,14 @@ +BSD 3-Clause License + +Copyright (c) Clipteam +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/packages/infra/README.md b/packages/infra/README.md new file mode 100644 index 00000000..326e257a --- /dev/null +++ b/packages/infra/README.md @@ -0,0 +1,235 @@ +# clipcc-infra + +Provides shared infrastructure for other clipcc packages. Right now that mainly means a webpack configuration builder with a consistent set of defaults for JS, TypeScript, React, CSS modules, playground builds, and source-linked workspace packages. + +## Webpack + +### Basic Usage + +Add something like this to your `webpack.config.js` file: + +```javascript +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ + +const WebpackConfigBuilder = require('clipcc-infra'); + +/** @type {WebpackManifest} */ +const manifest = { + rootPath: __dirname, + libraryName: 'my-library', + entry: './src/index.js', + enableReact: true, + enableTs: true, + plugins: [], + rules: [] +}; + +if (process.env.FOO === 'bar') { + manifest.plugins.push(new MyCustomPlugin()); + manifest.rules.push({ + test: /\.foo$/, + use: [/* FOO loaders */] + }); +} + +const builder = new WebpackConfigBuilder(manifest); + +module.exports = builder.get(); +``` + +This produces a webpack 5 configuration with sensible defaults for clipcc packages. Custom plugins, aliases, optimization settings, snapshot settings, and module rules are merged into that generated configuration instead of replacing it wholesale. + +### Workspace Packages + +If your project is part of a monorepo, add package names to `workspacePackages` so other workspace packages can be consumed from source safely. This is useful when related packages are under active development and you want webpack to compile them directly instead of relying on a prebuilt local install. + +To make a package consumable from source, add a `webpack.manifest.js` file at the package root: + +```javascript +// @ts-check +/** + * @import { WebpackManifest } from '../infra'; + */ + +/** @satisfies {WebpackManifest} */ +const manifest = { + libraryName: 'ClipCCRender', + entry: './src/index.js', + rootPath: __dirname, + enableTs: true +}; + +module.exports = manifest; +``` + +That manifest is what downstream packages read, so keep `entry` aligned with the public source entry for the package. + +Then make the package's `webpack.config.js` build from that manifest so it still works when built on its own: + +```javascript +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); + +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +const createConfig = overrideManifest => new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest +}).get(); + +// Playground +const playground = createConfig({ + target: 'web', + distPath: './playground', + entry: { + playground: './src/playground/playground.js', + queryPlayground: './src/playground/queryPlayground.js' + }, + playground: 8361 +}); + +playground.plugins.push( + new CopyWebpackPlugin({ + patterns: [{ + context: 'src/playground', + from: '*.+(html|css)' + }] + }) +); + +// Web-compatible +const web = createConfig({ + target: 'web', + distPath: './dist/web', + entry: { + 'scratch-render': './src/index.js', + 'scratch-render.min': './src/index.js' + } +}); + +// Node-compatible +const node = createConfig({ + target: 'node' +}); + +module.exports = [playground, web, node]; +``` + +Other packages can then consume that package from source by listing it in `workspacePackages`: + +```javascript +// @ts-check +/** + * @import { WebpackManifest } from '../infra'; + */ +const CopyWebpackPlugin = require('copy-webpack-plugin'); + +/** @satisfies {WebpackManifest} */ +const base = { + libraryName: 'Consumer', + entry: './src/index.js', + rootPath: __dirname, + enableReact: true, + enableTs: true, + workspacePackages: [ + 'clipcc-render' + ], + plugins: [ + new CopyWebpackPlugin({ + /* ... */ + }) + ] +}; + +module.exports = base; +``` + +When a package is listed in `workspacePackages`, the builder will: + +1. Add an exact-match alias from `package-name$` to that package's entry file, so `import 'package-name'` resolves to its declared source entry. +2. Add a prefix alias from `package-name` to that package's source directory, so `import 'package-name/foo'` resolves inside its source tree. +3. Expand the default JS, TS, and React CSS handling to include that package's source paths. +4. Merge the package's own aliases, rules, and snapshot configuration into the generated config. +5. Recursively load its own `workspacePackages`, so nested source dependencies can also be compiled from source. + +### Default Behavior + +The builder currently does these things: + +- Resolves `entry`, `srcPath`, `distPath`, rule `include` and `exclude` paths, alias values, and snapshot paths relative to `rootPath`. +- Uses `cheap-module-source-map` by default for `devtool`. +- Uses `target: 'web'` by default. +- Emits UMD output for non-node targets and CommonJS output for node-like targets. +- Adds `babel-loader` with `@babel/preset-env` for JS sources. +- Adds `@babel/preset-react` when `enableReact` is enabled. +- Adds `ts-loader` with `transpileOnly: true` when `enableTs` is enabled. +- Adds CSS module handling for `.css` files under React source paths when `enableReact` is enabled. +- Injects `Buffer` through `webpack.ProvidePlugin`. +- Enables `devServer` when `playground` is set. +- Preserves user-supplied `optimization` and appends a terser pass for files that end with `.min.js`. +- Enables `splitChunks.chunks = 'async'` when `shouldSplitChunks` is enabled. +- Sets `externalsPresets.node = true` and disables `nodePrefixForCoreModules` for node-like targets. + +### Asset Handling + +The built-in rules currently add only a few asset-oriented behaviors: + +```js +import firmware from './firmware.hex'; +import bytes from './sound.wav?arrayBuffer'; +import text from './template.svg?raw'; +``` + +- `.hex` files are emitted as inline `data:` URLs using base64 text content. +- `?arrayBuffer` uses `arraybuffer-loader`. +- `?raw` uses webpack's `asset/source` behavior. + +If you need additional asset module behavior such as `asset/resource` or `asset/inline` for other file types, add a custom rule in your manifest. + +### API + +#### `new WebpackConfigBuilder(manifest: WebpackManifest)` + +Creates a builder instance and normalizes the manifest immediately. Any packages listed in `workspacePackages` are resolved and merged during construction. + +Required manifest fields: + +- `entry`: The webpack entry definition for the package. +- `libraryName`: The UMD global name used for non-node targets. +- `rootPath`: The base directory used to resolve relative paths in the manifest. + +Optional manifest fields: + +- `target`: Webpack target. Node-like targets switch output to CommonJS and enable `externalsPresets.node`. +- `devTool`: Source map mode for the generated config. Defaults to `cheap-module-source-map`. +- `srcPath`: Main source directory. Defaults to `./src`. +- `distPath`: Output directory for the bundle. Defaults to `./dist`. +- `publicPath`: Runtime base URL for emitted assets. Defaults to `/`. +- `sourcePaths`: Additional source directories that should go through the default JS and TS pipeline. +- `enableReact`: Enables React JS transpilation and CSS module handling for React source paths. +- `enableTs`: Enables `ts-loader` for TypeScript and TSX entries. +- `shouldSplitChunks`: Enables async chunk splitting under `optimization.splitChunks`. +- `rules`: Additional webpack rules appended after the built-in rules. +- `plugins`: Additional webpack plugins appended after the built-in `Buffer` provider. +- `alias`: Extra `resolve.alias` entries. These can override aliases inherited from workspace packages. +- `snapshot`: Snapshot configuration merged into the final webpack config. +- `playground`: Enables `devServer`; pass `true` to use `PORT` or `auto`, or pass a number to force a port. +- `externals`: Webpack externals passed through to the final config. +- `optimization`: Extra optimization settings merged into the final config before the default `.min.js` terser entry is appended. +- `workspacePackages`: Package names to consume from source through `webpack.manifest.js`. + +#### `builder.addWorkspacePackage(packageName: string)` + +Loads a package manifest from `packageName/webpack.manifest.js` and merges its source-aware aliases, rules, snapshot settings, and recursive workspace package dependencies into the current builder. + +#### `builder.get(): Configuration` + +Builds and returns the final webpack configuration object. + +#### Browser Targets + +This package does not manage a shared `browserslist` definition. If your Babel and other tooling need browser targeting, configure `browserslist` in your package's `package.json` or a top-level `.browserslistrc` file so the same target matrix can be reused everywhere. + diff --git a/packages/infra/package.json b/packages/infra/package.json new file mode 100644 index 00000000..cb821823 --- /dev/null +++ b/packages/infra/package.json @@ -0,0 +1,49 @@ +{ + "name": "clipcc-infra", + "version": "3.1.2", + "description": "Infrastrucutre for ClipCC", + "main": "src/index.js", + "types": "./dist/types/index.d.ts", + "type": "commonjs", + "scripts": { + "prepublish": "yarn build", + "build": "tsc --project ./tsconfig.dts.json", + "test": "jest" + }, + "repository": { + "type": "git", + "url": "https://github.com/Clipteam/clipcc/packages/infra" + }, + "keywords": [ + "ClipCC", + "webpack" + ], + "author": "Clipteam", + "license": "BSD-3-Clause", + "bugs": { + "url": "https://github.com/Clipteam/clipcc/issues" + }, + "homepage": "https://github.com/Clipteam/clipcc#readme", + "dependencies": { + "webpack-node-externals": "^3.0.0" + }, + "devDependencies": { + "@types/jest": "29.5.14", + "@types/node": "^25.4.0", + "jest": "29.7.0", + "typescript": "^5.9.3", + "webpack": "^5.105.4" + }, + "peerDependencies": { + "@babel/preset-env": "^7.29.0", + "arraybuffer-loader": "^1.0.8", + "autoprefixer": "^9.7.4", + "babel-loader": "^10.1.0", + "css-loader": "6.7.3", + "postcss-import": "^12.0.0", + "postcss-loader": "7.0.2", + "style-loader": "4.0.0", + "terser-webpack-plugin": "^5.3.17", + "ts-loader": "^9.5.4" + } +} diff --git a/packages/infra/src/index.js b/packages/infra/src/index.js new file mode 100644 index 00000000..97f9a9fa --- /dev/null +++ b/packages/infra/src/index.js @@ -0,0 +1,622 @@ +// @ts-check + +const path = require('path'); +const fs = require('fs'); +const webpack = require('webpack'); + +const TerserPlugin = require('terser-webpack-plugin'); + +/** @typedef {import('webpack').Configuration} Configuration */ +/** @typedef {import('webpack').RuleSetRule} RuleSetRule */ +/** @typedef {NonNullable} SnapshotConfig */ +/** @typedef {NonNullable} EntryConfig */ +/** + * @typedef {Configuration & { + * devServer?: { + * static: string, + * host: string, + * port: string | number + * } + * }} ConfigWithDevServer + */ +/** + * @typedef {RuleSetRule & { + * include?: RuleSetRule['include'], + * exclude?: RuleSetRule['exclude'], + * rules?: ManifestRule[], + * oneOf?: ManifestRule[] + * }} ManifestRule + */ +/** + * Manifest consumed by {@link WebpackConfigBuilder} to generate the final webpack configuration. + * + * @typedef {object} WebpackManifest + * @property {EntryConfig} entry Webpack entry definition. Relative string and array entries are resolved from `rootPath`. The first resolved entry is also used as the exact-match alias target when this package is consumed through `workspacePackages`. + * @property {string} libraryName Library name for non-node targets. It becomes `output.library.name` when the generated artifact is emitted as UMD. + * @property {Configuration['target']=} target Webpack target. Node-like targets emit `commonjs2` output and enable `externalsPresets.node`; other targets emit UMD bundles. + * @property {Configuration['devtool']=} devTool Source map mode for the generated config. Defaults to `cheap-module-source-map`. + * @property {string} rootPath Base path used to resolve relative entries, source paths, rule conditions, aliases, and snapshot paths. + * @property {string=} srcPath Main source directory for the package. Defaults to `src` and is included in the default transpilation rules. + * @property {string=} distPath Output directory for the generated bundle. Defaults to `dist` and becomes `output.path`. + * @property {string=} publicPath Runtime base URL for emitted assets and chunks. Defaults to `/` and becomes `output.publicPath`. + * @property {string[]=} sourcePaths Additional source directories that should be processed by the default JS and TS rules alongside `srcPath`. + * @property {boolean=} enableReact Enables React support. This adds `@babel/preset-react` and enables CSS module handling for React source paths. + * @property {boolean=} enableTs Enables TypeScript support. This adds `ts-loader` with `transpileOnly: true` for `.ts` and `.tsx` files under the configured source paths. + * @property {boolean=} shouldSplitChunks Enables async chunk splitting by setting `optimization.splitChunks.chunks` to `async`. + * @property {ManifestRule[]=} rules Additional webpack rules appended after the built-in rules. Nested `rules`, `oneOf`, `include`, and `exclude` paths are normalized from `rootPath`. + * @property {Configuration['plugins']=} plugins Additional webpack plugins appended after the built-in `webpack.ProvidePlugin` that injects `Buffer`. + * @property {Record=} alias Extra `resolve.alias` entries. Relative paths are resolved from `rootPath`; explicit aliases in the current manifest override inherited workspace-package aliases. + * @property {SnapshotConfig=} snapshot Webpack snapshot configuration merged into the final config. Path arrays are normalized from `rootPath` and merged across workspace packages. + * @property {boolean | number=} playground Enables `devServer` output for local playground builds. `true` uses `process.env.PORT` or `auto`; a number forces a specific port. + * @property {Configuration['externals']=} externals Webpack externals passed through to the final config. This changes which dependencies are bundled into the emitted artifact. + * @property {Configuration['optimization']=} optimization Extra optimization settings merged into the generated config before the default `.min.js` terser minimizer is appended. + * @property {string[]=} workspacePackages Package names to resolve through `packageName/webpack.manifest.js`. Their source paths, aliases, rules, and snapshot settings are merged so they can be consumed directly from source. + */ + +const DEFAULT_CHUNK_FILENAME = 'chunks/[name].js'; +const DEFAULT_TS_LOADER_OPTIONS = { + transpileOnly: true +}; + +/** + * @param {Buffer} content + * @returns {string} + */ +const createHexDataUrl = content => `data:text/plain;base64,${content.toString('base64')}`; + +/** + * @template T + * @param {T | T[]=} value + * @returns {T[]} + */ +const toArray = value => { + if (Array.isArray(value)) return value; + if (typeof value === 'undefined') return []; + return [value]; +}; + +/** + * @template T + * @param {Array} values + * @returns {T[]} + */ +const unique = values => { + const filteredValues = /** @type {T[]} */ (values.filter(value => typeof value !== 'undefined')); + return Array.from(new Set(filteredValues)); +}; + +/** + * @param {string} rootPath + * @param {ManifestRule[]=} rules + * @returns {ManifestRule[]} + */ +const normalizeChildRules = (rootPath, rules) => (rules ?? []) + .filter(childRule => Boolean(childRule) && typeof childRule === 'object') + .map(childRule => normalizeRule(rootPath, /** @type {ManifestRule} */ (childRule))); + +/** + * @param {string} value + * @returns {boolean} + */ +const isRelativePath = value => value.startsWith('.') || value.startsWith('..'); + +/** + * @param {string} rootPath + * @param {string} value + * @returns {string} + */ +const maybeResolvePath = (rootPath, value) => { + if (path.isAbsolute(value) || isRelativePath(value)) { + return path.resolve(rootPath, value); + } + + const resolvedPath = path.resolve(rootPath, value); + return fs.existsSync(resolvedPath) ? resolvedPath : value; +}; + +/** + * @param {string} rootPath + * @param {EntryConfig} entry + * @returns {EntryConfig} + */ +const normalizeEntry = (rootPath, entry) => { + if (typeof entry === 'string') { + return maybeResolvePath(rootPath, entry); + } + + if (Array.isArray(entry)) { + return entry.map(value => maybeResolvePath(rootPath, value)); + } + + return Object.fromEntries( + Object.entries(entry).map(([key, value]) => { + if (typeof value === 'string' || Array.isArray(value)) { + return [key, normalizeEntry(rootPath, value)]; + } + + if (value && typeof value === 'object' && 'import' in value) { + return [key, { + ...value, + ...(value.import ? {import: normalizeEntry(rootPath, value.import)} : {}) + }]; + } + + return [key, value]; + }) + ); +}; + +/** + * @param {EntryConfig} entry + * @returns {string | undefined} + */ +const findFirstEntryPath = entry => { + if (typeof entry === 'string') { + return entry; + } + + if (Array.isArray(entry)) { + return entry[0]; + } + + for (const value of Object.values(entry)) { + if (typeof value === 'string') { + return value; + } + + if (Array.isArray(value)) { + return value[0]; + } + + if (value && typeof value === 'object' && 'import' in value) { + const entryPath = value.import; + + if (typeof entryPath === 'string') { + return entryPath; + } + + if (Array.isArray(entryPath)) { + return entryPath[0]; + } + } + } + + return undefined; +}; + +/** + * @param {string} rootPath + * @param {RuleSetRule['include'] | RuleSetRule['exclude']=} condition + * @returns {RuleSetRule['include'] | RuleSetRule['exclude'] | undefined} + */ +const normalizeRuleCondition = (rootPath, condition) => { + if (Array.isArray(condition)) { + return condition.map(entry => typeof entry === 'string' ? path.resolve(rootPath, entry) : entry); + } + + if (typeof condition === 'string') { + return path.resolve(rootPath, condition); + } + + return condition; +}; + +/** + * @param {string} rootPath + * @param {ManifestRule} rule + * @returns {ManifestRule} + */ +const normalizeRule = (rootPath, rule) => ({ + ...rule, + include: normalizeRuleCondition(rootPath, rule.include), + exclude: normalizeRuleCondition(rootPath, rule.exclude), + ...(Array.isArray(rule.rules) ? { + rules: normalizeChildRules(rootPath, rule.rules) + } : {}), + ...(Array.isArray(rule.oneOf) ? { + oneOf: normalizeChildRules(rootPath, rule.oneOf) + } : {}) +}); + +/** + * @param {string} rootPath + * @param {Configuration['snapshot']=} snapshot + * @returns {Configuration['snapshot'] | undefined} + */ +const normalizeSnapshot = (rootPath, snapshot) => { + if (!snapshot) return undefined; + + /** + * @param {Array=} entries + * @returns {Array | undefined} + */ + const normalizePathEntries = entries => { + if (!entries) return undefined; + return unique(entries.map(entry => typeof entry === 'string' ? path.resolve(rootPath, entry) : entry)); + }; + + return { + ...snapshot, + immutablePaths: normalizePathEntries(snapshot.immutablePaths), + managedPaths: normalizePathEntries(snapshot.managedPaths), + unmanagedPaths: normalizePathEntries(snapshot.unmanagedPaths) + }; +}; + +/** + * @param {SnapshotConfig=} currentSnapshot + * @param {SnapshotConfig=} nextSnapshot + * @returns {SnapshotConfig} + */ +const mergeSnapshot = (currentSnapshot, nextSnapshot) => { + if (!currentSnapshot && !nextSnapshot) return {}; + if (!currentSnapshot) return nextSnapshot ?? {}; + if (!nextSnapshot) return currentSnapshot; + + /** + * @param {Array=} currentEntries + * @param {Array=} nextEntries + * @returns {Array | undefined} + */ + const mergeEntries = (currentEntries, nextEntries) => { + const merged = unique([...(currentEntries ?? []), ...(nextEntries ?? [])]); + return merged.length > 0 ? merged : undefined; + }; + + return { + ...currentSnapshot, + ...nextSnapshot, + immutablePaths: mergeEntries(currentSnapshot.immutablePaths, nextSnapshot.immutablePaths), + managedPaths: mergeEntries(currentSnapshot.managedPaths, nextSnapshot.managedPaths), + unmanagedPaths: mergeEntries(currentSnapshot.unmanagedPaths, nextSnapshot.unmanagedPaths) + }; +}; + +/** + * @param {string} rootPath + * @param {Record} alias + * @returns {Record} + */ +const normalizeAlias = (rootPath, alias) => Object.fromEntries( + Object.entries(alias).map(([key, value]) => [key, maybeResolvePath(rootPath, value)]) +); + +/** + * @param {WebpackManifest} manifest + * @returns {Required} + */ +const normalizeManifest = manifest => { + const rootPath = path.resolve(manifest.rootPath); + const srcPath = path.resolve(rootPath, manifest.srcPath ?? 'src'); + const distPath = path.resolve(rootPath, manifest.distPath ?? 'dist'); + + return { + entry: normalizeEntry(rootPath, manifest.entry), + libraryName: manifest.libraryName, + devTool: manifest.devTool ?? 'cheap-module-source-map', + rootPath, + srcPath, + distPath, + publicPath: manifest.publicPath ?? '/', + sourcePaths: unique(toArray(manifest.sourcePaths).map(sourcePath => path.resolve(rootPath, sourcePath))), + enableReact: manifest.enableReact ?? false, + enableTs: manifest.enableTs ?? false, + shouldSplitChunks: manifest.shouldSplitChunks ?? false, + rules: (manifest.rules ?? []).map(rule => normalizeRule(rootPath, rule)), + alias: normalizeAlias(rootPath, manifest.alias ?? {}), + plugins: manifest.plugins ?? [], + snapshot: normalizeSnapshot(rootPath, manifest.snapshot) ?? {}, + target: manifest.target ?? 'web', + playground: manifest.playground ?? false, + workspacePackages: manifest.workspacePackages ?? [], + externals: manifest.externals ?? {}, + optimization: manifest.optimization ?? {} + }; +}; + +/** + * @param {Required} manifest + * @returns {string} + */ +const getEntryPath = manifest => findFirstEntryPath(manifest.entry) ?? manifest.srcPath; + +/** + * @param {string} packageName + * @param {Required} manifest + * @returns {Record} + */ +const createWorkspaceAliases = (packageName, manifest) => ({ + [packageName]: manifest.srcPath, + [`${packageName}$`]: getEntryPath(manifest) +}); + +/** + * @param {Required} manifest + * @returns {string[]} + */ +const getManifestSourcePaths = manifest => unique([ + manifest.srcPath, + ...manifest.sourcePaths +]); + +/** + * @param {Required} manifest + * @param {string[]} sourcePaths + * @param {string[]} cssSourcePaths + * @returns {RuleSetRule[]} + */ +const createDefaultRules = (manifest, sourcePaths, cssSourcePaths) => { + /** @type {RuleSetRule[]} */ + const rules = []; + + if (manifest.enableTs) { + rules.push({ + include: sourcePaths, + test: /\.([cm]?ts|tsx)$/, + loader: 'ts-loader', + options: DEFAULT_TS_LOADER_OPTIONS + }); + } + + rules.push({ + include: sourcePaths, + test: manifest.enableReact ? /\.[cm]?jsx?$/ : /\.[cm]?js$/, + loader: 'babel-loader', + options: { + babelrc: false, + presets: [ + '@babel/preset-env', + ...(manifest.enableReact ? ['@babel/preset-react'] : []) + ] + } + }); + + if (manifest.enableReact) { + rules.push({ + include: cssSourcePaths, + test: /\.css$/, + use: [{ + loader: 'style-loader' + }, { + loader: 'css-loader', + options: { + modules: { + localIdentName: '[name]_[local]_[hash:base64:5]', + exportLocalsConvention: 'camelCase' + }, + importLoaders: 1 + } + }, { + loader: 'postcss-loader', + options: { + postcssOptions: { + plugins: [ + 'postcss-import', + 'autoprefixer' + ] + } + } + }] + }); + } + + rules.push( + { + test: /\.hex$/, + type: 'asset/inline', + generator: { + dataUrl: createHexDataUrl + } + }, + { + resourceQuery: '?arrayBuffer', + type: 'javascript/auto', + use: 'arraybuffer-loader' + }, + { + resourceQuery: /raw/, + type: 'asset/source' + } + ); + + return rules; +}; + +/** + * @param {string} packageName + * @returns {WebpackManifest | null} + */ +const resolveWorkspacePackageManifest = packageName => { + try { + const packageJsonPath = require.resolve(`${packageName}/package.json`); + const packageDir = path.dirname(packageJsonPath); + const webpackManifestPath = path.join(packageDir, 'webpack.manifest.js'); + + if (!fs.existsSync(webpackManifestPath)) return null; + + const manifest = require(webpackManifestPath); + return manifest.default ?? manifest; + } catch { + return null; + } +}; + +class WebpackConfigBuilder { + /** + * @param {WebpackManifest} manifest + */ + constructor(manifest) { + /** @readonly @private @type {Set} */ + this.loadedWorkspacePackages = new Set(); + /** @type {Required} */ + this.manifest = normalizeManifest(manifest); + /** @readonly @private @type {string[]} */ + this.localSourcePaths = getManifestSourcePaths(this.manifest); + /** @private @type {string[]} */ + this.reactSourcePaths = this.manifest.enableReact ? [...this.localSourcePaths] : []; + + for (const packageName of this.manifest.workspacePackages) { + this.addWorkspacePackage(packageName); + } + } + + /** + * @param {string} packageName + * @returns {this} + */ + addWorkspacePackage(packageName) { + if (this.loadedWorkspacePackages.has(packageName)) { + return this; + } + + this.loadedWorkspacePackages.add(packageName); + + const workspaceManifest = resolveWorkspacePackageManifest(packageName); + if (!workspaceManifest) { + console.warn(`Package ${packageName} does not have a webpack manifest, skipping.`); + return this; + } + + const normalizedManifest = normalizeManifest(workspaceManifest); + + for (const dependencyName of normalizedManifest.workspacePackages) { + this.addWorkspacePackage(dependencyName); + } + + this.manifest.enableReact ||= normalizedManifest.enableReact; + this.manifest.enableTs ||= normalizedManifest.enableTs; + this.manifest.shouldSplitChunks ||= normalizedManifest.shouldSplitChunks; + this.manifest.sourcePaths = unique([ + ...this.manifest.sourcePaths, + normalizedManifest.srcPath, + ...normalizedManifest.sourcePaths + ]); + + if (normalizedManifest.enableReact) { + this.reactSourcePaths = unique([ + ...this.reactSourcePaths, + ...getManifestSourcePaths(normalizedManifest) + ]); + } + + this.manifest.alias = { + ...createWorkspaceAliases(packageName, normalizedManifest), + ...normalizedManifest.alias, + ...this.manifest.alias + }; + + if (normalizedManifest.rules.length > 0) { + this.manifest.rules = [{ + include: normalizedManifest.srcPath, + rules: normalizedManifest.rules + }, ...this.manifest.rules]; + } + + this.manifest.snapshot = mergeSnapshot(this.manifest.snapshot, normalizedManifest.snapshot); + this.manifest.workspacePackages = unique([...this.manifest.workspacePackages, packageName]); + + return this; + } + + /** + * @returns {Configuration} + */ + get() { + const sourcePaths = unique([ + this.manifest.srcPath, + ...this.manifest.sourcePaths + ]); + + const targetingNode = this.manifest.target?.toString().startsWith('node'); + + /** @type {Configuration['output']} */ + const output = { + path: this.manifest.distPath, + publicPath: this.manifest.publicPath, + filename: '[name].js', + chunkFilename: DEFAULT_CHUNK_FILENAME, + ...(targetingNode ? { + library: { + type: 'commonjs2' + } + } : { + library: { + name: this.manifest.libraryName, + type: 'umd' + } + }) + }; + + /** @type {ConfigWithDevServer} */ + const configuration = { + context: this.manifest.rootPath, + mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', + devtool: this.manifest.devTool, + target: this.manifest.target, + entry: this.manifest.entry, + output, + resolve: { + extensions: this.manifest.enableReact ? ['.ts', '.js', '.tsx', '.jsx'] : (this.manifest.enableTs ? ['.ts', '.js'] : ['.js']), + alias: this.manifest.alias + }, + module: { + rules: [ + ...createDefaultRules(this.manifest, sourcePaths, this.reactSourcePaths), + ...this.manifest.rules + ] + }, + plugins: [ + new webpack.ProvidePlugin({ + Buffer: ['buffer', 'Buffer'] + }), + ...this.manifest.plugins + ], + optimization: this.manifest.optimization, + externals: this.manifest.externals + }; + + if (Object.keys(this.manifest.snapshot).length > 0) { + configuration.snapshot = this.manifest.snapshot; + } + + if (this.manifest.shouldSplitChunks) { + configuration.optimization = { + ...configuration.optimization, + splitChunks: { + chunks: 'async' + } + }; + } + + if (this.manifest.playground) { + configuration.devServer = { + static: this.manifest.distPath, + host: '0.0.0.0', + port: typeof this.manifest.playground === 'number' ? this.manifest.playground : (process.env.PORT || 'auto') + }; + } + + configuration.optimization = { + minimize: process.env.NODE_ENV === 'production', + ...configuration.optimization, + minimizer: [ + ...configuration.optimization?.minimizer ?? [], + new TerserPlugin({ + include: /\.min\.js$/ + }) + ] + }; + + if (targetingNode) { + configuration.externalsPresets = {node: true}; + configuration.output.environment = { + nodePrefixForCoreModules: false // Backwards compatibility + }; + } + + return configuration; + } +} + +module.exports = WebpackConfigBuilder; +module.exports.default = WebpackConfigBuilder; diff --git a/packages/infra/test/fixtures/workspace-packages/alpha/package.json b/packages/infra/test/fixtures/workspace-packages/alpha/package.json new file mode 100644 index 00000000..73a541be --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/alpha/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-workspace-alpha", + "version": "1.0.0", + "main": "src/index.js" +} diff --git a/packages/infra/test/fixtures/workspace-packages/alpha/webpack.manifest.js b/packages/infra/test/fixtures/workspace-packages/alpha/webpack.manifest.js new file mode 100644 index 00000000..06f25ba4 --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/alpha/webpack.manifest.js @@ -0,0 +1,24 @@ +module.exports = { + rootPath: __dirname, + libraryName: 'AlphaFixture', + entry: ['./src/index.js', './src/polyfill.js'], + enableReact: true, + enableTs: true, + sourcePaths: ['./alpha-extra'], + alias: { + '@workspace-shared': './alpha-alias' + }, + rules: [{ + test: /\.alpha$/, + include: './alpha-include', + oneOf: [{ + test: /\.alpha-inner$/, + include: './alpha-inner' + }] + }], + snapshot: { + immutablePaths: ['./alpha-immutable'], + unmanagedPaths: ['./alpha-unmanaged'] + }, + workspacePackages: ['test-workspace-beta'] +}; diff --git a/packages/infra/test/fixtures/workspace-packages/beta/package.json b/packages/infra/test/fixtures/workspace-packages/beta/package.json new file mode 100644 index 00000000..4caf8520 --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/beta/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-workspace-beta", + "version": "1.0.0", + "main": "src/beta-entry.js" +} diff --git a/packages/infra/test/fixtures/workspace-packages/beta/webpack.manifest.js b/packages/infra/test/fixtures/workspace-packages/beta/webpack.manifest.js new file mode 100644 index 00000000..e52d6058 --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/beta/webpack.manifest.js @@ -0,0 +1,22 @@ +module.exports = { + rootPath: __dirname, + libraryName: 'BetaFixture', + entry: { + beta: { + import: ['./src/beta-entry.js', './src/beta-helper.js'] + } + }, + sourcePaths: ['./beta-extra'], + alias: { + '@beta-only': './beta-alias' + }, + rules: [{ + test: /\.beta$/, + exclude: './beta-exclude' + }], + snapshot: { + immutablePaths: ['./beta-immutable'], + managedPaths: ['./beta-managed'] + }, + workspacePackages: ['test-workspace-gamma'] +}; diff --git a/packages/infra/test/fixtures/workspace-packages/gamma/package.json b/packages/infra/test/fixtures/workspace-packages/gamma/package.json new file mode 100644 index 00000000..5b936de1 --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/gamma/package.json @@ -0,0 +1,5 @@ +{ + "name": "test-workspace-gamma", + "version": "1.0.0", + "main": "src/gamma-entry.js" +} diff --git a/packages/infra/test/fixtures/workspace-packages/gamma/webpack.manifest.js b/packages/infra/test/fixtures/workspace-packages/gamma/webpack.manifest.js new file mode 100644 index 00000000..d1307ef7 --- /dev/null +++ b/packages/infra/test/fixtures/workspace-packages/gamma/webpack.manifest.js @@ -0,0 +1,16 @@ +module.exports = { + rootPath: __dirname, + libraryName: 'GammaFixture', + entry: './src/gamma-entry.js', + enableReact: true, + alias: { + '@gamma-only': './gamma-alias' + }, + rules: [{ + test: /\.gamma$/, + include: './gamma-include' + }], + snapshot: { + unmanagedPaths: ['./gamma-unmanaged'] + } +}; diff --git a/packages/infra/test/target.test.js b/packages/infra/test/target.test.js new file mode 100644 index 00000000..c9e45331 --- /dev/null +++ b/packages/infra/test/target.test.js @@ -0,0 +1,191 @@ +const path = require('path'); +const webpack = require('webpack'); +const TerserPlugin = require('terser-webpack-plugin'); + +const WebpackConfigBuilder = require('../src'); + +const getRuleByLoader = (rules, loader) => rules.find(rule => rule.loader === loader); + +const getAssetRule = (rules, predicate) => rules.find(predicate); + +describe('WebpackConfigBuilder targets', () => { + const originalNodeEnv = process.env.NODE_ENV; + const originalPort = process.env.PORT; + + afterEach(() => { + process.env.NODE_ENV = originalNodeEnv; + process.env.PORT = originalPort; + }); + + test('builds the expected web configuration defaults and feature flags', () => { + const rootPath = path.join(__dirname, 'fixtures', 'target-consumer'); + + process.env.NODE_ENV = 'production'; + process.env.PORT = '4321'; + + const config = new WebpackConfigBuilder({ + rootPath, + libraryName: 'TargetFixture', + entry: { + main: './src/index.js', + worker: { + import: ['./src/worker.js', './src/runtime.js'] + } + }, + enableReact: true, + enableTs: true, + sourcePaths: ['./extras'], + alias: { + '@shared': './shared' + }, + rules: [{ + test: /\.custom$/, + include: './custom', + oneOf: [{ + test: /\.custom-child$/, + include: './nested' + }] + }], + shouldSplitChunks: true, + optimization: { + moduleIds: 'deterministic' + }, + playground: true, + publicPath: '/static/' + }).get(); + + expect(config.context).toBe(rootPath); + expect(config.mode).toBe('production'); + expect(config.devtool).toBe('cheap-module-source-map'); + expect(config.entry).toEqual({ + main: path.join(rootPath, 'src', 'index.js'), + worker: { + import: [ + path.join(rootPath, 'src', 'worker.js'), + path.join(rootPath, 'src', 'runtime.js') + ] + } + }); + expect(config.output).toMatchObject({ + path: path.join(rootPath, 'dist'), + publicPath: '/static/', + filename: '[name].js', + chunkFilename: 'chunks/[name].js', + library: { + name: 'TargetFixture', + type: 'umd' + } + }); + expect(config.resolve).toEqual({ + extensions: ['.ts', '.js', '.tsx', '.jsx'], + alias: { + '@shared': path.join(rootPath, 'shared') + } + }); + + const tsRule = getRuleByLoader(config.module.rules, 'ts-loader'); + expect(tsRule).toMatchObject({ + test: /\.([cm]?ts|tsx)$/, + loader: 'ts-loader', + options: { + transpileOnly: true + }, + include: [ + path.join(rootPath, 'src'), + path.join(rootPath, 'extras') + ] + }); + + const babelRule = getRuleByLoader(config.module.rules, 'babel-loader'); + expect(babelRule).toMatchObject({ + test: /\.[cm]?jsx?$/, + loader: 'babel-loader', + include: [ + path.join(rootPath, 'src'), + path.join(rootPath, 'extras') + ] + }); + expect(babelRule.options).toMatchObject({ + babelrc: false, + presets: ['@babel/preset-env', '@babel/preset-react'] + }); + + const cssRule = getAssetRule(config.module.rules, rule => Array.isArray(rule.use)); + expect(cssRule).toMatchObject({ + test: /\.css$/, + include: [ + path.join(rootPath, 'src'), + path.join(rootPath, 'extras') + ] + }); + expect(cssRule.use.map(loader => loader.loader)).toEqual([ + 'style-loader', + 'css-loader', + 'postcss-loader' + ]); + + const customRule = config.module.rules.find(rule => String(rule.test) === String(/\.custom$/)); + expect(customRule).toMatchObject({ + include: path.join(rootPath, 'custom') + }); + expect(customRule.oneOf[0]).toMatchObject({ + test: /\.custom-child$/, + include: path.join(rootPath, 'nested') + }); + + expect(getAssetRule(config.module.rules, rule => String(rule.test) === String(/\.hex$/))).toMatchObject({ + type: 'asset/inline' + }); + expect(getAssetRule(config.module.rules, rule => String(rule.resourceQuery) === String(/raw/))).toMatchObject({ + type: 'asset/source' + }); + expect(getAssetRule(config.module.rules, rule => rule.resourceQuery === '?arrayBuffer')).toMatchObject({ + type: 'javascript/auto', + use: 'arraybuffer-loader' + }); + + expect(config.plugins[0]).toBeInstanceOf(webpack.ProvidePlugin); + expect(config.devServer).toEqual({ + static: path.join(rootPath, 'dist'), + host: '0.0.0.0', + port: '4321' + }); + expect(config.optimization).toMatchObject({ + minimize: true, + moduleIds: 'deterministic', + splitChunks: { + chunks: 'async' + } + }); + expect(config.optimization.minimizer).toHaveLength(1); + expect(config.optimization.minimizer[0]).toBeInstanceOf(TerserPlugin); + }); + + test('builds node targets with commonjs output and node presets', () => { + const rootPath = path.join(__dirname, 'fixtures', 'node-consumer'); + + process.env.NODE_ENV = 'development'; + + const config = new WebpackConfigBuilder({ + rootPath, + libraryName: 'NodeFixture', + entry: './src/index.js', + target: 'node18', + enableTs: true + }).get(); + + expect(config.mode).toBe('development'); + expect(config.entry).toBe(path.join(rootPath, 'src', 'index.js')); + expect(config.output.library).toEqual({ + type: 'commonjs2' + }); + expect(config.externalsPresets).toEqual({ + node: true + }); + expect(config.output.environment).toEqual({ + nodePrefixForCoreModules: false + }); + expect(config.resolve.extensions).toEqual(['.ts', '.js']); + expect(config.devServer).toBeUndefined(); + }); +}); diff --git a/packages/infra/test/workspace.test.js b/packages/infra/test/workspace.test.js new file mode 100644 index 00000000..aa3c0ff6 --- /dev/null +++ b/packages/infra/test/workspace.test.js @@ -0,0 +1,128 @@ +const path = require('path'); +const Module = require('module'); + +const WebpackConfigBuilder = require('../src'); + +const originalResolveFilename = Module._resolveFilename; + +const fixtureDir = path.join(__dirname, 'fixtures', 'workspace-packages'); + +const fixturePackageJsonPaths = { + 'test-workspace-alpha/package.json': path.join(fixtureDir, 'alpha', 'package.json'), + 'test-workspace-beta/package.json': path.join(fixtureDir, 'beta', 'package.json'), + 'test-workspace-gamma/package.json': path.join(fixtureDir, 'gamma', 'package.json') +}; + +const getRuleByLoader = (rules, loader) => rules.find(rule => rule.loader === loader); + +describe('WebpackConfigBuilder workspace packages', () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + test('merges workspace package manifests recursively into the generated config', () => { + jest.spyOn(Module, '_resolveFilename').mockImplementation((request, parent, isMain, options) => { + if (request in fixturePackageJsonPaths) { + return fixturePackageJsonPaths[request]; + } + + return originalResolveFilename.call(Module, request, parent, isMain, options); + }); + + const rootPath = path.join(__dirname, 'fixtures', 'workspace-consumer'); + + const config = new WebpackConfigBuilder({ + rootPath, + libraryName: 'WorkspaceFixture', + entry: './src/index.js', + workspacePackages: ['test-workspace-alpha'], + alias: { + '@workspace-shared': './local-alias' + }, + snapshot: { + managedPaths: ['./local-managed'] + } + }).get(); + + const alphaRoot = path.join(fixtureDir, 'alpha'); + const betaRoot = path.join(fixtureDir, 'beta'); + const gammaRoot = path.join(fixtureDir, 'gamma'); + + expect(config.resolve.extensions).toEqual(['.ts', '.js', '.tsx', '.jsx']); + expect(config.resolve.alias).toEqual(expect.objectContaining({ + 'test-workspace-alpha': path.join(alphaRoot, 'src'), + 'test-workspace-alpha$': path.join(alphaRoot, 'src', 'index.js'), + 'test-workspace-beta': path.join(betaRoot, 'src'), + 'test-workspace-beta$': path.join(betaRoot, 'src', 'beta-entry.js'), + 'test-workspace-gamma': path.join(gammaRoot, 'src'), + 'test-workspace-gamma$': path.join(gammaRoot, 'src', 'gamma-entry.js'), + '@beta-only': path.join(betaRoot, 'beta-alias'), + '@gamma-only': path.join(gammaRoot, 'gamma-alias'), + '@workspace-shared': path.join(rootPath, 'local-alias') + })); + + const tsRule = getRuleByLoader(config.module.rules, 'ts-loader'); + expect(tsRule.include).toEqual(expect.arrayContaining([ + path.join(rootPath, 'src'), + path.join(alphaRoot, 'src'), + path.join(alphaRoot, 'alpha-extra'), + path.join(betaRoot, 'src'), + path.join(betaRoot, 'beta-extra'), + path.join(gammaRoot, 'src') + ])); + + const cssRule = config.module.rules.find(rule => Array.isArray(rule.use)); + expect(cssRule.include).toEqual(expect.arrayContaining([ + path.join(alphaRoot, 'src'), + path.join(alphaRoot, 'alpha-extra'), + path.join(gammaRoot, 'src') + ])); + expect(cssRule.include).not.toContain(path.join(rootPath, 'src')); + expect(cssRule.include).not.toContain(path.join(betaRoot, 'src')); + + const appendedRules = config.module.rules.filter(rule => Array.isArray(rule.rules)); + expect(appendedRules).toHaveLength(3); + expect(appendedRules[0]).toMatchObject({ + include: path.join(alphaRoot, 'src') + }); + expect(appendedRules[0].rules[0]).toMatchObject({ + test: /\.alpha$/, + include: path.join(alphaRoot, 'alpha-include') + }); + expect(appendedRules[0].rules[0].oneOf[0]).toMatchObject({ + test: /\.alpha-inner$/, + include: path.join(alphaRoot, 'alpha-inner') + }); + + expect(appendedRules[1]).toMatchObject({ + include: path.join(betaRoot, 'src') + }); + expect(appendedRules[1].rules[0]).toMatchObject({ + test: /\.beta$/, + exclude: path.join(betaRoot, 'beta-exclude') + }); + + expect(appendedRules[2]).toMatchObject({ + include: path.join(gammaRoot, 'src') + }); + expect(appendedRules[2].rules[0]).toMatchObject({ + test: /\.gamma$/, + include: path.join(gammaRoot, 'gamma-include') + }); + + expect(config.snapshot).toMatchObject({ + immutablePaths: expect.arrayContaining([ + path.join(alphaRoot, 'alpha-immutable'), + path.join(betaRoot, 'beta-immutable') + ]), + managedPaths: expect.arrayContaining([ + path.join(rootPath, 'local-managed'), + path.join(betaRoot, 'beta-managed') + ]), + unmanagedPaths: expect.arrayContaining([ + path.join(alphaRoot, 'alpha-unmanaged'), + path.join(gammaRoot, 'gamma-unmanaged') + ]) + }); + }); +}); diff --git a/packages/infra/tsconfig.dts.json b/packages/infra/tsconfig.dts.json new file mode 100644 index 00000000..440768f2 --- /dev/null +++ b/packages/infra/tsconfig.dts.json @@ -0,0 +1,27 @@ +// Reference: https://www.typescriptlang.org/docs/handbook/declaration-files/dts-from-js.html +{ + "include": [ + "./src/**/*" + ], + "exclude": [ + "node_modules" + ], + "compilerOptions": { + // Tells TypeScript to read JS files, as + // normally they are ignored as source files + "allowJs": true, + // Generate d.ts files + "declaration": true, + // This compiler run should + // only output d.ts files + "emitDeclarationOnly": true, + // Types should go into this directory. + // Removing this would place the .d.ts files + // next to the .js files + "outDir": "./dist/types/", + // go to js file when using IDE functions like + // "Go to Definition" in VSCode + "declarationMap": true, + "skipLibCheck": true + } +} diff --git a/packages/l10n/package.json b/packages/l10n/package.json index f724cc79..b78cf9b6 100644 --- a/packages/l10n/package.json +++ b/packages/l10n/package.json @@ -35,6 +35,7 @@ "async": "3.2.3", "babel-eslint": "^10.0.1", "babel-loader": "^10.1.0", + "clipcc-infra": "workspace:*", "eslint": "^9.39.2", "eslint-config-clipcc": "workspace:*", "eslint-plugin-import": "2.26.0", diff --git a/packages/l10n/webpack.config.js b/packages/l10n/webpack.config.js index 675e8380..ea29c186 100644 --- a/packages/l10n/webpack.config.js +++ b/packages/l10n/webpack.config.js @@ -1,28 +1,13 @@ -const path = require('path'); +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); -module.exports = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: 'cheap-module-source-map', - module: { - rules: [{ - test: /\.js$/, - include: path.resolve(__dirname, 'src'), - use: { - loader: 'babel-loader', - options: { - presets: ['@babel/preset-env'] - } - } - }] - }, +const config = new WebpackConfigBuilder({ + ...manifest, entry: { - l10n: './src/index.js', + l10n: manifest.entry, supportedLocales: './src/supported-locales.js', localeData: './src/locale-data.js' - }, - output: { - path: path.resolve(__dirname, 'dist'), - filename: '[name].js', - libraryTarget: 'commonjs2' } -}; +}).get(); + +module.exports = config; diff --git a/packages/l10n/webpack.manifest.js b/packages/l10n/webpack.manifest.js new file mode 100644 index 00000000..b51592a8 --- /dev/null +++ b/packages/l10n/webpack.manifest.js @@ -0,0 +1,13 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ + +/** @satisfies {WebpackManifest} */ +const manifest = { + entry: './src/index.js', + libraryName: 'l10n', + rootPath: __dirname +}; + +module.exports = manifest; diff --git a/packages/paint/package.json b/packages/paint/package.json index b485ad14..54972018 100644 --- a/packages/paint/package.json +++ b/packages/paint/package.json @@ -56,6 +56,7 @@ "babel-jest": "23.6.0", "babel-loader": "^10.1.0", "babel-plugin-react-intl": "3.0.1", + "clipcc-infra": "workspace:*", "css-loader": "^6.7.3", "enzyme": "3.11.0", "enzyme-adapter-react-16": "1.15.6", diff --git a/packages/paint/webpack.config.js b/packages/paint/webpack.config.js index 6c8b32eb..ee55a447 100644 --- a/packages/paint/webpack.config.js +++ b/packages/paint/webpack.config.js @@ -1,111 +1,46 @@ -const defaultsDeep = require('lodash.defaultsdeep'); const path = require('path'); +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); // Plugins const HtmlWebpackPlugin = require('html-webpack-plugin'); -const TerserPlugin = require('terser-webpack-plugin'); -const base = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: 'cheap-module-source-map', - module: { - rules: [{ - test: /\.jsx?$/, - loader: 'babel-loader', - include: path.resolve(__dirname, 'src'), - options: { - presets: ['@babel/preset-env', '@babel/preset-react'] - } - }, - { - test: /\.css$/, - use: [{ - loader: 'style-loader' - }, { - loader: 'css-loader', - options: { - modules: { - localIdentName: '[name]_[local]_[fullhash:base64:5]', - exportLocalsConvention: 'camelCase' - }, - importLoaders: 1 - } - }, { - loader: 'postcss-loader', - options: { - postcssOptions: { - plugins: [ - 'postcss-import', - 'autoprefixer' - ] - } - } - }] - }, - { - test: /\.png$/i, - type: 'asset/inline' - }, - { - test: /\.svg$/, - loader: 'svg-url-loader' - }] + +const playground = new WebpackConfigBuilder({ + ...manifest, + entry: { + playground: './src/playground/playground.jsx' }, - optimization: { - minimizer: [ - new TerserPlugin({ - include: /\.min\.js$/ - }) - ] + playground: 8078, + distPath: path.resolve(__dirname, 'playground'), + plugins: [ + new HtmlWebpackPlugin({ + template: 'src/playground/index.ejs', + title: 'Scratch 3.0 Paint Editor Playground' + }) + ] +}).get(); + +const library = new WebpackConfigBuilder({ + ...manifest, + entry: { + 'scratch-paint': manifest.entry }, - plugins: [] -}; + distPath: path.resolve(__dirname, 'dist'), + externals: { + '@turbowarp/nanolog': '@turbowarp/nanolog', + 'prop-types': 'prop-types', + 'react': 'react', + 'react-dom': 'react-dom', + 'react-intl': 'react-intl', + 'react-intl-redux': 'react-intl-redux', + 'react-popover': 'react-popover', + 'react-redux': 'react-redux', + 'react-responsive': 'react-responsive', + 'react-style-proptype': 'react-style-proptype', + 'react-tooltip': 'react-tooltip', + 'redux': 'redux' + } +}).get(); -module.exports = [ - // For the playground - defaultsDeep({}, base, { - devServer: { - static: path.resolve(__dirname, 'playground'), - host: '0.0.0.0', - port: process.env.PORT || 8078 - }, - entry: { - playground: './src/playground/playground.jsx' - }, - output: { - path: path.resolve(__dirname, 'playground'), - filename: '[name].js' - }, - plugins: base.plugins.concat([ - new HtmlWebpackPlugin({ - template: 'src/playground/index.ejs', - title: 'Scratch 3.0 Paint Editor Playground' - }) - ]) - }), - // For use as a library - defaultsDeep({}, base, { - externals: { - '@turbowarp/nanolog': '@turbowarp/nanolog', - 'prop-types': 'prop-types', - 'react': 'react', - 'react-dom': 'react-dom', - 'react-intl': 'react-intl', - 'react-intl-redux': 'react-intl-redux', - 'react-popover': 'react-popover', - 'react-redux': 'react-redux', - 'react-responsive': 'react-responsive', - 'react-style-proptype': 'react-style-proptype', - 'react-tooltip': 'react-tooltip', - 'redux': 'redux' - }, - entry: { - 'scratch-paint': './src/index.js' - }, - output: { - path: path.resolve(__dirname, 'dist'), - filename: '[name].js', - libraryTarget: 'commonjs2' - } - }) -]; +module.exports = [playground, library]; diff --git a/packages/paint/webpack.manifest.js b/packages/paint/webpack.manifest.js new file mode 100644 index 00000000..f1eda22a --- /dev/null +++ b/packages/paint/webpack.manifest.js @@ -0,0 +1,23 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ + +/** @satisfies {WebpackManifest} */ +const manifest = { + entry: './src/index.js', + libraryName: 'ScratchPaint', + rootPath: __dirname, + enableReact: true, + enableTs: true, + rules: [{ + test: /\.png$/i, + type: 'asset/inline' + }, + { + test: /\.svg$/, + type: 'asset/inline' + }] +}; + +module.exports = manifest; diff --git a/packages/render/package.json b/packages/render/package.json index 8b380cd7..8ff9ef34 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -39,6 +39,7 @@ "json": "9.0.6", "node-polyfill-webpack-plugin": "^3.0.0", "playwright-chromium": "1.13.0", + "clipcc-infra": "workspace:*", "clipcc-render-fonts": "1.0.256", "tap": "21.0.1", "terser-webpack-plugin": "^5.3.17", diff --git a/packages/render/webpack.config.js b/packages/render/webpack.config.js index 762d2cb5..96d112a4 100644 --- a/packages/render/webpack.config.js +++ b/packages/render/webpack.config.js @@ -1,118 +1,62 @@ +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); + const CopyWebpackPlugin = require('copy-webpack-plugin'); -const path = require('path'); -const TerserPlugin = require('terser-webpack-plugin'); -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); -const base = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - resolve: { - extensions: ['.ts', '.js'] - }, - devtool: 'cheap-module-source-map', - module: { - rules: [ - { - include: [ - path.resolve('src') - ], - test: /\.([cm]?ts|tsx)$/, - loader: 'ts-loader', - options: { - transpileOnly: true, - allowTsInNodeModules: true - } - }, - { - include: [ - path.resolve('src') - ], - test: /\.js$/, - loader: 'babel-loader', - options: { - presets: [[ - '@babel/preset-env', - {targets: {browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']}} - ]] - } - }, - { - resourceQuery: /raw/, - type: 'asset/source' - } - ] - }, - optimization: { - minimizer: [ - new TerserPlugin({ - include: /\.min\.js$/ - }) - ] - }, - plugins: [ - new NodePolyfillPlugin() - ] +const createConfig = overrideManifest => { + const config = new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest + }).get(); + + return config; }; -module.exports = [ - // Playground - Object.assign({}, base, { - target: 'web', - devServer: { - static: false, - host: '0.0.0.0', - port: process.env.PORT || 8361 - }, - entry: { - playground: './src/playground/playground.js', - queryPlayground: './src/playground/queryPlayground.js' - }, - output: { - libraryTarget: 'umd', - path: path.resolve('playground'), - filename: '[name].js' - }, - plugins: base.plugins.concat([ - new CopyWebpackPlugin({ - patterns: [{ - context: 'src/playground', - from: '*.+(html|css)' - }] - }) - ]) - }), - // Web-compatible - Object.assign({}, base, { - target: 'web', - entry: { - 'scratch-render': './src/index.js', - 'scratch-render.min': './src/index.js' - }, - output: { - library: 'ScratchRender', - libraryTarget: 'umd', - path: path.resolve('dist', 'web'), - filename: '[name].js' - } - }), - // Node-compatible - Object.assign({}, base, { - target: 'node', - entry: { - 'scratch-render': './src/index.js' - }, - output: { - library: 'ScratchRender', - libraryTarget: 'commonjs2', - path: path.resolve('dist', 'node'), - filename: '[name].js' - }, - externals: { - '!ify-loader!grapheme-breaker': 'grapheme-breaker', - '!ify-loader!linebreak': 'linebreak', - 'hull.js': true, - 'clipcc-svg-renderer': true, - 'twgl.js': true, - 'xml-escape': true - } +// Playground +const playground = createConfig({ + target: 'web', + distPath: './playground', + entry: { + playground: './src/playground/playground.js', + queryPlayground: './src/playground/queryPlayground.js' + }, + playground: 8361 +}); + +playground.plugins.push( + new CopyWebpackPlugin({ + patterns: [{ + context: 'src/playground', + from: '*.+(html|css)' + }] }) -]; +); + +// Web-compatible +const web = createConfig({ + target: 'web', + distPath: './dist/web', + entry: { + 'scratch-render': './src/index.js', + 'scratch-render.min': './src/index.js' + } +}); + +// Node-compatible +const node = createConfig({ + target: 'node', + distPath: './dist/node', + entry: { + 'scratch-render': './src/index.js' + }, + externals: { + '!ify-loader!grapheme-breaker': 'grapheme-breaker', + '!ify-loader!linebreak': 'linebreak', + 'hull.js': true, + 'twgl.js': true, + 'xml-escape': true, + 'clipcc-svg-renderer': true + } +}); + +module.exports = [playground, web, node]; diff --git a/packages/render/webpack.manifest.js b/packages/render/webpack.manifest.js new file mode 100644 index 00000000..6c0273f4 --- /dev/null +++ b/packages/render/webpack.manifest.js @@ -0,0 +1,22 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ + +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); + +/** @satisfies {WebpackManifest} */ +const manifest = { + libraryName: 'ClipCCRender', + entry: './src/index.js', + rootPath: __dirname, + enableTs: true, + + plugins: [ + new NodePolyfillPlugin({ + includeAliases: ['events'] + }) + ] +}; + +module.exports = manifest; diff --git a/packages/storage/package.json b/packages/storage/package.json index 2c133df9..29213946 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -41,6 +41,7 @@ "@types/jest": "29.5.14", "@typescript-eslint/eslint-plugin": "7.18.0", "babel-loader": "^10.1.0", + "clipcc-infra": "workspace:*", "buffer": "6.0.3", "eslint": "^9.39.2", "eslint-config-clipcc": "workspace:*", diff --git a/packages/storage/webpack.config.js b/packages/storage/webpack.config.js index 0afb6321..1ecdd781 100644 --- a/packages/storage/webpack.config.js +++ b/packages/storage/webpack.config.js @@ -1,116 +1,46 @@ -const path = require('path'); const webpack = require('webpack'); -const TerserPlugin = require('terser-webpack-plugin'); -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); -const baseConfig = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - target: 'browserslist', - devtool: 'cheap-module-source-map', - module: { - rules: [ - { - include: [ - path.resolve(__dirname, 'src') - ], - test: /\.js$/, - loader: 'babel-loader', - options: { - presets: [ - ['@babel/preset-env', {targets: {browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']}}] - ] - } - }, - { - include: [ - path.resolve(__dirname, 'src') - ], - test: /\.([cm]?ts|tsx)$/, - loader: 'ts-loader' - }, - { - resourceQuery: '?arrayBuffer', - type: 'javascript/auto', - use: 'arraybuffer-loader' - } - ] - }, - resolve: { - extensions: ['.ts', '.js', '.json'], - fallback: { - Buffer: require.resolve('buffer/') - } - }, - optimization: { - splitChunks: false, - minimizer: [ - new TerserPlugin({ - include: /\.min\.js$/, - terserOptions: { - sourceMap: true - } - }) - ] - }, - plugins: [ - new NodePolyfillPlugin() - ] -}; +const manifest = require('./webpack.manifest'); +const WebpackConfigBuilder = require('../infra'); -if (!process.env.CI) { - baseConfig.plugins.push(new webpack.ProgressPlugin()); -} +const createConfig = overrideManifest => { + const config = new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest + }).get(); -// Web-compatible -const webConfig = { - ...baseConfig, - output: { - library: { - name: 'ScratchStorage', - type: 'umd' - }, - path: path.resolve(__dirname, 'dist', 'web'), - clean: false - } + return config; }; -const webNonMinConfig = { - ...webConfig, +// Web-compatible +const webNonMin = createConfig({ + target: 'browserslist', + distPath: './dist/web', entry: { - 'scratch-storage': path.join(__dirname, './src/index.ts') + 'scratch-storage': './src/index.ts' }, optimization: { minimize: false } -}; +}); -const webMinConfig = { - ...webConfig, +const webMin = createConfig({ + target: 'web', + distPath: './dist/web', entry: { - 'scratch-storage.min': path.join(__dirname, './src/index.ts') + 'scratch-storage.min': './src/index.ts' }, optimization: { minimize: true } -}; +}); // Node-compatible -const nodeConfig = { - ...baseConfig, +const node = createConfig({ target: 'node', + distPath: './dist/node', entry: { - 'scratch-storage': path.join(__dirname, './src/index.ts') - }, - output: { - library: { - type: 'commonjs2' - }, - environment: { - nodePrefixForCoreModules: false - }, - chunkFormat: 'commonjs', - path: path.resolve(__dirname, 'dist', 'node'), - clean: false + 'scratch-storage': './src/index.ts' }, externals: { 'base64-js': true, @@ -118,11 +48,15 @@ const nodeConfig = { 'localforage': true, 'fastestsmallesttextencoderdecoder': true }, - plugins: baseConfig.plugins.concat([ + plugins: [ new webpack.ProvidePlugin({ fetch: ['node-fetch', 'default'] }) - ]) -}; + ] +}); -module.exports = [webNonMinConfig, webMinConfig, nodeConfig]; +module.exports = [ + webNonMin, + webMin, + node +]; diff --git a/packages/storage/webpack.manifest.js b/packages/storage/webpack.manifest.js new file mode 100644 index 00000000..544a5006 --- /dev/null +++ b/packages/storage/webpack.manifest.js @@ -0,0 +1,22 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ +const webpack = require('webpack'); + +/** @satisfies {WebpackManifest} */ +const manifest = { + entry: './src/index.js', + libraryName: 'ScratchStorage', + target: 'browserslist', + rootPath: __dirname, + enableTs: true, + /** @type {NonNullable} */ + plugins: [] +}; + +if (!process.env.CI) { + manifest.plugins.push(new webpack.ProgressPlugin()); +} + +module.exports = manifest; diff --git a/packages/vm/package.json b/packages/vm/package.json index 798f387f..d0b66daa 100644 --- a/packages/vm/package.json +++ b/packages/vm/package.json @@ -29,12 +29,14 @@ "version": "json -f package.json -I -e \"this.repository.sha = '$(git log -n1 --pretty=format:%H)'\"" }, "dependencies": { + "@turbowarp/nanolog": "^1.0.1", "@vernier/godirect": "1.8.3", "arraybuffer-loader": "1.0.8", "atob": "2.1.2", "btoa": "1.2.1", "canvas-toBlob": "1.0.0", "clipcc-parser": "workspace:~", + "clipcc-sb1-converter": "1.0.327", "decode-html": "2.0.0", "diff-match-patch": "1.0.4", "fastestsmallesttextencoderdecoder": "^1.0.22", @@ -42,13 +44,11 @@ "htmlparser2": "10.1.0", "immutable": "5.1.5", "jszip": "^3.10.1", - "@turbowarp/nanolog": "^1.0.1", "node-polyfill-webpack-plugin": "^3.0.0", - "clipcc-sb1-converter": "1.0.326", "scratch-translate-extension-languages": "1.0.7" }, "peerDependencies": { - "clipcc-svg-renderer": "2.5.48" + "clipcc-svg-renderer": "2.5.49" }, "devDependencies": { "@babel/core": "7.29.0", @@ -61,6 +61,7 @@ "callsite": "1.0.0", "clipcc-audio": "workspace:~", "clipcc-block": "workspace:~", + "clipcc-infra": "workspace:*", "clipcc-l10n": "workspace:~", "clipcc-render": "workspace:~", "clipcc-storage": "workspace:~", @@ -76,7 +77,6 @@ "json": "^9.0.6", "lodash.defaultsdeep": "4.6.1", "pngjs": "7.0.0", - "clipcc-render-fonts": "1.0.256", "script-loader": "0.7.2", "stats.js": "0.17.0", "tap": "21.0.1", diff --git a/packages/vm/webpack.config.js b/packages/vm/webpack.config.js index 45440f5c..9ccf47a9 100644 --- a/packages/vm/webpack.config.js +++ b/packages/vm/webpack.config.js @@ -1,150 +1,82 @@ -const webpack = require('webpack'); const CopyWebpackPlugin = require('copy-webpack-plugin'); -const defaultsDeep = require('lodash.defaultsdeep'); -const path = require('path'); -const TerserPlugin = require('terser-webpack-plugin'); -const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); -const {version} = require('../../package.json'); +const WebpackConfigBuilder = require('../infra'); +const manifest = require('./webpack.manifest'); -const base = { - mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', - devtool: 'cheap-module-source-map', - output: { - library: 'VirtualMachine', - libraryTarget: 'umd', - filename: '[name].js' - }, - resolve: { - alias: { - 'text-encoding': 'fastestsmallesttextencoderdecoder', - 'clipcc-render': path.resolve(__dirname, '../render/src/index.js'), - 'clipcc-audio': path.resolve(__dirname, '../audio/src/index.js') - }, - extensions: ['.ts', '.js'] - }, - module: { - rules: [{ - include: [ - path.resolve('src'), - path.resolve('../render/src') - ], - test: /\.([cm]?ts|tsx)$/, - loader: 'ts-loader', - options: { - transpileOnly: true, - allowTsInNodeModules: true - } - }, - { - test: /\.js$/, - loader: 'babel-loader', - include: path.resolve(__dirname, 'src'), - options: { - presets: [['@babel/preset-env', {targets: {browsers: ['last 3 versions', 'Safari >= 8', 'iOS >= 8']}}]] - } - }, - { - test: /\.mp3$/, - type: 'asset/resource' - }, - { - resourceQuery: /raw/, - type: 'asset/source' - }, - { - resourceQuery: '?arrayBuffer', - type: 'javascript/auto', - use: 'arraybuffer-loader' - }] +const createConfig = overrideManifest => { + const config = new WebpackConfigBuilder({ + ...manifest, + ...overrideManifest + }).get(); + + return config; +}; + +// Web-compatible +const web = createConfig({ + target: 'web', + distPath: './dist/web', + entry: { + 'scratch-vm': './src/index.js', + 'scratch-vm.min': './src/index.js' + } +}); + +// Node-compatible +const node = createConfig({ + target: 'node', + distPath: './dist/node', + entry: { + 'scratch-vm': './src/index.js' }, - optimization: { - minimizer: [ - new TerserPlugin({ - include: /\.min\.js$/ - }) - ] + externals: { + 'decode-html': true, + 'format-message': true, + 'htmlparser2': true, + 'immutable': true, + 'jszip': true, + '@turbowarp/nanolog': true, + 'clipcc-parser': true, + 'socket.io-client': true, + 'canvas': true + } +}); + +// Playground +const playground = createConfig({ + target: 'web', + distPath: './playground', + entry: { + 'benchmark': './src/playground/benchmark', + 'video-sensing-extension-debug': './src/extensions/scratch3_video_sensing/debug' }, - plugins: [ - new NodePolyfillPlugin(), - new webpack.DefinePlugin({ - 'clipcc.VERSION': version, - 'clipcc.BUILD_TIME': Date.now() - }) - ] + playground: true +}); +playground.devServer.static = false; +playground.devServer.port = process.env.PORT || 8073; +playground.module.rules.push({ + test: require.resolve('stats.js/build/stats.min.js'), + loader: 'script-loader' +}); +playground.performance = { + hints: false }; +playground.plugins = playground.plugins.concat([ + new CopyWebpackPlugin({ + patterns: [{ + from: '../block/media', + to: 'media' + }, { + from: '../storage/dist/web' + }, { + from: '../render/dist/web' + }, { + from: 'src/playground' + }] + }) +]); module.exports = [ - // Web-compatible - defaultsDeep({}, base, { - target: 'web', - entry: { - 'scratch-vm': './src/index.js', - 'scratch-vm.min': './src/index.js' - }, - output: { - path: path.resolve('dist', 'web') - } - }), - // Node-compatible - defaultsDeep({}, base, { - target: 'node', - entry: { - 'scratch-vm': './src/index.js' - }, - output: { - path: path.resolve('dist', 'node') - }, - externals: { - 'decode-html': true, - 'format-message': true, - 'htmlparser2': true, - 'immutable': true, - 'jszip': true, - '@turbowarp/nanolog': true, - 'clipcc-parser': true, - 'socket.io-client': true - } - }), - // Playground - defaultsDeep({}, base, { - target: 'web', - entry: { - 'benchmark': './src/playground/benchmark', - 'video-sensing-extension-debug': './src/extensions/scratch3_video_sensing/debug' - }, - devServer: { - static: false, - host: '0.0.0.0', - port: process.env.PORT || 8073 - }, - output: { - path: path.resolve(__dirname, 'playground'), - filename: '[name].js' - }, - module: { - rules: base.module.rules.concat([ - { - test: require.resolve('stats.js/build/stats.min.js'), - loader: 'script-loader' - } - ]) - }, - performance: { - hints: false - }, - plugins: base.plugins.concat([ - new CopyWebpackPlugin({ - patterns: [{ - from: '../block/media', - to: 'media' - }, { - from: '../storage/dist/web' - }, { - from: '../render/dist/web' - }, { - from: 'src/playground' - }] - }) - ]) - }) + web, + node, + playground ]; diff --git a/packages/vm/webpack.manifest.js b/packages/vm/webpack.manifest.js new file mode 100644 index 00000000..dbb7a11d --- /dev/null +++ b/packages/vm/webpack.manifest.js @@ -0,0 +1,31 @@ +// @ts-check +/** + * @import { WebpackManifest } from 'clipcc-infra'; + */ +const webpack = require('webpack'); +const NodePolyfillPlugin = require('node-polyfill-webpack-plugin'); +const {version} = require('../../package.json'); + +/** @satisfies {WebpackManifest} */ +const manifest = { + libraryName: 'VirtualMachine', + rootPath: __dirname, + entry: './src/index.js', + enableTs: true, + alias: { + 'text-encoding': 'fastestsmallesttextencoderdecoder' + }, + workspacePackages: ['clipcc-render', 'clipcc-audio'], + rules: [], + plugins: [ + new NodePolyfillPlugin({ + includeAliases: ['events', 'buffer'] + }), + new webpack.DefinePlugin({ + 'clipcc.VERSION': version, + 'clipcc.BUILD_TIME': Date.now() + }) + ] +}; + +module.exports = manifest; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 661afc33..19541a29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,10 +21,10 @@ importers: version: 0.2.1 '@changesets/cli': specifier: ^2.30.0 - version: 2.30.0(@types/node@12.20.55) + version: 2.30.0(@types/node@25.4.0) '@commitlint/cli': specifier: ^20.4.3 - version: 20.4.3(@types/node@12.20.55)(typescript@5.9.3) + version: 20.4.3(@types/node@25.4.0)(typescript@5.9.3) '@crowdin/cli': specifier: ^4.12.0 version: 4.14.0(encoding@0.1.13) @@ -71,6 +71,9 @@ importers: babel-eslint: specifier: 10.0.3 version: 10.0.3(eslint@9.39.2(jiti@2.6.1)) + clipcc-infra: + specifier: workspace:* + version: link:../infra eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) @@ -79,7 +82,7 @@ importers: version: link:../lint-config tap: specifier: 21.0.1 - version: 21.0.1(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + version: 21.0.1(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) typescript: specifier: ^5.9.3 version: 5.9.3 @@ -108,6 +111,9 @@ importers: '@jest/globals': specifier: ^30.3.0 version: 30.3.0 + clipcc-infra: + specifier: workspace:* + version: link:../infra clipcc-l10n: specifier: workspace:~ version: link:../l10n @@ -128,7 +134,7 @@ importers: version: 16.5.0 jest: specifier: ^30.3.0 - version: 30.3.0(@types/node@12.20.55) + version: 30.3.0(@types/node@25.4.0) jest-canvas-mock: specifier: ^2.5.2 version: 2.5.2 @@ -138,15 +144,12 @@ importers: lodash.defaultsdeep: specifier: ^4.6.1 version: 4.6.1 - raw-loader: - specifier: ^4.0.2 - version: 4.0.2(webpack@5.105.4) source-map-loader: specifier: ^5.0.0 version: 5.0.0(webpack@5.105.4) ts-jest: specifier: ^29.4.5 - version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3) ts-loader: specifier: ^9.5.4 version: 9.5.4(typescript@5.9.3)(webpack@5.105.4) @@ -391,6 +394,9 @@ importers: chromedriver: specifier: 146.0.1 version: 146.0.1 + clipcc-infra: + specifier: workspace:* + version: link:../infra cross-fetch: specifier: ^3.1.8 version: 3.2.0(encoding@0.1.13) @@ -500,6 +506,58 @@ importers: specifier: 2.10.0 version: 2.10.0 + packages/infra: + dependencies: + '@babel/preset-env': + specifier: ^7.29.0 + version: 7.29.0(@babel/core@7.29.0) + arraybuffer-loader: + specifier: ^1.0.8 + version: 1.0.8 + autoprefixer: + specifier: ^9.7.4 + version: 9.7.4 + babel-loader: + specifier: ^10.1.0 + version: 10.1.0(@babel/core@7.29.0)(webpack@5.105.4) + css-loader: + specifier: 6.7.3 + version: 6.7.3(webpack@5.105.4) + postcss-import: + specifier: ^12.0.0 + version: 12.0.1 + postcss-loader: + specifier: 7.0.2 + version: 7.0.2(postcss@8.5.8)(webpack@5.105.4) + style-loader: + specifier: 4.0.0 + version: 4.0.0(webpack@5.105.4) + terser-webpack-plugin: + specifier: ^5.3.17 + version: 5.3.17(webpack@5.105.4) + ts-loader: + specifier: ^9.5.4 + version: 9.5.4(typescript@5.9.3)(webpack@5.105.4) + webpack-node-externals: + specifier: ^3.0.0 + version: 3.0.0 + devDependencies: + '@types/jest': + specifier: 29.5.14 + version: 29.5.14 + '@types/node': + specifier: ^25.4.0 + version: 25.4.0 + jest: + specifier: 29.7.0 + version: 29.7.0(@types/node@25.4.0) + typescript: + specifier: ^5.9.3 + version: 5.9.3 + webpack: + specifier: ^5.105.4 + version: 5.105.4(webpack-cli@6.0.1) + packages/l10n: dependencies: '@babel/cli': @@ -533,6 +591,9 @@ importers: babel-loader: specifier: ^10.1.0 version: 10.1.0(@babel/core@7.29.0)(webpack@5.105.4) + clipcc-infra: + specifier: workspace:* + version: link:../infra eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) @@ -670,6 +731,9 @@ importers: babel-plugin-react-intl: specifier: 3.0.1 version: 3.0.1 + clipcc-infra: + specifier: workspace:* + version: link:../infra clipcc-l10n: specifier: workspace:~ version: link:../l10n @@ -702,7 +766,7 @@ importers: version: 5.6.6(webpack@5.105.4) jest: specifier: ^30.3.0 - version: 30.3.0(@types/node@12.20.55) + version: 30.3.0(@types/node@25.4.0) jest-canvas-mock: specifier: 2.3.1 version: 2.3.1 @@ -823,7 +887,7 @@ importers: version: 13.0.6 tap: specifier: ^21.0.1 - version: 21.0.1(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + version: 21.0.1(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) packages/render: dependencies: @@ -870,6 +934,9 @@ importers: babel-loader: specifier: ^10.1.0 version: 10.1.0(@babel/core@7.29.0)(webpack@5.105.4) + clipcc-infra: + specifier: workspace:* + version: link:../infra clipcc-render-fonts: specifier: 1.0.256 version: 1.0.256 @@ -899,7 +966,7 @@ importers: version: 1.13.0 tap: specifier: 21.0.1 - version: 21.0.1(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + version: 21.0.1(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) terser-webpack-plugin: specifier: ^5.3.17 version: 5.3.17(webpack@5.105.4) @@ -964,6 +1031,9 @@ importers: buffer: specifier: 6.0.3 version: 6.0.3 + clipcc-infra: + specifier: workspace:* + version: link:../infra eslint: specifier: ^9.39.2 version: 9.39.2(jiti@2.6.1) @@ -972,7 +1042,7 @@ importers: version: link:../lint-config eslint-plugin-jest: specifier: 27.9.0 - version: 27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.57.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3) + version: 27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.57.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3) eslint-plugin-react: specifier: 7.37.5 version: 7.37.5(eslint@9.39.2(jiti@2.6.1)) @@ -981,7 +1051,7 @@ importers: version: 6.2.0(webpack@5.105.4) jest: specifier: ^30.3.0 - version: 30.3.0(@types/node@12.20.55) + version: 30.3.0(@types/node@25.4.0) json: specifier: ^9.0.6 version: 9.0.6 @@ -993,10 +1063,10 @@ importers: version: 6.0.1 ts-jest: specifier: ^29.4.5 - version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3) + version: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3) ts-jest-mock-import-meta: specifier: 1.2.1 - version: 1.2.1(ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3)) + version: 1.2.1(ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3)) ts-loader: specifier: 9.5.4 version: 9.5.4(typescript@5.9.3)(webpack@5.105.4) @@ -1034,8 +1104,8 @@ importers: specifier: workspace:~ version: link:../parser clipcc-sb1-converter: - specifier: 1.0.326 - version: 1.0.326 + specifier: 1.0.327 + version: 1.0.327 decode-html: specifier: 2.0.0 version: 2.0.0 @@ -1094,15 +1164,15 @@ importers: clipcc-block: specifier: workspace:~ version: link:../block + clipcc-infra: + specifier: workspace:* + version: link:../infra clipcc-l10n: specifier: workspace:~ version: link:../l10n clipcc-render: specifier: workspace:~ version: link:../render - clipcc-render-fonts: - specifier: 1.0.256 - version: 1.0.256 clipcc-storage: specifier: workspace:~ version: link:../storage @@ -1150,7 +1220,7 @@ importers: version: 0.17.0 tap: specifier: 21.0.1 - version: 21.0.1(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) + version: 21.0.1(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3) terser-webpack-plugin: specifier: ^5.3.17 version: 5.3.17(webpack@5.105.4) @@ -2220,10 +2290,23 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} + '@jest/console@29.7.0': + resolution: {integrity: sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/console@30.3.0': resolution: {integrity: sha512-PAwCvFJ4696XP2qZj+LAn1BWjZaJ6RjG6c7/lkMaUJnkyMS34ucuIsfqYvfskVNvUI27R/u4P1HMYFnlVXG/Ww==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/core@29.7.0': + resolution: {integrity: sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/core@30.3.0': resolution: {integrity: sha512-U5mVPsBxLSO6xYbf+tgkymLx+iAhvZX43/xI1+ej2ZOPnPdkdO1CzDmFKh2mZBn2s4XZixszHeQnzp1gm/DIxw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2247,6 +2330,10 @@ packages: canvas: optional: true + '@jest/environment@29.7.0': + resolution: {integrity: sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/environment@30.3.0': resolution: {integrity: sha512-SlLSF4Be735yQXyh2+mctBOzNDx5s5uLv88/j8Qn1wH679PDcwy67+YdADn8NJnGjzlXtN62asGH/T4vWOkfaw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2259,10 +2346,18 @@ packages: resolution: {integrity: sha512-j0+W5iQQ8hBh7tHZkTQv3q2Fh/M7Je72cIsYqC4OaktgtO7v1So9UTjp6uPBHIaB6beoF/RRsCgMJKvti0wADA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/expect@29.7.0': + resolution: {integrity: sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/expect@30.3.0': resolution: {integrity: sha512-76Nlh4xJxk2D/9URCn3wFi98d2hb19uWE1idLsTt2ywhvdOldbw3S570hBgn25P4ICUZ/cBjybrBex2g17IDbg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/fake-timers@29.7.0': + resolution: {integrity: sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/fake-timers@30.3.0': resolution: {integrity: sha512-WUQDs8SOP9URStX1DzhD425CqbN/HxUYCTwVrT8sTVBfMvFqYt/s61EK5T05qnHu0po6RitXIvP9otZxYDzTGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2271,6 +2366,10 @@ packages: resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/globals@29.7.0': + resolution: {integrity: sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/globals@30.3.0': resolution: {integrity: sha512-+owLCBBdfpgL3HU+BD5etr1SvbXpSitJK0is1kiYjJxAAJggYMRQz5hSdd5pq1sSggfxPbw2ld71pt4x5wwViA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2279,6 +2378,15 @@ packages: resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/reporters@29.7.0': + resolution: {integrity: sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + '@jest/reporters@30.3.0': resolution: {integrity: sha512-a09z89S+PkQnL055bVj8+pe2Caed2PBOaczHcXCykW5ngxX9EWx/1uAwncxc/HiU0oZqfwseMjyhxgRjS49qPw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2300,18 +2408,34 @@ packages: resolution: {integrity: sha512-ORbRN9sf5PP82v3FXNSwmO1OTDR2vzR2YTaR+E3VkSBZ8zadQE6IqYdYEeFH1NIkeB2HIGdF02dapb6K0Mj05g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/source-map@29.6.3': + resolution: {integrity: sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/source-map@30.0.1': resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/test-result@29.7.0': + resolution: {integrity: sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-result@30.3.0': resolution: {integrity: sha512-e/52nJGuD74AKTSe0P4y5wFRlaXP0qmrS17rqOMHeSwm278VyNyXE3gFO/4DTGF9w+65ra3lo3VKj0LBrzmgdQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/test-sequencer@29.7.0': + resolution: {integrity: sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/test-sequencer@30.3.0': resolution: {integrity: sha512-dgbWy9b8QDlQeRZcv7LNF+/jFiiYHTKho1xirauZ7kVwY7avjFF6uTT0RqlgudB5OuIPagFdVtfFMosjVbk1eA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + '@jest/transform@29.7.0': + resolution: {integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + '@jest/transform@30.3.0': resolution: {integrity: sha512-TLKY33fSLVd/lKB2YI1pH69ijyUblO/BQvCj566YvnwuzoTNr648iE0j22vRvVNk2HsPwByPxATg3MleS3gf5A==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -2659,6 +2783,9 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + '@sinonjs/fake-timers@10.3.0': + resolution: {integrity: sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==} + '@sinonjs/fake-timers@15.1.1': resolution: {integrity: sha512-cO5W33JgAPbOh07tvZjUOJ7oWhtaqGHiZw+11DPbyqh2kHTBc3eF/CjJDeQ4205RLQsX6rxCuYOroFQwl7JDRw==} @@ -2881,6 +3008,9 @@ packages: '@types/fastestsmallesttextencoderdecoder@1.0.2': resolution: {integrity: sha512-0Md9qUxGnQ9uJ0NjsA70Y0NHIl3NwfBO1d8CGXK0zhczRL85CXJ3M0JZsmCNQ0WcAbt08FuT4U1G1mWQqgOsUg==} + '@types/graceful-fs@4.1.9': + resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} + '@types/html-minifier-terser@6.1.0': resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==} @@ -2929,6 +3059,9 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + '@types/node@25.4.0': + resolution: {integrity: sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw==} + '@types/parse-json@4.0.2': resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} @@ -3697,6 +3830,12 @@ packages: peerDependencies: babel-core: ^6.0.0 || ^7.0.0-0 + babel-jest@29.7.0: + resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.8.0 + babel-jest@30.3.0: resolution: {integrity: sha512-gRpauEU2KRrCox5Z296aeVHR4jQ98BCnu0IO332D/xpHNOsIH/bgSRk9k6GbKIbBw8vFeN6ctuu6tV8WOyVfYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3725,6 +3864,10 @@ packages: babel-plugin-istanbul@4.1.6: resolution: {integrity: sha512-PWP9FQ1AhZhS01T/4qLSKoHGY/xvkZdVBGlKM/HuxxS3+sC66HhTNR7+MpbO/so/cz/wY94MeSWJuP1hXIPfwQ==} + babel-plugin-istanbul@6.1.1: + resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==} + engines: {node: '>=8'} + babel-plugin-istanbul@7.0.1: resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} engines: {node: '>=12'} @@ -3732,6 +3875,10 @@ packages: babel-plugin-jest-hoist@23.2.0: resolution: {integrity: sha512-N0MlMjZtahXK0yb0K3V9hWPrq5e7tThbghvDr0k3X75UuOOqwsWW6mk8XHD2QvEC0Ca9dLIfTgNU36TeJD6Hnw==} + babel-plugin-jest-hoist@29.6.3: + resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + babel-plugin-jest-hoist@30.3.0: resolution: {integrity: sha512-+TRkByhsws6sfPjVaitzadk1I0F5sPvOVUH5tyTSzhePpsGIVrdeunHSw/C36QeocS95OOk8lunc4rlu5Anwsg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -3773,6 +3920,12 @@ packages: babel-preset-jest@23.2.0: resolution: {integrity: sha512-AdfWwc0PYvDtwr009yyVNh72Ev68os7SsPmOFVX7zSA+STXuk5CV2iMVazZU01bEoHCSwTkgv4E4HOOcODPkPg==} + babel-preset-jest@29.6.3: + resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@babel/core': ^7.0.0 + babel-preset-jest@30.3.0: resolution: {integrity: sha512-6ZcUbWHC+dMz2vfzdNwi87Z1gQsLNK2uLuK1Q89R11xdvejcivlYYwDlEv0FHX3VwEXpbBQ9uufB/MUNpZGfhQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -4135,6 +4288,9 @@ packages: resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==} engines: {node: '>= 0.10'} + cjs-module-lexer@1.4.3: + resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + cjs-module-lexer@2.2.0: resolution: {integrity: sha512-4bHTS2YuzUvtoLjdy+98ykbNB5jS0+07EvFNXerqZQJ89F7DI6ET7OQo/HJuW6K0aVsKA9hj9/RVb2kQVOrPDQ==} @@ -4176,8 +4332,8 @@ packages: clipcc-render-fonts@1.0.256: resolution: {integrity: sha512-BUkYYtOs7Qo5biLgsFugxr0rzhDquC4q2wuraGCp7vl/6m6Vx9fg9gY5raKJz/+XSeBgj3Q7czV5FsrF8Z3mGg==} - clipcc-sb1-converter@1.0.326: - resolution: {integrity: sha512-t7qPJhHXtGw8+Z82msjNqfM7tzO/u9sdqgI8EQ7nVpqS+/q66tbbni8ff4EM6VFHlH6Yc3FfpM5FyH1chdu1Iw==} + clipcc-sb1-converter@1.0.327: + resolution: {integrity: sha512-OnmnpRWKImSKTA28gfxkdFVkZnGECFxGOJIFS0u3GJngG+QWTGYajA8J9eeRie9uXghj7QjRRdW818pNczofmg==} clipcc-svg-renderer@2.5.49: resolution: {integrity: sha512-O9tBPOcuk3B6Srgrpr0G2UmHNhexxtLlprDC1DmT1542kqtZvLYJDNtf3HrTR2OwmaiWo/t3qCvYlJXrGiVvtg==} @@ -4420,6 +4576,11 @@ packages: create-hmac@1.1.7: resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + create-jest@29.7.0: + resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + cross-env@10.1.0: resolution: {integrity: sha512-GsYosgnACZTADcmEyJctkJIoqAhHjttw7RsFrVoJNXbsWWqaq6Ym+7kZjq6mS45O0jij6vtiReppKQEtqWy6Dw==} engines: {node: '>=20'} @@ -6596,6 +6757,10 @@ packages: istanbul-lib-instrument@1.10.2: resolution: {integrity: sha512-aWHxfxDqvh/ZlxR8BBaEPVSWDPUkGD63VjGQn3jcw8jCp7sHEMKcrj4xfJn/ABzdMEHiQNyvDQhqm5o8+SQg7A==} + istanbul-lib-instrument@5.2.1: + resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} + engines: {node: '>=8'} + istanbul-lib-instrument@6.0.3: resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} engines: {node: '>=10'} @@ -6604,6 +6769,10 @@ packages: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} + istanbul-lib-source-maps@4.0.1: + resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} + engines: {node: '>=10'} + istanbul-lib-source-maps@5.0.6: resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} engines: {node: '>=10'} @@ -6633,14 +6802,32 @@ packages: jest-canvas-mock@2.5.2: resolution: {integrity: sha512-vgnpPupjOL6+L5oJXzxTxFrlGEIbHdZqFU+LFNdtLxZ3lRDCl17FlTMM7IatoRQkrcyOTMlDinjUguqmQ6bR2A==} + jest-changed-files@29.7.0: + resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-changed-files@30.3.0: resolution: {integrity: sha512-B/7Cny6cV5At6M25EWDgf9S617lHivamL8vl6KEpJqkStauzcG4e+WPfDgMMF+H4FVH4A2PLRyvgDJan4441QA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-circus@29.7.0: + resolution: {integrity: sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-circus@30.3.0: resolution: {integrity: sha512-PyXq5szeSfR/4f1lYqCmmQjh0vqDkURUYi9N6whnHjlRz4IUQfMcXkGLeEoiJtxtyPqgUaUUfyQlApXWBSN1RA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-cli@29.7.0: + resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jest-cli@30.3.0: resolution: {integrity: sha512-l6Tqx+j1fDXJEW5bqYykDQQ7mQg+9mhWXtnj+tQZrTWYHyHoi6Be8HPumDSA+UiX2/2buEgjA58iJzdj146uCw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6651,6 +6838,18 @@ packages: node-notifier: optional: true + jest-config@29.7.0: + resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + peerDependencies: + '@types/node': '*' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + ts-node: + optional: true + jest-config@30.3.0: resolution: {integrity: sha512-WPMAkMAtNDY9P/oKObtsRG/6KTrhtgPJoBTmk20uDn4Uy6/3EJnnaZJre/FMT1KVRx8cve1r7/FlMIOfRVWL4w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6674,10 +6873,18 @@ packages: resolution: {integrity: sha512-n3q4PDQjS4LrKxfWB3Z5KNk1XjXtZTBwQp71OP0Jo03Z6V60x++K5L8k6ZrW8MY8pOFylZvHM0zsjS1RqlHJZQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-docblock@29.7.0: + resolution: {integrity: sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-docblock@30.2.0: resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-each@29.7.0: + resolution: {integrity: sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-each@30.3.0: resolution: {integrity: sha512-V8eMndg/aZ+3LnCJgSm13IxS5XSBM22QSZc9BtPK8Dek6pm+hfUNfwBdvsB3d342bo1q7wnSkC38zjX259qZNA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6691,6 +6898,10 @@ packages: canvas: optional: true + jest-environment-node@29.7.0: + resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-environment-node@30.3.0: resolution: {integrity: sha512-4i6HItw/JSiJVsC5q0hnKIe/hbYfZLVG9YJ/0pU9Hz2n/9qZe3Rhn5s5CUZA5ORZlcdT/vmAXRMyONXJwPrmYQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6703,6 +6914,10 @@ packages: resolution: {integrity: sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-haste-map@29.7.0: + resolution: {integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-haste-map@30.3.0: resolution: {integrity: sha512-mMi2oqG4KRU0R9QEtscl87JzMXfUhbKaFqOxmjb2CKcbHcUGFrJCBWHmnTiUqi6JcnzoBlO4rWfpdl2k/RfLCA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6715,6 +6930,10 @@ packages: resolution: {integrity: sha512-ljUdO0hLyu0A92xk7R2Wet3kj99fmazTo+ZFYQP6b7AGOBxJUj8ZkJWzJ632ajpXko2Y5oNoGR2kvOwiDdu6hg==} engines: {node: '>=6.0.0'} + jest-leak-detector@29.7.0: + resolution: {integrity: sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-leak-detector@30.3.0: resolution: {integrity: sha512-cuKmUUGIjfXZAiGJ7TbEMx0bcqNdPPI6P1V+7aF+m/FUJqFDxkFR4JqkTu8ZOiU5AaX/x0hZ20KaaIPXQzbMGQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6739,6 +6958,10 @@ packages: resolution: {integrity: sha512-Z/j4Bo+4ySJ+JPJN3b2Qbl9hDq3VrXmnjjGEWD/x0BCXeOXPTV1iZYYzl2X8c1MaCOL+ewMyNBcm88sboE6YWw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-mock@29.7.0: + resolution: {integrity: sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-mock@30.3.0: resolution: {integrity: sha512-OTzICK8CpE+t4ndhKrwlIdbM6Pn8j00lvmSmq5ejiO+KxukbLjgOflKWMn3KE34EZdQm5RqTuKj+5RIEniYhog==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6752,26 +6975,50 @@ packages: jest-resolve: optional: true + jest-regex-util@29.6.3: + resolution: {integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-regex-util@30.0.1: resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve-dependencies@29.7.0: + resolution: {integrity: sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-resolve-dependencies@30.3.0: resolution: {integrity: sha512-9ev8s3YN6Hsyz9LV75XUwkCVFlwPbaFn6Wp75qnI0wzAINYWY8Fb3+6y59Rwd3QaS3kKXffHXsZMziMavfz/nw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-resolve@29.7.0: + resolution: {integrity: sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-resolve@30.3.0: resolution: {integrity: sha512-NRtTAHQlpd15F9rUR36jqwelbrDV/dY4vzNte3S2kxCKUJRYNd5/6nTSbYiak1VX5g8IoFF23Uj5TURkUW8O5g==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-runner@29.7.0: + resolution: {integrity: sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runner@30.3.0: resolution: {integrity: sha512-gDv6C9LGKWDPLia9TSzZwf4h3kMQCqyTpq+95PODnTRDO0g9os48XIYYkS6D236vjpBir2fF63YmJFtqkS5Duw==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-runtime@29.7.0: + resolution: {integrity: sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-runtime@30.3.0: resolution: {integrity: sha512-CgC+hIBJbuh78HEffkhNKcbXAytQViplcl8xupqeIWyKQF50kCQA8J7GeJCkjisC6hpnC9Muf8jV5RdtdFbGng==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-snapshot@29.7.0: + resolution: {integrity: sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-snapshot@30.3.0: resolution: {integrity: sha512-f14c7atpb4O2DeNhwcvS810Y63wEn8O1HqK/luJ4F6M4NjvxmAKQwBUWjbExUtMxWJQ0wVgmCKymeJK6NZMnfQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6788,10 +7035,18 @@ packages: resolution: {integrity: sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==} engines: {node: '>= 6'} + jest-validate@29.7.0: + resolution: {integrity: sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-validate@30.3.0: resolution: {integrity: sha512-I/xzC8h5G+SHCb2P2gWkJYrNiTbeL47KvKeW5EzplkyxzBRBw1ssSHlI/jXec0ukH2q7x2zAWQm7015iusg62Q==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest-watcher@29.7.0: + resolution: {integrity: sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-watcher@30.3.0: resolution: {integrity: sha512-PJ1d9ThtTR8aMiBWUdcownq9mDdLXsQzJayTk4kmaBRHKvwNQn+ANveuhEBUyNI2hR1TVhvQ8D5kHubbzBHR/w==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6800,10 +7055,24 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + jest-worker@30.3.0: resolution: {integrity: sha512-DrCKkaQwHexjRUFTmPzs7sHQe0TSj9nvDALKGdwmK5mW9v7j90BudWirKAJHt3QQ9Dhrg1F7DogPzhChppkJpQ==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + jest@29.7.0: + resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + jest@30.3.0: resolution: {integrity: sha512-AkXIIFcaazymvey2i/+F94XRnM6TsVLZDhBMLsd1Sf/W0wzsvvpjeyUrCZD6HGG4SDYPgDJDBKeiJTBb10WzMg==} engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} @@ -6965,6 +7234,10 @@ packages: resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} engines: {node: '>=0.10.0'} + kleur@3.0.3: + resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} + engines: {node: '>=6'} + klona@2.0.6: resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} engines: {node: '>= 8'} @@ -8155,6 +8428,10 @@ packages: promise@7.3.1: resolution: {integrity: sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==} + prompts@2.4.2: + resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} + engines: {node: '>= 6'} + prop-types-exact@1.2.7: resolution: {integrity: sha512-A4RaV6mg3jocQqBYmqi2ojJ2VnV4AKTEHhl3xHsud08/u87gcVJc8DUOtgnPegoOCQv/shUqEk4eZGYibjnHzQ==} engines: {node: '>= 0.8'} @@ -8198,6 +8475,9 @@ packages: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} + pure-rand@6.1.0: + resolution: {integrity: sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==} + pure-rand@7.0.1: resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} @@ -8265,12 +8545,6 @@ packages: raw-loader@0.5.1: resolution: {integrity: sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q==} - raw-loader@4.0.2: - resolution: {integrity: sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA==} - engines: {node: '>= 10.13.0'} - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 - react-contextmenu@2.9.4: resolution: {integrity: sha512-ma4YQw4J7JNv8L8mMVd1NHHbvH3+gaK/rQ6FC5Tft7OCUweKi7PHqHEE0fDF2009FLSGoV5JnjPnTTEyThqaJg==} peerDependencies: @@ -8572,6 +8846,10 @@ packages: resolution: {integrity: sha512-gLWKdA5tiv5j/D7ipR47u3ovbVfzFPrctTdw2Ulnpmr6PPVVSvPKGNWu09jXVNlOSLLAeD6CA13bjIelpWttSw==} engines: {node: 20 || >=22} + resolve.exports@2.0.3: + resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} + engines: {node: '>=10'} + resolve@1.22.11: resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} engines: {node: '>= 0.4'} @@ -8857,6 +9135,9 @@ packages: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} + sisteransi@1.0.5: + resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} + slash@2.0.0: resolution: {integrity: sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==} engines: {node: '>=6'} @@ -9598,6 +9879,9 @@ packages: unbzip2-stream@1.4.3: resolution: {integrity: sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==} + undici-types@7.18.2: + resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@7.22.0: resolution: {integrity: sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==} engines: {node: '>=20.18.1'} @@ -9812,6 +10096,10 @@ packages: resolution: {integrity: sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==} engines: {node: '>=18.0.0'} + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + webpack-sources@3.3.4: resolution: {integrity: sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==} engines: {node: '>=10.13.0'} @@ -9920,6 +10208,10 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + write-file-atomic@4.0.2: + resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + write-file-atomic@5.0.1: resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} @@ -10960,7 +11252,7 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.30.0(@types/node@12.20.55)': + '@changesets/cli@2.30.0(@types/node@25.4.0)': dependencies: '@changesets/apply-release-plan': 7.1.0 '@changesets/assemble-release-plan': 6.0.9 @@ -10976,7 +11268,7 @@ snapshots: '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@12.20.55) + '@inquirer/external-editor': 1.0.3(@types/node@25.4.0) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 enquirer: 2.4.1 @@ -11074,11 +11366,11 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@commitlint/cli@20.4.3(@types/node@12.20.55)(typescript@5.9.3)': + '@commitlint/cli@20.4.3(@types/node@25.4.0)(typescript@5.9.3)': dependencies: '@commitlint/format': 20.4.3 '@commitlint/lint': 20.4.3 - '@commitlint/load': 20.4.3(@types/node@12.20.55)(typescript@5.9.3) + '@commitlint/load': 20.4.3(@types/node@25.4.0)(typescript@5.9.3) '@commitlint/read': 20.4.3 '@commitlint/types': 20.4.3 tinyexec: 1.0.2 @@ -11120,14 +11412,14 @@ snapshots: '@commitlint/rules': 20.4.3 '@commitlint/types': 20.4.3 - '@commitlint/load@20.4.3(@types/node@12.20.55)(typescript@5.9.3)': + '@commitlint/load@20.4.3(@types/node@25.4.0)(typescript@5.9.3)': dependencies: '@commitlint/config-validator': 20.4.3 '@commitlint/execute-rule': 20.0.0 '@commitlint/resolve-extends': 20.4.3 '@commitlint/types': 20.4.3 cosmiconfig: 9.0.1(typescript@5.9.3) - cosmiconfig-typescript-loader: 6.2.0(@types/node@12.20.55)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) + cosmiconfig-typescript-loader: 6.2.0(@types/node@25.4.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3) is-plain-obj: 4.1.0 lodash.mergewith: 4.6.2 picocolors: 1.1.1 @@ -11365,12 +11657,12 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/external-editor@1.0.3(@types/node@12.20.55)': + '@inquirer/external-editor@1.0.3(@types/node@25.4.0)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 12.20.55 + '@types/node': 25.4.0 '@isaacs/cliui@8.0.2': dependencies: @@ -11387,14 +11679,14 @@ snapshots: dependencies: minipass: 7.1.3 - '@isaacs/ts-node-temp-fork-for-pr-2009@10.9.7(@types/node@12.20.55)(typescript@5.5.4)': + '@isaacs/ts-node-temp-fork-for-pr-2009@10.9.7(@types/node@25.4.0)(typescript@5.5.4)': dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node14': 14.1.8 '@tsconfig/node16': 16.1.8 '@tsconfig/node18': 18.2.6 '@tsconfig/node20': 20.1.9 - '@types/node': 12.20.55 + '@types/node': 25.4.0 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -11403,14 +11695,14 @@ snapshots: typescript: 5.5.4 v8-compile-cache-lib: 3.0.1 - '@isaacs/ts-node-temp-fork-for-pr-2009@10.9.7(@types/node@12.20.55)(typescript@5.9.3)': + '@isaacs/ts-node-temp-fork-for-pr-2009@10.9.7(@types/node@25.4.0)(typescript@5.9.3)': dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node14': 14.1.8 '@tsconfig/node16': 16.1.8 '@tsconfig/node18': 18.2.6 '@tsconfig/node20': 20.1.9 - '@types/node': 12.20.55 + '@types/node': 25.4.0 acorn: 8.16.0 acorn-walk: 8.3.5 arg: 4.1.3 @@ -11429,6 +11721,15 @@ snapshots: '@istanbuljs/schema@0.1.3': {} + '@jest/console@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + chalk: 4.1.2 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + '@jest/console@30.3.0': dependencies: '@jest/types': 30.3.0 @@ -11438,6 +11739,41 @@ snapshots: jest-util: 30.3.0 slash: 3.0.0 + '@jest/core@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/reporters': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + ci-info: 3.9.0 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-changed-files: 29.7.0 + jest-config: 29.7.0(@types/node@25.4.0) + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-resolve-dependencies: 29.7.0 + jest-runner: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + jest-watcher: 29.7.0 + micromatch: 4.0.8 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-ansi: 6.0.1 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + - ts-node + '@jest/core@30.3.0': dependencies: '@jest/console': 30.3.0 @@ -11497,6 +11833,13 @@ snapshots: jest-util: 30.3.0 jsdom: 28.1.0 + '@jest/environment@29.7.0': + dependencies: + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + jest-mock: 29.7.0 + '@jest/environment@30.3.0': dependencies: '@jest/fake-timers': 30.3.0 @@ -11512,6 +11855,13 @@ snapshots: dependencies: '@jest/get-type': 30.1.0 + '@jest/expect@29.7.0': + dependencies: + expect: 29.7.0 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + '@jest/expect@30.3.0': dependencies: expect: 30.3.0 @@ -11519,6 +11869,15 @@ snapshots: transitivePeerDependencies: - supports-color + '@jest/fake-timers@29.7.0': + dependencies: + '@jest/types': 29.6.3 + '@sinonjs/fake-timers': 10.3.0 + '@types/node': 25.4.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + '@jest/fake-timers@30.3.0': dependencies: '@jest/types': 30.3.0 @@ -11530,6 +11889,15 @@ snapshots: '@jest/get-type@30.1.0': {} + '@jest/globals@29.7.0': + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/types': 29.6.3 + jest-mock: 29.7.0 + transitivePeerDependencies: + - supports-color + '@jest/globals@30.3.0': dependencies: '@jest/environment': 30.3.0 @@ -11544,6 +11912,35 @@ snapshots: '@types/node': 12.20.55 jest-regex-util: 30.0.1 + '@jest/reporters@29.7.0': + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@jest/console': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 25.4.0 + chalk: 4.1.2 + collect-v8-coverage: 1.0.3 + exit: 0.1.2 + glob: 7.2.3 + graceful-fs: 4.2.11 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 + istanbul-lib-report: 3.0.1 + istanbul-lib-source-maps: 4.0.1 + istanbul-reports: 3.2.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + jest-worker: 29.7.0 + slash: 3.0.0 + string-length: 4.0.2 + strip-ansi: 6.0.1 + v8-to-istanbul: 9.3.0 + transitivePeerDependencies: + - supports-color + '@jest/reporters@30.3.0': dependencies: '@bcoe/v8-coverage': 0.2.3 @@ -11587,12 +11984,25 @@ snapshots: graceful-fs: 4.2.11 natural-compare: 1.4.0 - '@jest/source-map@30.0.1': + '@jest/source-map@29.6.3': dependencies: '@jridgewell/trace-mapping': 0.3.31 callsites: 3.1.0 graceful-fs: 4.2.11 + '@jest/source-map@30.0.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + callsites: 3.1.0 + graceful-fs: 4.2.11 + + '@jest/test-result@29.7.0': + dependencies: + '@jest/console': 29.7.0 + '@jest/types': 29.6.3 + '@types/istanbul-lib-coverage': 2.0.6 + collect-v8-coverage: 1.0.3 + '@jest/test-result@30.3.0': dependencies: '@jest/console': 30.3.0 @@ -11600,6 +12010,13 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.3 + '@jest/test-sequencer@29.7.0': + dependencies: + '@jest/test-result': 29.7.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + slash: 3.0.0 + '@jest/test-sequencer@30.3.0': dependencies: '@jest/test-result': 30.3.0 @@ -11607,6 +12024,26 @@ snapshots: jest-haste-map: 30.3.0 slash: 3.0.0 + '@jest/transform@29.7.0': + dependencies: + '@babel/core': 7.29.0 + '@jest/types': 29.6.3 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 6.1.1 + chalk: 4.1.2 + convert-source-map: 2.0.0 + fast-json-stable-stringify: 2.1.0 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + micromatch: 4.0.8 + pirates: 4.0.7 + slash: 3.0.0 + write-file-atomic: 4.0.2 + transitivePeerDependencies: + - supports-color + '@jest/transform@30.3.0': dependencies: '@babel/core': 7.29.0 @@ -11637,7 +12074,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 12.20.55 + '@types/node': 25.4.0 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -12077,23 +12514,27 @@ snapshots: dependencies: type-detect: 4.0.8 + '@sinonjs/fake-timers@10.3.0': + dependencies: + '@sinonjs/commons': 3.0.1 + '@sinonjs/fake-timers@15.1.1': dependencies: '@sinonjs/commons': 3.0.1 - '@tapjs/after-each@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/after-each@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) function-loop: 4.0.0 - '@tapjs/after@3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/after@3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) is-actual-promise: 1.0.2 - '@tapjs/asserts@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': + '@tapjs/asserts@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/stack': 4.0.0 is-actual-promise: 1.0.2 tcompare: 9.0.0(react-dom@16.4.0(react@18.3.1))(react@18.3.1) @@ -12102,35 +12543,35 @@ snapshots: - react - react-dom - '@tapjs/before-each@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/before-each@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) function-loop: 4.0.0 - '@tapjs/before@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/before@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) is-actual-promise: 1.0.2 - '@tapjs/chdir@3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/chdir@3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/config@5.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/config@5.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) chalk: 5.6.2 jackspeak: 4.2.3 polite-json: 5.0.0 tap-yaml: 4.0.0 walk-up-path: 4.0.0 - '@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': + '@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': dependencies: '@tapjs/processinfo': 3.1.9 '@tapjs/stack': 4.0.0 - '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) async-hook-domain: 4.0.1 diff: 5.2.2 is-actual-promise: 1.0.2 @@ -12151,33 +12592,33 @@ snapshots: dependencies: minipass: 7.1.3 - '@tapjs/filter@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/filter@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/fixture@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/fixture@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) mkdirp: 3.0.1 rimraf: 6.0.1 - '@tapjs/intercept@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/intercept@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/stack': 4.0.0 - '@tapjs/mock@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/mock@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/stack': 4.0.0 resolve-import: 2.4.0 walk-up-path: 4.0.0 - '@tapjs/node-serialize@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/node-serialize@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/error-serdes': 4.0.0 '@tapjs/stack': 4.0.0 tap-parser: 18.0.0 @@ -12190,10 +12631,10 @@ snapshots: signal-exit: 4.1.0 uuid: 8.3.2 - '@tapjs/reporter@4.0.1(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))': + '@tapjs/reporter@4.0.1(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))': dependencies: - '@tapjs/config': 5.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/config': 5.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/stack': 4.0.0 chalk: 5.6.2 ink: 5.2.1(react@18.3.1) @@ -12214,17 +12655,17 @@ snapshots: - react-dom - utf-8-validate - '@tapjs/run@4.0.1(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': + '@tapjs/run@4.0.1(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': dependencies: - '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/config': 5.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/config': 5.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/processinfo': 3.1.9 - '@tapjs/reporter': 4.0.1(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1)) - '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/reporter': 4.0.1(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1)) + '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) c8: 10.1.3 chalk: 5.6.2 chokidar: 3.6.0 @@ -12258,9 +12699,9 @@ snapshots: - supports-color - utf-8-validate - '@tapjs/snapshot@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': + '@tapjs/snapshot@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) is-actual-promise: 1.0.2 tcompare: 9.0.0(react-dom@16.4.0(react@18.3.1))(react@18.3.1) trivial-deferred: 2.0.0 @@ -12268,36 +12709,36 @@ snapshots: - react - react-dom - '@tapjs/spawn@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/spawn@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@tapjs/stack@4.0.0': {} - '@tapjs/stdin@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': - dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - - '@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': - dependencies: - '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@12.20.55)(typescript@5.5.4) - '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/after-each': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/asserts': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/before-each': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/chdir': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/filter': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/fixture': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/intercept': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/mock': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/node-serialize': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/snapshot': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/typescript': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(typescript@5.5.4) - '@tapjs/worker': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/stdin@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + dependencies: + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + + '@tapjs/test@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)': + dependencies: + '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@25.4.0)(typescript@5.5.4) + '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/after-each': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/asserts': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/before-each': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/chdir': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/filter': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/fixture': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/intercept': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/mock': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/node-serialize': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/snapshot': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/typescript': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(typescript@5.5.4) + '@tapjs/worker': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) glob: 11.1.0 jackspeak: 4.2.3 mkdirp: 3.0.1 @@ -12316,29 +12757,29 @@ snapshots: - react - react-dom - '@tapjs/typescript@3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(typescript@5.5.4)': + '@tapjs/typescript@3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(typescript@5.5.4)': dependencies: - '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@12.20.55)(typescript@5.5.4) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@25.4.0)(typescript@5.5.4) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' - '@types/node' - typescript - '@tapjs/typescript@3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(typescript@5.9.3)': + '@tapjs/typescript@3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(typescript@5.9.3)': dependencies: - '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@12.20.55)(typescript@5.9.3) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@isaacs/ts-node-temp-fork-for-pr-2009': 10.9.7(@types/node@25.4.0)(typescript@5.9.3) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) transitivePeerDependencies: - '@swc/core' - '@swc/wasm' - '@types/node' - typescript - '@tapjs/worker@4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': + '@tapjs/worker@4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))': dependencies: - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) '@testim/chrome-version@1.1.4': {} @@ -12437,6 +12878,10 @@ snapshots: '@types/fastestsmallesttextencoderdecoder@1.0.2': {} + '@types/graceful-fs@4.1.9': + dependencies: + '@types/node': 25.4.0 + '@types/html-minifier-terser@6.1.0': {} '@types/http-errors@2.0.5': {} @@ -12489,6 +12934,10 @@ snapshots: '@types/node@12.20.55': {} + '@types/node@25.4.0': + dependencies: + undici-types: 7.18.2 + '@types/parse-json@4.0.2': {} '@types/prop-types@15.5.9': {} @@ -13361,6 +13810,19 @@ snapshots: transitivePeerDependencies: - supports-color + babel-jest@29.7.0(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/transform': 29.7.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 6.1.1 + babel-preset-jest: 29.6.3(@babel/core@7.29.0) + chalk: 4.1.2 + graceful-fs: 4.2.11 + slash: 3.0.0 + transitivePeerDependencies: + - supports-color + babel-jest@30.3.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -13401,6 +13863,16 @@ snapshots: transitivePeerDependencies: - supports-color + babel-plugin-istanbul@6.1.1: + dependencies: + '@babel/helper-plugin-utils': 7.28.6 + '@istanbuljs/load-nyc-config': 1.1.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-instrument: 5.2.1 + test-exclude: 6.0.0 + transitivePeerDependencies: + - supports-color + babel-plugin-istanbul@7.0.1: dependencies: '@babel/helper-plugin-utils': 7.28.6 @@ -13413,6 +13885,13 @@ snapshots: babel-plugin-jest-hoist@23.2.0: {} + babel-plugin-jest-hoist@29.6.3: + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + '@types/babel__core': 7.20.5 + '@types/babel__traverse': 7.28.0 + babel-plugin-jest-hoist@30.3.0: dependencies: '@types/babel__core': 7.20.5 @@ -13502,6 +13981,12 @@ snapshots: babel-plugin-jest-hoist: 23.2.0 babel-plugin-syntax-object-rest-spread: 6.13.0 + babel-preset-jest@29.6.3(@babel/core@7.29.0): + dependencies: + '@babel/core': 7.29.0 + babel-plugin-jest-hoist: 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + babel-preset-jest@30.3.0(@babel/core@7.29.0): dependencies: '@babel/core': 7.29.0 @@ -13966,6 +14451,8 @@ snapshots: safe-buffer: 5.2.1 to-buffer: 1.2.2 + cjs-module-lexer@1.4.3: {} + cjs-module-lexer@2.2.0: {} classnames@2.5.1: {} @@ -14002,7 +14489,7 @@ snapshots: dependencies: base64-loader: 1.0.0 - clipcc-sb1-converter@1.0.326: + clipcc-sb1-converter@1.0.327: dependencies: '@turbowarp/nanolog': 1.0.1 fastestsmallesttextencoderdecoder: 1.0.22 @@ -14205,9 +14692,9 @@ snapshots: core-util-is@1.0.3: {} - cosmiconfig-typescript-loader@6.2.0(@types/node@12.20.55)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): + cosmiconfig-typescript-loader@6.2.0(@types/node@25.4.0)(cosmiconfig@9.0.1(typescript@5.9.3))(typescript@5.9.3): dependencies: - '@types/node': 12.20.55 + '@types/node': 25.4.0 cosmiconfig: 9.0.1(typescript@5.9.3) jiti: 2.6.1 typescript: 5.9.3 @@ -14253,6 +14740,21 @@ snapshots: safe-buffer: 5.2.1 sha.js: 2.4.12 + create-jest@29.7.0(@types/node@25.4.0): + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + exit: 0.1.2 + graceful-fs: 4.2.11 + jest-config: 29.7.0(@types/node@25.4.0) + jest-util: 29.7.0 + prompts: 2.4.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + cross-env@10.1.0: dependencies: '@epic-web/invariant': 1.0.0 @@ -15089,13 +15591,13 @@ snapshots: '@typescript-eslint/experimental-utils': 1.13.0(eslint@9.39.2(jiti@2.6.1)) eslint: 9.39.2(jiti@2.6.1) - eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.57.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3): + eslint-plugin-jest@27.9.0(@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@8.57.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3): dependencies: '@typescript-eslint/utils': 5.62.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) eslint: 9.39.2(jiti@2.6.1) optionalDependencies: '@typescript-eslint/eslint-plugin': 7.18.0(@typescript-eslint/parser@8.57.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) - jest: 30.3.0(@types/node@12.20.55) + jest: 30.3.0(@types/node@25.4.0) transitivePeerDependencies: - supports-color - typescript @@ -16844,6 +17346,16 @@ snapshots: transitivePeerDependencies: - supports-color + istanbul-lib-instrument@5.2.1: + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.0 + '@istanbuljs/schema': 0.1.3 + istanbul-lib-coverage: 3.2.2 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-instrument@6.0.3: dependencies: '@babel/core': 7.29.0 @@ -16860,6 +17372,14 @@ snapshots: make-dir: 4.0.0 supports-color: 7.2.0 + istanbul-lib-source-maps@4.0.1: + dependencies: + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 + source-map: 0.6.1 + transitivePeerDependencies: + - supports-color + istanbul-lib-source-maps@5.0.6: dependencies: '@jridgewell/trace-mapping': 0.3.31 @@ -16907,12 +17427,44 @@ snapshots: cssfontparser: 1.2.1 moo-color: 1.0.3 + jest-changed-files@29.7.0: + dependencies: + execa: 5.1.1 + jest-util: 29.7.0 + p-limit: 3.1.0 + jest-changed-files@30.3.0: dependencies: execa: 5.1.1 jest-util: 30.3.0 p-limit: 3.1.0 + jest-circus@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/expect': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + chalk: 4.1.2 + co: 4.6.0 + dedent: 1.7.2 + is-generator-fn: 2.1.0 + jest-each: 29.7.0 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-runtime: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + p-limit: 3.1.0 + pretty-format: 29.7.0 + pure-rand: 6.1.0 + slash: 3.0.0 + stack-utils: 2.0.6 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-circus@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -16939,6 +17491,25 @@ snapshots: - babel-plugin-macros - supports-color + jest-cli@29.7.0(@types/node@25.4.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + chalk: 4.1.2 + create-jest: 29.7.0(@types/node@25.4.0) + exit: 0.1.2 + import-local: 3.2.0 + jest-config: 29.7.0(@types/node@25.4.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest-cli@30.3.0(@types/node@12.20.55): dependencies: '@jest/core': 30.3.0 @@ -16958,6 +17529,55 @@ snapshots: - supports-color - ts-node + jest-cli@30.3.0(@types/node@25.4.0): + dependencies: + '@jest/core': 30.3.0 + '@jest/test-result': 30.3.0 + '@jest/types': 30.3.0 + chalk: 4.1.2 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.3.0(@types/node@25.4.0) + jest-util: 30.3.0 + jest-validate: 30.3.0 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + + jest-config@29.7.0(@types/node@25.4.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/test-sequencer': 29.7.0 + '@jest/types': 29.6.3 + babel-jest: 29.7.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 3.9.0 + deepmerge: 4.3.1 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-circus: 29.7.0 + jest-environment-node: 29.7.0 + jest-get-type: 29.6.3 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-runner: 29.7.0 + jest-util: 29.7.0 + jest-validate: 29.7.0 + micromatch: 4.0.8 + parse-json: 5.2.0 + pretty-format: 29.7.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.4.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-config@30.3.0(@types/node@12.20.55): dependencies: '@babel/core': 7.29.0 @@ -16989,6 +17609,37 @@ snapshots: - babel-plugin-macros - supports-color + jest-config@30.3.0(@types/node@25.4.0): + dependencies: + '@babel/core': 7.29.0 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.3.0 + '@jest/types': 30.3.0 + babel-jest: 30.3.0(@babel/core@7.29.0) + chalk: 4.1.2 + ci-info: 4.4.0 + deepmerge: 4.3.1 + glob: 10.5.0 + graceful-fs: 4.2.11 + jest-circus: 30.3.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.3.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.3.0 + jest-runner: 30.3.0 + jest-util: 30.3.0 + jest-validate: 30.3.0 + parse-json: 5.2.0 + pretty-format: 30.3.0 + slash: 3.0.0 + strip-json-comments: 3.1.1 + optionalDependencies: + '@types/node': 25.4.0 + transitivePeerDependencies: + - babel-plugin-macros + - supports-color + jest-diff@29.7.0: dependencies: chalk: 4.1.2 @@ -17003,10 +17654,22 @@ snapshots: chalk: 4.1.2 pretty-format: 30.3.0 + jest-docblock@29.7.0: + dependencies: + detect-newline: 3.1.0 + jest-docblock@30.2.0: dependencies: detect-newline: 3.1.0 + jest-each@29.7.0: + dependencies: + '@jest/types': 29.6.3 + chalk: 4.1.2 + jest-get-type: 29.6.3 + jest-util: 29.7.0 + pretty-format: 29.7.0 + jest-each@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -17025,6 +17688,15 @@ snapshots: - supports-color - utf-8-validate + jest-environment-node@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + jest-mock: 29.7.0 + jest-util: 29.7.0 + jest-environment-node@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -17039,6 +17711,22 @@ snapshots: jest-get-type@29.6.3: {} + jest-haste-map@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/graceful-fs': 4.1.9 + '@types/node': 25.4.0 + anymatch: 3.1.3 + fb-watchman: 2.0.2 + graceful-fs: 4.2.11 + jest-regex-util: 29.6.3 + jest-util: 29.7.0 + jest-worker: 29.7.0 + micromatch: 4.0.8 + walker: 1.0.8 + optionalDependencies: + fsevents: 2.3.3 + jest-haste-map@30.3.0: dependencies: '@jest/types': 30.3.0 @@ -17068,6 +17756,11 @@ snapshots: strip-ansi: 4.0.0 xml: 1.0.1 + jest-leak-detector@29.7.0: + dependencies: + jest-get-type: 29.6.3 + pretty-format: 29.7.0 + jest-leak-detector@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -17113,18 +17806,37 @@ snapshots: slash: 3.0.0 stack-utils: 2.0.6 + jest-mock@29.7.0: + dependencies: + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + jest-util: 29.7.0 + jest-mock@30.3.0: dependencies: '@jest/types': 30.3.0 '@types/node': 12.20.55 jest-util: 30.3.0 + jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): + optionalDependencies: + jest-resolve: 29.7.0 + jest-pnp-resolver@1.2.3(jest-resolve@30.3.0): optionalDependencies: jest-resolve: 30.3.0 + jest-regex-util@29.6.3: {} + jest-regex-util@30.0.1: {} + jest-resolve-dependencies@29.7.0: + dependencies: + jest-regex-util: 29.6.3 + jest-snapshot: 29.7.0 + transitivePeerDependencies: + - supports-color + jest-resolve-dependencies@30.3.0: dependencies: jest-regex-util: 30.0.1 @@ -17132,6 +17844,18 @@ snapshots: transitivePeerDependencies: - supports-color + jest-resolve@29.7.0: + dependencies: + chalk: 4.1.2 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) + jest-util: 29.7.0 + jest-validate: 29.7.0 + resolve: 1.22.11 + resolve.exports: 2.0.3 + slash: 3.0.0 + jest-resolve@30.3.0: dependencies: chalk: 4.1.2 @@ -17143,6 +17867,32 @@ snapshots: slash: 3.0.0 unrs-resolver: 1.11.1 + jest-runner@29.7.0: + dependencies: + '@jest/console': 29.7.0 + '@jest/environment': 29.7.0 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + chalk: 4.1.2 + emittery: 0.13.1 + graceful-fs: 4.2.11 + jest-docblock: 29.7.0 + jest-environment-node: 29.7.0 + jest-haste-map: 29.7.0 + jest-leak-detector: 29.7.0 + jest-message-util: 29.7.0 + jest-resolve: 29.7.0 + jest-runtime: 29.7.0 + jest-util: 29.7.0 + jest-watcher: 29.7.0 + jest-worker: 29.7.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 + transitivePeerDependencies: + - supports-color + jest-runner@30.3.0: dependencies: '@jest/console': 30.3.0 @@ -17170,6 +17920,33 @@ snapshots: transitivePeerDependencies: - supports-color + jest-runtime@29.7.0: + dependencies: + '@jest/environment': 29.7.0 + '@jest/fake-timers': 29.7.0 + '@jest/globals': 29.7.0 + '@jest/source-map': 29.6.3 + '@jest/test-result': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + chalk: 4.1.2 + cjs-module-lexer: 1.4.3 + collect-v8-coverage: 1.0.3 + glob: 7.2.3 + graceful-fs: 4.2.11 + jest-haste-map: 29.7.0 + jest-message-util: 29.7.0 + jest-mock: 29.7.0 + jest-regex-util: 29.6.3 + jest-resolve: 29.7.0 + jest-snapshot: 29.7.0 + jest-util: 29.7.0 + slash: 3.0.0 + strip-bom: 4.0.0 + transitivePeerDependencies: + - supports-color + jest-runtime@30.3.0: dependencies: '@jest/environment': 30.3.0 @@ -17197,6 +17974,31 @@ snapshots: transitivePeerDependencies: - supports-color + jest-snapshot@29.7.0: + dependencies: + '@babel/core': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/plugin-syntax-jsx': 7.28.6(@babel/core@7.29.0) + '@babel/plugin-syntax-typescript': 7.28.6(@babel/core@7.29.0) + '@babel/types': 7.29.0 + '@jest/expect-utils': 29.7.0 + '@jest/transform': 29.7.0 + '@jest/types': 29.6.3 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.0) + chalk: 4.1.2 + expect: 29.7.0 + graceful-fs: 4.2.11 + jest-diff: 29.7.0 + jest-get-type: 29.6.3 + jest-matcher-utils: 29.7.0 + jest-message-util: 29.7.0 + jest-util: 29.7.0 + natural-compare: 1.4.0 + pretty-format: 29.7.0 + semver: 7.7.4 + transitivePeerDependencies: + - supports-color + jest-snapshot@30.3.0: dependencies: '@babel/core': 7.29.0 @@ -17226,7 +18028,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 12.20.55 + '@types/node': 25.4.0 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -17250,6 +18052,15 @@ snapshots: leven: 3.1.0 pretty-format: 24.9.0 + jest-validate@29.7.0: + dependencies: + '@jest/types': 29.6.3 + camelcase: 6.3.0 + chalk: 4.1.2 + jest-get-type: 29.6.3 + leven: 3.1.0 + pretty-format: 29.7.0 + jest-validate@30.3.0: dependencies: '@jest/get-type': 30.1.0 @@ -17259,6 +18070,17 @@ snapshots: leven: 3.1.0 pretty-format: 30.3.0 + jest-watcher@29.7.0: + dependencies: + '@jest/test-result': 29.7.0 + '@jest/types': 29.6.3 + '@types/node': 25.4.0 + ansi-escapes: 4.3.2 + chalk: 4.1.2 + emittery: 0.13.1 + jest-util: 29.7.0 + string-length: 4.0.2 + jest-watcher@30.3.0: dependencies: '@jest/test-result': 30.3.0 @@ -17276,6 +18098,13 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest-worker@29.7.0: + dependencies: + '@types/node': 25.4.0 + jest-util: 29.7.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + jest-worker@30.3.0: dependencies: '@types/node': 12.20.55 @@ -17284,6 +18113,18 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 + jest@29.7.0(@types/node@25.4.0): + dependencies: + '@jest/core': 29.7.0 + '@jest/types': 29.6.3 + import-local: 3.2.0 + jest-cli: 29.7.0(@types/node@25.4.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - supports-color + - ts-node + jest@30.3.0(@types/node@12.20.55): dependencies: '@jest/core': 30.3.0 @@ -17297,6 +18138,19 @@ snapshots: - supports-color - ts-node + jest@30.3.0(@types/node@25.4.0): + dependencies: + '@jest/core': 30.3.0 + '@jest/types': 30.3.0 + import-local: 3.2.0 + jest-cli: 30.3.0(@types/node@25.4.0) + transitivePeerDependencies: + - '@types/node' + - babel-plugin-macros + - esbuild-register + - supports-color + - ts-node + jiti@2.6.1: {} jpeg-js@0.4.4: {} @@ -17464,6 +18318,8 @@ snapshots: kind-of@6.0.3: {} + kleur@3.0.3: {} + klona@2.0.6: {} launch-editor@2.13.1: @@ -18685,6 +19541,11 @@ snapshots: dependencies: asap: 2.0.6 + prompts@2.4.2: + dependencies: + kleur: 3.0.3 + sisteransi: 1.0.5 + prop-types-exact@1.2.7: dependencies: call-bound: 1.0.4 @@ -18750,6 +19611,8 @@ snapshots: punycode@2.3.1: {} + pure-rand@6.1.0: {} + pure-rand@7.0.1: {} pvtsutils@1.3.6: @@ -18821,12 +19684,6 @@ snapshots: raw-loader@0.5.1: {} - raw-loader@4.0.2(webpack@5.105.4): - dependencies: - loader-utils: 2.0.4 - schema-utils: 3.3.0 - webpack: 5.105.4(webpack-cli@6.0.1) - react-contextmenu@2.9.4(prop-types@15.8.1)(react-dom@16.2.1(react@16.2.0))(react@16.2.0): dependencies: classnames: 2.5.1 @@ -19210,6 +20067,8 @@ snapshots: glob: 13.0.6 walk-up-path: 4.0.0 + resolve.exports@2.0.3: {} + resolve@1.22.11: dependencies: is-core-module: 2.16.1 @@ -19538,6 +20397,8 @@ snapshots: mrmime: 2.0.1 totalist: 3.0.1 + sisteransi@1.0.5: {} + slash@2.0.0: {} slash@3.0.0: {} @@ -19937,27 +20798,27 @@ snapshots: yaml: 2.8.2 yaml-types: 0.4.0(yaml@2.8.2) - tap@21.0.1(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3): - dependencies: - '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/after-each': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/asserts': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/before-each': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/chdir': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/core': 4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/filter': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/fixture': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/intercept': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/mock': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/node-serialize': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/run': 4.0.1(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/snapshot': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) - '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) - '@tapjs/typescript': 3.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@12.20.55)(typescript@5.9.3) - '@tapjs/worker': 4.0.0(@tapjs/core@4.0.0(@types/node@12.20.55)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + tap@21.0.1(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)(typescript@5.9.3): + dependencies: + '@tapjs/after': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/after-each': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/asserts': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/before': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/before-each': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/chdir': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/core': 4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/filter': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/fixture': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/intercept': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/mock': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/node-serialize': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/run': 4.0.1(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/snapshot': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/spawn': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/stdin': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) + '@tapjs/test': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1) + '@tapjs/typescript': 3.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1))(@types/node@25.4.0)(typescript@5.9.3) + '@tapjs/worker': 4.0.0(@tapjs/core@4.0.0(@types/node@25.4.0)(react-dom@16.4.0(react@18.3.1))(react@18.3.1)) resolve-import: 2.4.0 transitivePeerDependencies: - '@swc/core' @@ -20193,16 +21054,16 @@ snapshots: dependencies: typescript: 5.9.3 - ts-jest-mock-import-meta@1.2.1(ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3)): + ts-jest-mock-import-meta@1.2.1(ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3)): dependencies: - ts-jest: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3) + ts-jest: 29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3) - ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@12.20.55))(typescript@5.9.3): + ts-jest@29.4.6(@babel/core@7.29.0)(@jest/transform@30.3.0)(@jest/types@30.3.0)(babel-jest@30.3.0(@babel/core@7.29.0))(jest-util@30.3.0)(jest@30.3.0(@types/node@25.4.0))(typescript@5.9.3): dependencies: bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 handlebars: 4.7.8 - jest: 30.3.0(@types/node@12.20.55) + jest: 30.3.0(@types/node@25.4.0) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 @@ -20373,6 +21234,8 @@ snapshots: buffer: 5.7.1 through: 2.3.8 + undici-types@7.18.2: {} + undici@7.22.0: {} unicode-canonical-property-names-ecmascript@2.0.1: {} @@ -20649,6 +21512,8 @@ snapshots: flat: 5.0.2 wildcard: 2.0.1 + webpack-node-externals@3.0.0: {} + webpack-sources@3.3.4: {} webpack@5.105.4(webpack-cli@6.0.1): @@ -20804,6 +21669,11 @@ snapshots: wrappy@1.0.2: {} + write-file-atomic@4.0.2: + dependencies: + imurmurhash: 0.1.4 + signal-exit: 3.0.7 + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4