diff --git a/.controlplane/Dockerfile b/.controlplane/Dockerfile index a36aae82..9a955122 100644 --- a/.controlplane/Dockerfile +++ b/.controlplane/Dockerfile @@ -80,7 +80,8 @@ RUN SECRET_KEY_BASE=precompile_placeholder yarn res:build && \ # For Kubernetes and ControlPlane, this is the command on the workload. ENTRYPOINT ["./.controlplane/entrypoint.sh"] -# Default args to pass to the entry point that can be overridden -# For Kubernetes and ControlPlane, these are the "workload args" -# Use Thruster HTTP/2 proxy for optimized performance -CMD ["bundle", "exec", "thrust", "bin/rails", "server"] +# Run the Pro Node renderer and Rails in a single container (Option 1 from +# docs/oss/building-features/node-renderer/container-deployment.md). +# `wait -n` exits on first child failure → container exits → Control Plane +# restarts the whole unit. Keeps the existing Thruster HTTP/2 proxy for Rails. +CMD ["bash", "-c", "node renderer/node-renderer.js & RENDERER_PID=$! ; bundle exec thrust bin/rails server & RAILS_PID=$! ; wait -n ; exit 1"] diff --git a/.controlplane/templates/app.yml b/.controlplane/templates/app.yml index add22f1f..0ff2ac87 100644 --- a/.controlplane/templates/app.yml +++ b/.controlplane/templates/app.yml @@ -24,6 +24,25 @@ spec: # set to a secure random value using: openssl rand -hex 64 # Production apps should configure this manually after app creation via a secret. value: 'placeholder_secret_key_base_for_test_apps_only' + # Pro Node renderer settings. The renderer process runs in the same + # container as Rails (see .controlplane/Dockerfile CMD). + - name: RENDERER_PORT + value: '3800' + - name: RENDERER_LOG_LEVEL + value: info + - name: RENDERER_WORKERS_COUNT + value: '2' + - name: RENDERER_URL + value: http://localhost:3800 + # RENDERER_PASSWORD and REACT_ON_RAILS_PRO_LICENSE must be created in the + # Control Plane Secret named by {{APP_SECRETS}} before deploy. cpflow + # resolves {{APP_SECRETS}} to `{APP_PREFIX}-secrets` — which means review + # apps all share one `qa-react-webpack-rails-tutorial-secrets` (thanks to + # match_if_app_name_starts_with: true on the qa template). + - name: RENDERER_PASSWORD + value: cpln://secret/{{APP_SECRETS}}.RENDERER_PASSWORD + - name: REACT_ON_RAILS_PRO_LICENSE + value: cpln://secret/{{APP_SECRETS}}.REACT_ON_RAILS_PRO_LICENSE # Part of standard configuration staticPlacement: locationLinks: diff --git a/.controlplane/templates/org.yml b/.controlplane/templates/org.yml index 6616376d..c04129e8 100644 --- a/.controlplane/templates/org.yml +++ b/.controlplane/templates/org.yml @@ -4,14 +4,25 @@ # other sensitive information that is shared across multiple apps # in the same organization. -# This is how you apply this once (not during CI) -# cpl apply-template secrets -a qa-react-webpack-rails-tutorial --org shakacode-open-source-examples-staging +# The qa-* dictionary is bootstrapped via this template; prod and +# staging dictionaries are created manually with real values. + +# Initial bootstrap (once, manually, not in CI): +# cpflow apply-template secrets -a qa-react-webpack-rails-tutorial --org shakacode-open-source-examples-staging +# +# Populate real values with `cpln apply -f ` or `cpln secret edit`. +# Do NOT re-apply this template after real values are set: it will +# overwrite them with the placeholders below. kind: secret name: {{APP_SECRETS}} type: dictionary data: - SOME_ENV: "123456" + # Both sides of the Rails/Node renderer handshake must match. + # Generate with `openssl rand -hex 32`. + RENDERER_PASSWORD: "replace-with-openssl-rand-hex-32" + # JWT from https://pro.reactonrails.com/; same token across envs. + REACT_ON_RAILS_PRO_LICENSE: "replace-with-pro-license-jwt" --- diff --git a/.controlplane/templates/rails.yml b/.controlplane/templates/rails.yml index 49fe1909..ed686ed1 100644 --- a/.controlplane/templates/rails.yml +++ b/.controlplane/templates/rails.yml @@ -15,8 +15,11 @@ spec: # Inherit other ENV values from GVC inheritEnv: true image: {{APP_IMAGE_LINK}} - # 512 corresponds to a standard 1x dyno type - memory: 512Mi + # 1Gi (up from 512Mi) gives the single container enough headroom for + # Rails + the Pro Node renderer. Below the marketplace demo's 2Gi + # because this tutorial's Rails surface is smaller; capacityAI below + # adjusts upward if actual usage warrants it. + memory: 1Gi ports: - number: 3000 protocol: http @@ -27,7 +30,9 @@ spec: autoscaling: # Max of 1 effectively disables autoscaling, so a like a Heroku dyno count of 1 maxScale: 1 - capacityAI: false + # CapacityAI adjusts CPU/memory within the single replica based on observed + # usage, so the 1Gi/300m baseline can grow if Rails + renderer need more. + capacityAI: true firewallConfig: external: # Default to allow public access to Rails server diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..346c9981 --- /dev/null +++ b/.env.example @@ -0,0 +1,22 @@ +# React on Rails Pro license (JWT token from https://pro.reactonrails.com/). +# Required in production; optional in development/test. +REACT_ON_RAILS_PRO_LICENSE= + +# Shared secret between Rails and the Node renderer. Must match on both sides. +# Dev/test default to `local-dev-renderer-password` if unset; production must +# set this explicitly (Pro raises at boot otherwise). +RENDERER_PASSWORD= + +# Node renderer endpoint. Defaults to http://localhost:3800. +# RENDERER_URL=http://localhost:3800 + +# Port the Node renderer listens on. Defaults to 3800. +# RENDERER_PORT=3800 + +# Log verbosity for the Node renderer (debug, info, warn, error). +# Defaults to info. +# RENDERER_LOG_LEVEL=info + +# Number of renderer worker processes. `0` is single-process mode, useful +# for local debugging. Defaults to 3; the app's CI workflow caps to 2. +# RENDERER_WORKERS_COUNT=3 diff --git a/.github/workflows/rspec_test.yml b/.github/workflows/rspec_test.yml index d117458a..83a67698 100644 --- a/.github/workflows/rspec_test.yml +++ b/.github/workflows/rspec_test.yml @@ -33,6 +33,9 @@ jobs: DRIVER: selenium_chrome CHROME_BIN: /usr/bin/google-chrome USE_COVERALLS: true + # Must match config.renderer_password in config/initializers/react_on_rails_pro.rb. + # Setting explicitly avoids silent drift if the two-sided defaults ever diverge. + RENDERER_PASSWORD: local-dev-renderer-password steps: - name: Install Chrome @@ -82,6 +85,25 @@ jobs: - name: Build shakapacker chunks run: NODE_ENV=development bundle exec bin/shakapacker + - name: Start Node renderer for SSR + run: | + node renderer/node-renderer.js & + RENDERER_PID=$! + echo "Waiting for Node renderer (PID $RENDERER_PID) on port 3800..." + for i in $(seq 1 30); do + if ! kill -0 $RENDERER_PID 2>/dev/null; then + echo "Node renderer process exited unexpectedly (see output above)." + exit 1 + fi + if nc -z localhost 3800 2>/dev/null; then + echo "Node renderer is ready" + exit 0 + fi + sleep 1 + done + echo "Node renderer failed to start within 30 seconds (see output above)." + exit 1 + - name: Run rspec with xvfb uses: coactions/setup-xvfb@v1 with: diff --git a/.gitignore b/.gitignore index 366e6a9b..198c879e 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,9 @@ client/app/bundles/comments/rescript/**/*.bs.js # Using React on Rails default directory /ssr-generated/ +# Pro Node renderer bundle cache +/renderer/.node-renderer-bundles/ + # Generated React on Rails packs **/generated/** diff --git a/Procfile.dev b/Procfile.dev index 102c0a8d..20cd0f7b 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -2,13 +2,14 @@ # You can run these commands in separate shells # # Note: bin/dev runs precompile tasks (rescript + locale) BEFORE starting these processes. -# This ensures all generated files exist before Rspack starts watching. +# This ensures all generated files exist before webpack starts watching. # # ReScript watch mode (no clean - bin/dev already did the clean build) rescript: yarn res:watch # redis: redis-server # Run Redis as a system service instead (brew services start redis) rails: bundle exec thrust bin/rails server -p 3000 -# Client Rspack dev server with HMR +# Client webpack dev server with HMR wp-client: RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server -# Server Rspack watcher for SSR bundle +# Server webpack watcher for SSR bundle wp-server: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch +node-renderer: NODE_ENV=development node renderer/node-renderer.js diff --git a/client/__tests__/webpack/bundlerUtils.spec.js b/client/__tests__/webpack/bundlerUtils.spec.js index 0e710c70..7e7def5f 100644 --- a/client/__tests__/webpack/bundlerUtils.spec.js +++ b/client/__tests__/webpack/bundlerUtils.spec.js @@ -1,7 +1,9 @@ /* eslint-disable max-classes-per-file */ /* eslint-disable global-require */ /** - * Unit tests for bundlerUtils.js in Rspack-only mode. + * Unit tests for bundlerUtils.js. The utility returns the active bundler + * module based on shakapacker.yml's `assets_bundler` setting; it supports + * both webpack and rspack. */ jest.mock('@rspack/core', () => ({ @@ -10,12 +12,18 @@ jest.mock('@rspack/core', () => ({ optimize: { LimitChunkCountPlugin: class MockRspackLimitChunkCount {} }, })); +jest.mock('webpack', () => ({ + ProvidePlugin: class MockWebpackProvidePlugin {}, + DefinePlugin: class MockWebpackDefinePlugin {}, + optimize: { LimitChunkCountPlugin: class MockWebpackLimitChunkCount {} }, +})); + describe('bundlerUtils', () => { let mockConfig; beforeEach(() => { jest.resetModules(); - mockConfig = { assets_bundler: 'rspack' }; + mockConfig = { assets_bundler: 'webpack' }; }); afterEach(() => { @@ -35,32 +43,30 @@ describe('bundlerUtils', () => { expect(bundler.CssExtractRspackPlugin.name).toBe('MockCssExtractRspackPlugin'); }); - it('throws when assets_bundler is webpack', () => { + it('returns webpack when assets_bundler is webpack', () => { mockConfig.assets_bundler = 'webpack'; jest.doMock('shakapacker', () => ({ config: mockConfig })); const utils = require('../../../config/webpack/bundlerUtils'); - expect(() => utils.getBundler()).toThrow('configured for Rspack only'); + const bundler = utils.getBundler(); + + expect(bundler).toBeDefined(); + expect(bundler.DefinePlugin).toBeDefined(); + expect(bundler.DefinePlugin.name).toBe('MockWebpackDefinePlugin'); }); - it('throws when assets_bundler is undefined', () => { + it('returns webpack when assets_bundler is undefined', () => { mockConfig.assets_bundler = undefined; jest.doMock('shakapacker', () => ({ config: mockConfig })); const utils = require('../../../config/webpack/bundlerUtils'); - expect(() => utils.getBundler()).toThrow('configured for Rspack only'); - }); - - it('throws when assets_bundler is invalid', () => { - mockConfig.assets_bundler = 'invalid-bundler'; - jest.doMock('shakapacker', () => ({ config: mockConfig })); - const utils = require('../../../config/webpack/bundlerUtils'); + const bundler = utils.getBundler(); - expect(() => utils.getBundler()).toThrow('configured for Rspack only'); + expect(bundler.DefinePlugin).toBeDefined(); }); it('returns cached bundler on subsequent calls', () => { - mockConfig.assets_bundler = 'rspack'; + mockConfig.assets_bundler = 'webpack'; jest.doMock('shakapacker', () => ({ config: mockConfig })); const utils = require('../../../config/webpack/bundlerUtils'); @@ -106,7 +112,7 @@ describe('bundlerUtils', () => { jest.doMock('shakapacker', () => ({ config: mockConfig })); const utils = require('../../../config/webpack/bundlerUtils'); - expect(() => utils.getCssExtractPlugin()).toThrow('configured for Rspack only'); + expect(() => utils.getCssExtractPlugin()).toThrow('only available when assets_bundler is rspack'); }); }); }); diff --git a/config/initializers/react_on_rails_pro.rb b/config/initializers/react_on_rails_pro.rb new file mode 100644 index 00000000..4e4f0389 --- /dev/null +++ b/config/initializers/react_on_rails_pro.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# See https://reactonrails.com/docs/configuration/configuration-pro +# Production license: set REACT_ON_RAILS_PRO_LICENSE to the JWT token from +# https://pro.reactonrails.com/. Development and test environments don't +# require a license — the Pro engine logs an info-level notice only. +ReactOnRailsPro.configure do |config| + config.server_renderer = "NodeRenderer" + # Raise loudly when the renderer is unavailable instead of silently + # degrading to ExecJS (default is true). + config.renderer_use_fallback_exec_js = false + + config.renderer_url = ENV.fetch("RENDERER_URL", "http://localhost:3800") + + # Must match the password in renderer/node-renderer.js. Use .presence + # so a blank env var (.env.example ships with `RENDERER_PASSWORD=`) + # falls back to the dev default, matching the JS side's `||`. + config.renderer_password = ENV["RENDERER_PASSWORD"].presence || "local-dev-renderer-password" +end diff --git a/config/shakapacker.yml b/config/shakapacker.yml index 9b248452..a56bb810 100644 --- a/config/shakapacker.yml +++ b/config/shakapacker.yml @@ -9,7 +9,12 @@ default: &default webpack_compile_output: true nested_entries: true javascript_transpiler: swc - assets_bundler: rspack + # TODO: flip back to rspack once shakacode/react_on_rails_rsc#29 ships a + # native rspack RSC plugin. On webpack for now because rspack + # 2.0.0-beta.7's webpack-compat layer doesn't cover the APIs the upstream + # RSCWebpackPlugin needs (contextModuleFactory.resolveDependencies, + # ModuleDependency). + assets_bundler: webpack # Additional paths webpack should lookup modules # ['app/assets', 'engine/foo/app/assets'] diff --git a/config/webpack/bundlerUtils.js b/config/webpack/bundlerUtils.js index a403abb6..2a483f20 100644 --- a/config/webpack/bundlerUtils.js +++ b/config/webpack/bundlerUtils.js @@ -1,55 +1,34 @@ -/** - * Bundler utilities for Rspack-only configuration. - * - * This repository standardizes on Rspack with Shakapacker. - */ +// Returns the active bundler module per shakapacker.yml's `assets_bundler`. +// Supports both webpack and rspack so this project can switch between them +// without touching every config file. const { config } = require('shakapacker'); -// Cache for bundler module let _cachedBundler = null; -const ensureRspack = () => { - if (config.assets_bundler !== 'rspack') { - throw new Error( - `Invalid assets_bundler: "${config.assets_bundler}". ` + - 'This project is configured for Rspack only. ' + - 'Set assets_bundler: rspack in config/shakapacker.yml', - ); - } -}; - -/** - * Gets the Rspack module for the current build. - * - * @returns {Object} @rspack/core module - * @throws {Error} If assets_bundler is not 'rspack' - */ const getBundler = () => { - ensureRspack(); - if (_cachedBundler) { return _cachedBundler; } - _cachedBundler = require('@rspack/core'); + _cachedBundler = config.assets_bundler === 'rspack' ? require('@rspack/core') : require('webpack'); return _cachedBundler; }; -/** - * Checks whether the configured bundler is Rspack. - * - * @returns {boolean} True when assets_bundler is rspack - */ const isRspack = () => config.assets_bundler === 'rspack'; -/** - * Gets the CSS extraction plugin for Rspack. - * - * @returns {Object} CssExtractRspackPlugin - */ -const getCssExtractPlugin = () => getBundler().CssExtractRspackPlugin; +// Only meaningful on rspack — webpack projects use mini-css-extract-plugin +// via shakapacker's generated config. +const getCssExtractPlugin = () => { + if (!isRspack()) { + throw new Error( + 'getCssExtractPlugin() is only available when assets_bundler is rspack. ' + + "On webpack, rely on shakapacker's generated MiniCssExtractPlugin configuration.", + ); + } + return getBundler().CssExtractRspackPlugin; +}; module.exports = { getBundler, diff --git a/config/webpack/serverWebpackConfig.js b/config/webpack/serverWebpackConfig.js index 8dada649..edcd3812 100644 --- a/config/webpack/serverWebpackConfig.js +++ b/config/webpack/serverWebpackConfig.js @@ -1,28 +1,22 @@ // The source code including full typescript support is available at: // https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh/blob/master/config/webpack/serverWebpackConfig.js +/* eslint-disable no-param-reassign */ const path = require('path'); const { config } = require('shakapacker'); +const { RSCWebpackPlugin } = require('react-on-rails-rsc/WebpackPlugin'); const commonWebpackConfig = require('./commonWebpackConfig'); const { getBundler } = require('./bundlerUtils'); -/** - * Generates the server-side rendering (SSR) bundle configuration. - * - * This creates a separate bundle optimized for server-side rendering: - * - Single chunk (no code splitting for Node.js execution) - * - CSS extraction disabled (uses exportOnlyLocals for class name mapping) - * - No asset hashing (not served directly to clients) - * - Outputs to ssr-generated/ directory - * - * Key differences from client config: - * - Removes CSS extraction loaders (mini-css-extract-plugin/CssExtractRspackPlugin) - * - Preserves CSS Modules configuration but adds exportOnlyLocals: true - * - Disables optimization/minification for faster builds and better debugging - * - * @returns {Object} Webpack/Rspack configuration object for server bundle - */ -const configureServer = () => { +function extractLoader(rule, loaderName) { + if (!Array.isArray(rule.use)) return undefined; + return rule.use.find((item) => { + const testValue = typeof item === 'string' ? item : item?.loader; + return typeof testValue === 'string' && testValue.includes(loaderName); + }); +} + +const configureServer = (rscBundle = false) => { const bundler = getBundler(); // We need to use "merge" because the clientConfigObject, EVEN after running @@ -31,7 +25,6 @@ const configureServer = () => { // Using webpack-merge into an empty object avoids this issue. const serverWebpackConfig = commonWebpackConfig(); - // We just want the single server bundle entry const serverEntry = { 'server-bundle': serverWebpackConfig.entry['server-bundle'], }; @@ -43,51 +36,49 @@ const configureServer = () => { throw new Error( `Server bundle entry 'server-bundle' not found.\n` + - `Expected file: ${fullPath}\n` + - `Current source_path: ${config.source_path}\n` + - `Current source_entry_path: ${config.source_entry_path}\n` + - `Verify:\n` + - `1. The server-bundle.js file exists at the expected location\n` + - `2. nested_entries is configured correctly in shakapacker.yml\n` + - `3. The file is properly exported from your entry point`, + `Expected file: ${fullPath}\n` + + `Current source_path: ${config.source_path}\n` + + `Current source_entry_path: ${config.source_entry_path}\n` + + `Verify:\n` + + `1. The server-bundle.js file exists at the expected location\n` + + `2. nested_entries is configured correctly in shakapacker.yml\n` + + `3. The file is properly exported from your entry point`, ); } serverWebpackConfig.entry = serverEntry; - // Remove the mini-css-extract-plugin from the style loaders because - // the client build will handle exporting CSS. - // replace file-loader with null-loader - serverWebpackConfig.module.rules.forEach((loader) => { - if (loader.use && loader.use.filter) { - loader.use = loader.use.filter( - (item) => !(typeof item === 'string' && item.match(/mini-css-extract-plugin/)), - ); - } - }); - - // No splitting of chunks for a server bundle serverWebpackConfig.optimization = { minimize: false, }; serverWebpackConfig.plugins.unshift(new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 })); - // Custom output for the server-bundle that matches the config in - // config/initializers/react_on_rails.rb - // Output to a private directory for SSR bundles (not in public/) - // Using the default React on Rails path: ssr-generated + if (!rscBundle) { + // Scope client-reference discovery to the app source dir. Without this, + // the plugin can walk into node_modules and hit .tsx source files that + // aren't configured for a loader. Derive from config.source_path so the + // scope follows shakapacker.yml instead of hardcoding client/app. + const clientReferencesDir = path.resolve(config.source_path || 'client/app'); + serverWebpackConfig.plugins.push( + new RSCWebpackPlugin({ + isServer: true, + clientReferences: [ + { directory: clientReferencesDir, recursive: true, include: /\.(js|ts|jsx|tsx)$/ }, + ], + }), + ); + } + + // libraryTarget: 'commonjs2' is required by the Pro Node renderer so it can + // `require()` the evaluated bundle. No publicPath: the server bundle is + // loaded from the filesystem, never served over HTTP. serverWebpackConfig.output = { filename: 'server-bundle.js', globalObject: 'this', - // If using the React on Rails Pro node server renderer, uncomment the next line - // libraryTarget: 'commonjs2', + libraryTarget: 'commonjs2', path: path.resolve(__dirname, '../../ssr-generated'), - publicPath: config.publicPath, - // https://webpack.js.org/configuration/output/#outputglobalobject }; - // Don't hash the server bundle b/c would conflict with the client manifest - // And no need for CSS extraction plugins (MiniCssExtractPlugin or CssExtractRspackPlugin) serverWebpackConfig.plugins = serverWebpackConfig.plugins.filter( (plugin) => plugin.constructor.name !== 'WebpackAssetsManifest' && @@ -96,64 +87,49 @@ const configureServer = () => { plugin.constructor.name !== 'ForkTsCheckerWebpackPlugin', ); - // Configure loader rules for SSR - // Remove the mini-css-extract-plugin/CssExtractRspackPlugin from the style loaders because - // the client build will handle exporting CSS. - // replace file-loader with null-loader - const rules = serverWebpackConfig.module.rules; - rules.forEach((rule) => { + serverWebpackConfig.module.rules.forEach((rule) => { if (Array.isArray(rule.use)) { - // remove the mini-css-extract-plugin/CssExtractRspackPlugin and style-loader rule.use = rule.use.filter((item) => { - let testValue; - if (typeof item === 'string') { - testValue = item; - } else if (typeof item.loader === 'string') { - testValue = item.loader; - } + const testValue = typeof item === 'string' ? item : item?.loader; + if (typeof testValue !== 'string') return true; return !( - testValue?.match(/mini-css-extract-plugin/) || - testValue?.match(/CssExtractRspackPlugin/) || - testValue?.includes('cssExtractLoader') || + testValue.match(/mini-css-extract-plugin/) || + testValue.match(/CssExtractRspackPlugin/) || + testValue.includes('cssExtractLoader') || testValue === 'style-loader' ); }); - const cssLoader = rule.use.find((item) => { - let testValue; - - if (typeof item === 'string') { - testValue = item; - } else if (typeof item.loader === 'string') { - testValue = item.loader; - } - return testValue?.includes('css-loader'); - }); - if (cssLoader && cssLoader.options && cssLoader.options.modules) { - // Preserve existing modules config but add exportOnlyLocals for SSR + const cssLoader = extractLoader(rule, 'css-loader'); + if (cssLoader?.options?.modules) { cssLoader.options.modules = { ...cssLoader.options.modules, exportOnlyLocals: true, }; } - // Skip writing image files during SSR by setting emitFile to false + const babelLoader = extractLoader(rule, 'babel-loader'); + if (babelLoader) { + babelLoader.options = babelLoader.options || {}; + babelLoader.options.caller = { ssr: true }; + } } else if (rule.use && (rule.use.loader === 'url-loader' || rule.use.loader === 'file-loader')) { rule.use.options.emitFile = false; } }); - // eval works well for the SSR bundle because it's the fastest and shows - // lines in the server bundle which is good for debugging SSR - // The default of cheap-module-source-map is slow and provides poor info. serverWebpackConfig.devtool = 'eval'; - // If using the default 'web', then libraries like Emotion and loadable-components - // break with SSR. The fix is to use a node renderer and change the target. - // If using the React on Rails Pro node server renderer, uncomment the next line - // serverWebpackConfig.target = 'node' + // target: 'node' fixes SSR breakage in libraries (Emotion, loadable-components, etc.) + // that don't behave under the default 'web' target. node: false disables the + // polyfill shims that only matter when targeting 'web'. + serverWebpackConfig.target = 'node'; + serverWebpackConfig.node = false; return serverWebpackConfig; }; -module.exports = configureServer; +module.exports = { + default: configureServer, + extractLoader, +}; diff --git a/config/webpack/webpackConfig.js b/config/webpack/webpackConfig.js index 4f68574e..44327e8b 100644 --- a/config/webpack/webpackConfig.js +++ b/config/webpack/webpackConfig.js @@ -2,7 +2,7 @@ // https://github.com/shakacode/react_on_rails_tutorial_with_ssr_and_hmr_fast_refresh/blob/master/config/webpack/webpackConfig.js const clientWebpackConfig = require('./clientWebpackConfig'); -const serverWebpackConfig = require('./serverWebpackConfig'); +const { default: serverWebpackConfig } = require('./serverWebpackConfig'); const webpackConfig = (envSpecific) => { const clientConfig = clientWebpackConfig(); diff --git a/package.json b/package.json index 989e4e16..1ae8c2c7 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "test:client": "yarn jest", "build:test": "rm -rf public/packs-test && RAILS_ENV=test NODE_ENV=test bin/shakapacker", "build:dev": "rm -rf public/packs && RAILS_ENV=development NODE_ENV=development bin/shakapacker", - "build:clean": "rm -rf public/packs || true" + "build:clean": "rm -rf public/packs || true", + "node-renderer": "node renderer/node-renderer.js" }, "dependencies": { "@babel/cli": "^7.21.0", @@ -77,10 +78,12 @@ "postcss-loader": "7.3.3", "postcss-preset-env": "^8.5.0", "prop-types": "^15.8.1", - "react": "^19.0.0", - "react-dom": "^19.0.0", + "react": "~19.0.4", + "react-dom": "~19.0.4", "react-intl": "^6.4.4", "react-on-rails-pro": "16.6.0", + "react-on-rails-pro-node-renderer": "16.6.0", + "react-on-rails-rsc": "19.0.4", "react-redux": "^8.1.0", "react-router": "^6.13.0", "react-router-dom": "^6.13.0", diff --git a/renderer/node-renderer.js b/renderer/node-renderer.js new file mode 100644 index 00000000..a4cb3a34 --- /dev/null +++ b/renderer/node-renderer.js @@ -0,0 +1,46 @@ +const path = require('path'); +const { reactOnRailsProNodeRenderer } = require('react-on-rails-pro-node-renderer'); + +const isProduction = process.env.NODE_ENV === 'production'; +const rendererPassword = process.env.RENDERER_PASSWORD || (!isProduction && 'local-dev-renderer-password'); + +if (!rendererPassword) { + throw new Error('RENDERER_PASSWORD must be set in production'); +} + +function parseIntegerEnv(name, defaultValue, { min, max = Number.MAX_SAFE_INTEGER }) { + const rawValue = process.env[name]; + if (rawValue == null || rawValue.trim() === '') { + return defaultValue; + } + + const normalized = rawValue.trim(); + if (!/^\d+$/.test(normalized)) { + throw new Error(`Invalid ${name}: "${rawValue}". Expected an integer.`); + } + + const parsed = Number.parseInt(normalized, 10); + if (parsed < min || parsed > max) { + throw new Error(`Invalid ${name}: "${rawValue}". Expected a value between ${min} and ${max}.`); + } + + return parsed; +} + +const config = { + serverBundleCachePath: path.resolve(__dirname, '.node-renderer-bundles'), + logLevel: process.env.RENDERER_LOG_LEVEL || 'info', + password: rendererPassword, + port: parseIntegerEnv('RENDERER_PORT', 3800, { min: 1, max: 65535 }), + supportModules: true, + // CI hosts report more CPUs than allocated to the container; default to + // 2 workers on CI to avoid oversubscribing memory. Explicit + // RENDERER_WORKERS_COUNT still wins on either path. + workersCount: parseIntegerEnv('RENDERER_WORKERS_COUNT', process.env.CI ? 2 : 3, { min: 0 }), + // Expose globals the VM sandbox doesn't auto-provide but that downstream + // deps rely on during SSR. Without URL, react-router-dom's NavLink throws + // `ReferenceError: URL is not defined` via encodeLocation. + additionalContext: { URL, AbortController }, +}; + +reactOnRailsProNodeRenderer(config); diff --git a/yarn.lock b/yarn.lock index ceb7d3f4..833bca34 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1336,6 +1336,76 @@ resolved "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz" integrity sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q== +"@fastify/ajv-compiler@^4.0.5": + version "4.0.5" + resolved "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-4.0.5.tgz#fdb0887a7af51abaae8c1829e8099d34f8ddd302" + integrity sha512-KoWKW+MhvfTRWL4qrhUwAAZoaChluo0m0vbiJlGMt2GXvL4LVPQEjt8kSpHI3IBq5Rez8fg+XeH3cneztq+C7A== + dependencies: + ajv "^8.12.0" + ajv-formats "^3.0.1" + fast-uri "^3.0.0" + +"@fastify/busboy@^3.0.0": + version "3.2.0" + resolved "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz#13ed8212f3b9ba697611529d15347f8528058cea" + integrity sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA== + +"@fastify/deepmerge@^3.0.0": + version "3.2.1" + resolved "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-3.2.1.tgz#0fe56a4ee3eec874556006439f7bc7d616f10dc1" + integrity sha512-N5Oqvltoa2r9z1tbx4xjky0oRR60v+T47Ic4J1ukoVQcptLOrIdRnCSdTGmOmajZuHVKlTnfcmrjyqsGEW1ztA== + +"@fastify/error@^4.0.0": + version "4.2.0" + resolved "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz#d40f46ba75f541fdcc4dc276b7308bbc8e8e6d7a" + integrity sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ== + +"@fastify/fast-json-stringify-compiler@^5.0.0": + version "5.0.3" + resolved "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.0.3.tgz#fae495bf30dbbd029139839ec5c2ea111bde7d3f" + integrity sha512-uik7yYHkLr6fxd8hJSZ8c+xF4WafPK+XzneQDPU+D10r5X19GW8lJcom2YijX2+qtFF1ENJlHXKFM9ouXNJYgQ== + dependencies: + fast-json-stringify "^6.0.0" + +"@fastify/formbody@^7.4.0 || ^8.0.2": + version "8.0.2" + resolved "https://registry.npmjs.org/@fastify/formbody/-/formbody-8.0.2.tgz#7f97c8ab25933db77760bbeaacd2ff5355a54682" + integrity sha512-84v5J2KrkXzjgBpYnaNRPqwgMsmY7ZDjuj0YVuMR3NXCJRCgKEZy/taSP1wUYGn0onfxJpLyRGDLa+NMaDJtnA== + dependencies: + fast-querystring "^1.1.2" + fastify-plugin "^5.0.0" + +"@fastify/forwarded@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@fastify/forwarded/-/forwarded-3.0.1.tgz#9662b7bd4a59f6d123cc3487494f75f635c32d23" + integrity sha512-JqDochHFqXs3C3Ml3gOY58zM7OqO9ENqPo0UqAjAjH8L01fRZqwX9iLeX34//kiJubF7r2ZQHtBRU36vONbLlw== + +"@fastify/merge-json-schemas@^0.2.0": + version "0.2.1" + resolved "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz#3aa30d2f0c81a8ac5995b6d94ed4eaa2c3055824" + integrity sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A== + dependencies: + dequal "^2.0.3" + +"@fastify/multipart@^8.3.1 || ^9.0.3": + version "9.4.0" + resolved "https://registry.npmjs.org/@fastify/multipart/-/multipart-9.4.0.tgz#be50e7d12d989cb42b835a5e46e08b40ab5b0728" + integrity sha512-Z404bzZeLSXTBmp/trCBuoVFX28pM7rhv849Q5TsbTFZHuk1lc4QjQITTPK92DKVpXmNtJXeHSSc7GYvqFpxAQ== + dependencies: + "@fastify/busboy" "^3.0.0" + "@fastify/deepmerge" "^3.0.0" + "@fastify/error" "^4.0.0" + fastify-plugin "^5.0.0" + secure-json-parse "^4.0.0" + +"@fastify/proxy-addr@^5.0.0": + version "5.1.0" + resolved "https://registry.npmjs.org/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz#f5360b5dd83c7de3d41b415be4aab84ae44aa106" + integrity sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw== + dependencies: + "@fastify/forwarded" "^3.0.0" + ipaddr.js "^2.1.0" + "@formatjs/ecma402-abstract@2.2.4": version "2.2.4" resolved "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.2.4.tgz" @@ -1956,6 +2026,11 @@ "@parcel/watcher-win32-ia32" "2.5.1" "@parcel/watcher-win32-x64" "2.5.1" +"@pinojs/redact@^0.4.0": + version "0.4.0" + resolved "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz#c3de060dd12640dcc838516aa2a6803cc7b2e9d6" + integrity sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg== + "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" @@ -2822,6 +2897,11 @@ resolved "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== +abstract-logging@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" + integrity sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA== + accepts@~1.3.8: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" @@ -2840,6 +2920,13 @@ acorn-jsx@^5.3.2: resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== +acorn-loose@^8.3.0: + version "8.5.2" + resolved "https://registry.npmjs.org/acorn-loose/-/acorn-loose-8.5.2.tgz#a7cc7dfbb7c8f3c2e55b055db640dc657e278d26" + integrity sha512-PPvV6g8UGMGgjrMu+n/f9E/tCSkNQ2Y97eFvuVdJfG11+xdIeDcLyNdC8SHcrHbRqkfwLASdplyR6B6sKM1U4A== + dependencies: + acorn "^8.15.0" + acorn@^8.15.0, acorn@^8.9.0: version "8.15.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz" @@ -2875,6 +2962,13 @@ ajv-formats@^2.1.1: dependencies: ajv "^8.0.0" +ajv-formats@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz#3d5dc762bca17679c3c2ea7e90ad6b7532309578" + integrity sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ== + dependencies: + ajv "^8.0.0" + ajv-keywords@^3.5.2: version "3.5.2" resolved "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz" @@ -2907,6 +3001,16 @@ ajv@^8.0.0, ajv@^8.17.1, ajv@^8.9.0: json-schema-traverse "^1.0.0" require-from-string "^2.0.2" +ajv@^8.12.0: + version "8.18.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz#8864186b6738d003eb3a933172bb3833e10cefbc" + integrity sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A== + dependencies: + fast-deep-equal "^3.1.3" + fast-uri "^3.0.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz" @@ -3149,6 +3253,11 @@ atob@^2.1.2: resolved "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== +atomic-sleep@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" + integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== + autoprefixer@^10.4.14: version "10.4.21" resolved "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz" @@ -3168,6 +3277,14 @@ available-typed-arrays@^1.0.7: dependencies: possible-typed-array-names "^1.0.0" +avvio@^9.0.0: + version "9.2.0" + resolved "https://registry.npmjs.org/avvio/-/avvio-9.2.0.tgz#16bb653c022237d1aeb984b00d3cbe2d96b77c20" + integrity sha512-2t/sy01ArdHHE0vRH5Hsay+RtCZt3dLPji7W7/MMOCEgze5b7SNDC4j5H6FnVgPkI1MTNFGzHdHrVXDDl7QSSQ== + dependencies: + "@fastify/error" "^4.0.0" + fastq "^1.17.1" + axe-core@^4.10.0: version "4.10.3" resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz" @@ -3407,6 +3524,11 @@ bser@2.1.1: dependencies: node-int64 "^0.4.0" +buffer-equal-constant-time@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" + integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" @@ -3780,6 +3902,11 @@ cookie@0.7.1: resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz" integrity sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w== +cookie@^1.0.1: + version "1.1.1" + resolved "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz#3bb9bdfc82369db9c2f69c93c9c3ceb310c88b3c" + integrity sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ== + cookie@~0.7.1: version "0.7.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" @@ -4329,6 +4456,13 @@ eastasianwidth@^0.2.0: resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== +ecdsa-sig-formatter@1.0.11: + version "1.0.11" + resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" + integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== + dependencies: + safe-buffer "^5.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" @@ -4993,6 +5127,11 @@ express@^4.18.2: utils-merge "1.0.1" vary "~1.1.2" +fast-decode-uri-component@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543" + integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg== + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" @@ -5019,12 +5158,31 @@ fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== +fast-json-stringify@^6.0.0: + version "6.3.0" + resolved "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-6.3.0.tgz#e59f2fbd558842d7ec085276444d15e6500c16d4" + integrity sha512-oRCntNDY/329HJPlmdNLIdogNtt6Vyjb1WuT01Soss3slIdyUp8kAcDU3saQTOquEK8KFVfwIIF7FebxUAu+yA== + dependencies: + "@fastify/merge-json-schemas" "^0.2.0" + ajv "^8.12.0" + ajv-formats "^3.0.1" + fast-uri "^3.0.0" + json-schema-ref-resolver "^3.0.0" + rfdc "^1.2.0" + fast-levenshtein@^2.0.6: version "2.0.6" resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== -fast-uri@^3.0.1: +fast-querystring@^1.0.0, fast-querystring@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53" + integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg== + dependencies: + fast-decode-uri-component "^1.0.1" + +fast-uri@^3.0.0, fast-uri@^3.0.1: version "3.1.0" resolved "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz" integrity sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA== @@ -5034,6 +5192,39 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== +fastify-plugin@^5.0.0: + version "5.1.0" + resolved "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz#7083e039d6418415f9a669f8c25e72fc5bf2d3e7" + integrity sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw== + +fastify@^5.8.3: + version "5.8.5" + resolved "https://registry.npmjs.org/fastify/-/fastify-5.8.5.tgz#c452224295e0ca550bcd0efc3f7d3e90e9c11955" + integrity sha512-Yqptv59pQzPgQUSIm87hMqHJmdkb1+GPxdE6vW6FRyVE9G86mt7rOghitiU4JHRaTyDUk9pfeKmDeu70lAwM4Q== + dependencies: + "@fastify/ajv-compiler" "^4.0.5" + "@fastify/error" "^4.0.0" + "@fastify/fast-json-stringify-compiler" "^5.0.0" + "@fastify/proxy-addr" "^5.0.0" + abstract-logging "^2.0.1" + avvio "^9.0.0" + fast-json-stringify "^6.0.0" + find-my-way "^9.0.0" + light-my-request "^6.0.0" + pino "^9.14.0 || ^10.1.0" + process-warning "^5.0.0" + rfdc "^1.3.1" + secure-json-parse "^4.0.0" + semver "^7.6.0" + toad-cache "^3.7.0" + +fastq@^1.17.1: + version "1.20.1" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz#ca750a10dc925bc8b18839fd203e3ef4b3ced675" + integrity sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw== + dependencies: + reusify "^1.0.4" + fastq@^1.6.0: version "1.19.1" resolved "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz" @@ -5103,6 +5294,15 @@ finalhandler@~1.3.1: statuses "~2.0.2" unpipe "~1.0.0" +find-my-way@^9.0.0: + version "9.5.0" + resolved "https://registry.npmjs.org/find-my-way/-/find-my-way-9.5.0.tgz#3e6819bf4310b5293f490c032e70be0b506d0dc8" + integrity sha512-VW2RfnmscZO5KgBY5XVyKREMW5nMZcxDy+buTOsL+zIPnBlbKm+00sgzoQzq1EVh4aALZLfKdwv6atBGcjvjrQ== + dependencies: + fast-deep-equal "^3.1.3" + fast-querystring "^1.0.0" + safe-regex2 "^5.0.0" + find-root@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz" @@ -5201,6 +5401,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.3.4" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz#ab6934eca8bcf6f7f6b82742e33591f86301d6fc" + integrity sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-monkey@^1.0.4: version "1.1.0" resolved "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz#632aa15a20e71828ed56b24303363fb1414e5997" @@ -5758,7 +5967,7 @@ ipaddr.js@1.9.1: resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== -ipaddr.js@^2.0.1: +ipaddr.js@^2.0.1, ipaddr.js@^2.1.0: version "2.3.0" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz#71dce70e1398122208996d1c22f2ba46a24b1abc" integrity sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg== @@ -6605,6 +6814,13 @@ json-parse-even-better-errors@^2.3.0, json-parse-even-better-errors@^2.3.1: resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== +json-schema-ref-resolver@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz#28f6a410122cde9238762a5e9296faa38be28708" + integrity sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A== + dependencies: + dequal "^2.0.3" + json-schema-traverse@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" @@ -6657,6 +6873,22 @@ jsonify@^0.0.1: resolved "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978" integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg== +jsonwebtoken@^9.0.3: + version "9.0.3" + resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz#6cd57ab01e9b0ac07cb847d53d3c9b6ee31f7ae2" + integrity sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g== + dependencies: + jws "^4.0.1" + lodash.includes "^4.3.0" + lodash.isboolean "^3.0.3" + lodash.isinteger "^4.0.4" + lodash.isnumber "^3.0.3" + lodash.isplainobject "^4.0.6" + lodash.isstring "^4.0.1" + lodash.once "^4.0.0" + ms "^2.1.1" + semver "^7.5.4" + "jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.5: version "3.3.5" resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz" @@ -6667,6 +6899,23 @@ jsonify@^0.0.1: object.assign "^4.1.4" object.values "^1.1.6" +jwa@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz#bf8176d1ad0cd72e0f3f58338595a13e110bc804" + integrity sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg== + dependencies: + buffer-equal-constant-time "^1.0.1" + ecdsa-sig-formatter "1.0.11" + safe-buffer "^5.0.1" + +jws@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz#07edc1be8fac20e677b283ece261498bd38f0690" + integrity sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA== + dependencies: + jwa "^2.0.1" + safe-buffer "^5.0.1" + keyv@^4.5.3: version "4.5.4" resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz" @@ -6724,6 +6973,15 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +light-my-request@^6.0.0: + version "6.6.0" + resolved "https://registry.npmjs.org/light-my-request/-/light-my-request-6.6.0.tgz#c9448772323f65f33720fb5979c7841f14060add" + integrity sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A== + dependencies: + cookie "^1.0.1" + process-warning "^4.0.0" + set-cookie-parser "^2.6.0" + lilconfig@^3.1.1, lilconfig@^3.1.3: version "3.1.3" resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz" @@ -6865,6 +7123,11 @@ lodash.has@^4.5.2: resolved "https://registry.npmjs.org/lodash.has/-/lodash.has-4.5.2.tgz#d19f4dc1095058cccbe2b0cdf4ee0fe4aa37c862" integrity sha512-rnYUdIo6xRCJnQmbVFEwcxF144erlD+M3YcJUVesflU9paQaE8p+fJDcIQrlMYbxoANFL+AB9hZrzSBBk5PL+g== +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + lodash.isarguments@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz" @@ -6875,6 +7138,31 @@ lodash.isarray@^3.0.0: resolved "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz" integrity sha512-JwObCrNJuT0Nnbuecmqr5DgtuBppuCvGD9lxjFpAzwnVtdGoDQ1zig+5W8k5/6Gcn0gZ3936HDAlGd28i7sOGQ== +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.keys@^3.0.0: version "3.1.2" resolved "https://registry.npmjs.org/lodash.keys/-/lodash.keys-3.1.2.tgz" @@ -6894,6 +7182,11 @@ lodash.merge@^4.6.0, lodash.merge@^4.6.2: resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz" @@ -7190,9 +7483,9 @@ negotiator@~0.6.4: resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz#777948e2452651c570b712dd01c23e262713fff7" integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== -neo-async@^2.6.2: +neo-async@^2.6.1, neo-async@^2.6.2: version "2.6.2" - resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" + resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== no-case@^3.0.4: @@ -7343,6 +7636,11 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== +on-exit-leak-free@^2.1.0: + version "2.1.2" + resolved "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz#fed195c9ebddb7d9e4c3842f93f281ac8dadd3b8" + integrity sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA== + on-finished@2.4.1, on-finished@~2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" @@ -7610,6 +7908,59 @@ pify@^4.0.1: resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== +pino-abstract-transport@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz#de241578406ac7b8a33ce0d77ae6e8a0b3b68a60" + integrity sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw== + dependencies: + split2 "^4.0.0" + +pino-abstract-transport@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-3.0.0.tgz#b21e5f33a297e8c4c915c62b3ce5dd4a87a52c23" + integrity sha512-wlfUczU+n7Hy/Ha5j9a/gZNy7We5+cXp8YL+X+PG8S0KXxw7n/JXA3c46Y0zQznIJ83URJiwy7Lh56WLokNuxg== + dependencies: + split2 "^4.0.0" + +pino-std-serializers@^7.0.0: + version "7.1.0" + resolved "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz#a7b0cd65225f29e92540e7853bd73b07479893fc" + integrity sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw== + +pino@^9.0.0: + version "9.14.0" + resolved "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz#673d9711c2d1e64d18670c1ec05ef7ba14562556" + integrity sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w== + dependencies: + "@pinojs/redact" "^0.4.0" + atomic-sleep "^1.0.0" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^2.0.0" + pino-std-serializers "^7.0.0" + process-warning "^5.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^3.0.0" + +"pino@^9.14.0 || ^10.1.0": + version "10.3.1" + resolved "https://registry.npmjs.org/pino/-/pino-10.3.1.tgz#6552c8f8d8481844c9e452e7bf0be90bff1939ce" + integrity sha512-r34yH/GlQpKZbU1BvFFqOjhISRo1MNx1tWYsYvmj6KIRHSPMT2+yHOEb1SG6NMvRoHRF0a07kCOox/9yakl1vg== + dependencies: + "@pinojs/redact" "^0.4.0" + atomic-sleep "^1.0.0" + on-exit-leak-free "^2.1.0" + pino-abstract-transport "^3.0.0" + pino-std-serializers "^7.0.0" + process-warning "^5.0.0" + quick-format-unescaped "^4.0.3" + real-require "^0.2.0" + safe-stable-stringify "^2.3.1" + sonic-boom "^4.0.1" + thread-stream "^4.0.0" + pirates@^4.0.1, pirates@^4.0.4: version "4.0.7" resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz" @@ -8271,6 +8622,16 @@ process-nextick-args@~2.0.0: resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== +process-warning@^4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-4.0.1.tgz#5c1db66007c67c756e4e09eb170cdece15da32fb" + integrity sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q== + +process-warning@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz#566e0bf79d1dff30a72d8bbbe9e8ecefe8d378d7" + integrity sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA== + process@^0.11.10: version "0.11.10" resolved "https://registry.npmjs.org/process/-/process-0.11.10.tgz" @@ -8330,6 +8691,11 @@ queue-microtask@^1.2.2: resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +quick-format-unescaped@^4.0.3: + version "4.0.4" + resolved "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz#93ef6dd8d3453cbc7970dd614fad4c5954d6b5a7" + integrity sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg== + quick-lru@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz" @@ -8372,12 +8738,12 @@ react-deep-force-update@^1.0.0: resolved "https://registry.npmjs.org/react-deep-force-update/-/react-deep-force-update-1.1.2.tgz" integrity sha512-WUSQJ4P/wWcusaH+zZmbECOk7H5N2pOIl0vzheeornkIMhu+qrNdGFm0bDZLCb0hSF0jf/kH1SgkNGfBdTc4wA== -react-dom@^19.0.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz" - integrity sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ== +react-dom@~19.0.4: + version "19.0.5" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.5.tgz#7666ca4385dd1f1d2ac2445423077b2f232aa3c0" + integrity sha512-yqJj7o8tlj5FiLpycpClCCTp1f1FXvMgCkFej41N1iTmVDiTeDIay6Y69sn8w9JXSCzZyCLP3fotgEhZagDZWw== dependencies: - scheduler "^0.27.0" + scheduler "^0.25.0" react-intl@^6.4.4: version "6.8.9" @@ -8410,6 +8776,19 @@ react-is@^18.0.0, react-is@^18.3.1: resolved "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz" integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg== +react-on-rails-pro-node-renderer@16.6.0: + version "16.6.0" + resolved "https://registry.npmjs.org/react-on-rails-pro-node-renderer/-/react-on-rails-pro-node-renderer-16.6.0.tgz#c13ca0f156566531d7c6e005759459b0f19472a8" + integrity sha512-fBZ0lKRaEe8LyVTdUsXx364zQfL6hGJuE+2qQsKo+bXm0aTVq2RtO49gzq0m7Y4xuhBTVmnPQUP0O1v1cGRzLg== + dependencies: + "@fastify/formbody" "^7.4.0 || ^8.0.2" + "@fastify/multipart" "^8.3.1 || ^9.0.3" + fastify "^5.8.3" + fs-extra "^11.2.0" + jsonwebtoken "^9.0.3" + lockfile "^1.0.4" + pino "^9.0.0" + react-on-rails-pro@16.6.0: version "16.6.0" resolved "https://registry.npmjs.org/react-on-rails-pro/-/react-on-rails-pro-16.6.0.tgz#19a5ea99d7b397dd56f14cff1f31955211b4d0a2" @@ -8417,6 +8796,15 @@ react-on-rails-pro@16.6.0: dependencies: react-on-rails "16.6.0" +react-on-rails-rsc@19.0.4: + version "19.0.4" + resolved "https://registry.npmjs.org/react-on-rails-rsc/-/react-on-rails-rsc-19.0.4.tgz#a605fbaa82a0bece504de1ef0b8d5c6fae5d6be3" + integrity sha512-KtHYz0opcXJk+Zw5aabjNiaszzpTdFgs1YfSLIZJSzU5SpddUw7b08epkxeJq/TCpR60Q9vsjxX3Q3OWJ97tMg== + dependencies: + acorn-loose "^8.3.0" + neo-async "^2.6.1" + webpack-sources "^3.2.0" + react-on-rails@16.6.0: version "16.6.0" resolved "https://registry.npmjs.org/react-on-rails/-/react-on-rails-16.6.0.tgz#da7f117fec14f420f7f6ffe6bdb34b7fc2e01b3a" @@ -8485,10 +8873,10 @@ react-transition-group@4.4.5: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^19.0.0: - version "19.2.0" - resolved "https://registry.npmjs.org/react/-/react-19.2.0.tgz" - integrity sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ== +react@~19.0.4: + version "19.0.5" + resolved "https://registry.npmjs.org/react/-/react-19.0.5.tgz#b9406da29c7085e446e4c2372dcfe4f7c4801aec" + integrity sha512-yIoQWl4moQfHFKNGmyJavhOki09GwCRcMFuXv3y3KMXoQrGnDi0ZHGe4H9EtQE+jrMWU4hgxaILMS4rxTkJdGw== read-cache@^1.0.0: version "1.0.0" @@ -8531,6 +8919,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +real-require@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78" + integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg== + rechoir@^0.8.0: version "0.8.0" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" @@ -8741,6 +9134,11 @@ resolve@^2.0.0-next.5: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +ret@~0.5.0: + version "0.5.0" + resolved "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz#30a4d38a7e704bd96dc5ffcbe7ce2a9274c41c95" + integrity sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw== + retry@^0.13.1: version "0.13.1" resolved "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -8764,6 +9162,11 @@ rework@^1.0.1: convert-source-map "^0.3.3" css "^2.0.0" +rfdc@^1.2.0, rfdc@^1.3.1: + version "1.4.1" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz#778f76c4fb731d93414e8f925fbecf64cce7f6ca" + integrity sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA== + rimraf@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" @@ -8808,7 +9211,7 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -8840,6 +9243,18 @@ safe-regex-test@^1.0.3, safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" +safe-regex2@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz#a5f3a6e35b8d84d0f41fa22efd5b6d30b367bbc7" + integrity sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA== + dependencies: + ret "~0.5.0" + +safe-stable-stringify@^2.3.1: + version "2.5.0" + resolved "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz#4ca2f8e385f2831c432a719b108a3bf7af42a1dd" + integrity sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA== + "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" @@ -8892,10 +9307,10 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.27.0: - version "0.27.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" - integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== +scheduler@^0.25.0: + version "0.25.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz#336cd9768e8cceebf52d3c80e3dcf5de23e7e015" + integrity sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA== schema-utils@^3.0.0, schema-utils@^3.3.0: version "3.3.0" @@ -8926,6 +9341,11 @@ schema-utils@^4.3.3: ajv-formats "^2.1.1" ajv-keywords "^5.1.0" +secure-json-parse@^4.0.0: + version "4.1.0" + resolved "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.0.tgz#4f1ab41c67a13497ea1b9131bb4183a22865477c" + integrity sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA== + select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -8954,6 +9374,11 @@ semver@^7.3.5, semver@^7.3.7, semver@^7.3.8, semver@^7.5.3, semver@^7.5.4: resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz" integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== +semver@^7.6.0: + version "7.7.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz#28464e36060e991fa7a11d0279d2d3f3b57a7e8a" + integrity sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA== + send@0.19.0: version "0.19.0" resolved "https://registry.npmjs.org/send/-/send-0.19.0.tgz" @@ -9037,6 +9462,11 @@ set-blocking@^2.0.0: resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-cookie-parser@^2.6.0: + version "2.7.2" + resolved "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz#ccd08673a9ae5d2e44ea2a2de25089e67c7edf68" + integrity sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw== + set-function-length@^1.2.2: version "1.2.2" resolved "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz" @@ -9182,6 +9612,13 @@ sockjs@^0.3.24: uuid "^8.3.2" websocket-driver "^0.7.4" +sonic-boom@^4.0.1: + version "4.2.1" + resolved "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz#28598250df4899c0ac572d7e2f0460690ba6a030" + integrity sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q== + dependencies: + atomic-sleep "^1.0.0" + "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.0.1, source-map-js@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz" @@ -9252,6 +9689,11 @@ spdy@^4.0.2: select-hose "^2.0.0" spdy-transport "^3.0.0" +split2@^4.0.0: + version "4.2.0" + resolved "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz#c9c5920904d148bab0b9f67145f245a86aadbfa4" + integrity sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" @@ -9670,6 +10112,20 @@ thenify-all@^1.0.0: dependencies: any-promise "^1.0.0" +thread-stream@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz#4b2ef252a7c215064507d4ef70c05a5e2d34c4f1" + integrity sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A== + dependencies: + real-require "^0.2.0" + +thread-stream@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/thread-stream/-/thread-stream-4.0.0.tgz#732f007c24da7084f729d6e3a7e3f5934a7380b7" + integrity sha512-4iMVL6HAINXWf1ZKZjIPcz5wYaOdPhtO8ATvZ+Xqp3BTdaqtAwQkNmKORqcIo5YkQqGXq5cwfswDwMqqQNrpJA== + dependencies: + real-require "^0.2.0" + thunky@^1.0.2: version "1.1.0" resolved "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" @@ -9704,6 +10160,11 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" +toad-cache@^3.7.0: + version "3.7.0" + resolved "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz#b9b63304ea7c45ec34d91f1d2fa513517025c441" + integrity sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw== + toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" @@ -10116,7 +10577,7 @@ webpack-merge@5, webpack-merge@^5.7.3, webpack-merge@^5.8.0: flat "^5.0.2" wildcard "^2.0.0" -webpack-sources@^3.3.4: +webpack-sources@^3.2.0, webpack-sources@^3.3.4: version "3.3.4" resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.4.tgz#a338b95eb484ecc75fbb196cbe8a2890618b4891" integrity sha512-7tP1PdV4vF+lYPnkMR0jMY5/la2ub5Fc/8VQrrU+lXkiM6C4TjVfGw7iKfyhnTQOsD+6Q/iKw0eFciziRgD58Q==