diff --git a/.github/workflows/rspec_test.yml b/.github/workflows/rspec_test.yml index d117458a6..5ebcb89da 100644 --- a/.github/workflows/rspec_test.yml +++ b/.github/workflows/rspec_test.yml @@ -33,6 +33,7 @@ jobs: DRIVER: selenium_chrome CHROME_BIN: /usr/bin/google-chrome USE_COVERALLS: true + RENDERER_PASSWORD: devPassword steps: - name: Install Chrome @@ -82,6 +83,20 @@ jobs: - name: Build shakapacker chunks run: NODE_ENV=development bundle exec bin/shakapacker + - name: Start Node renderer for SSR + run: | + node react-on-rails-pro-node-renderer.js & + echo "Waiting for Node renderer on port 3800..." + for i in $(seq 1 30); do + 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" + exit 1 + - name: Run rspec with xvfb uses: coactions/setup-xvfb@v1 with: diff --git a/.gitignore b/.gitignore index 366e6a9b7..5c7b739bb 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,9 @@ client/app/bundles/comments/rescript/**/*.bs.js # Using React on Rails default directory /ssr-generated/ +# Node renderer bundle cache +.node-renderer-bundles/ + # Generated React on Rails packs **/generated/** diff --git a/Gemfile b/Gemfile index e9eb85053..7acd9bfc0 100644 --- a/Gemfile +++ b/Gemfile @@ -5,7 +5,7 @@ git_source(:github) { |repo| "https://github.com/#{repo}.git" } ruby "3.4.6" -gem "react_on_rails", "16.6.0.rc.0" +gem "react_on_rails_pro", "16.5.1" gem "shakapacker", "10.0.0.rc.0" # Bundle edge Rails instead: gem "rails", github: "rails/rails" diff --git a/Gemfile.lock b/Gemfile.lock index b290c3986..3e0ee2285 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,6 +78,12 @@ GEM addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.3) + async (2.38.1) + console (~> 1.29) + fiber-annotation + io-event (~> 1.11) + metrics (~> 0.12) + traces (~> 0.18) autoprefixer-rails (10.4.16.0) execjs (~> 2) awesome_print (1.9.2) @@ -115,6 +121,10 @@ GEM coffee-script-source (1.12.2) concurrent-ruby (1.3.6) connection_pool (3.0.2) + console (1.34.3) + fiber-annotation + fiber-local (~> 1.1) + json coveralls_reborn (0.25.0) simplecov (>= 0.18.1, < 0.22.0) term-ansicolor (~> 1.6) @@ -146,16 +156,24 @@ GEM railties (>= 5.0.0) ffi (1.17.2-arm64-darwin) ffi (1.17.2-x86_64-linux-gnu) + fiber-annotation (0.2.0) + fiber-local (1.1.0) + fiber-storage + fiber-storage (1.0.1) foreman (0.88.1) generator_spec (0.10.0) activesupport (>= 3.0.0) railties (>= 3.0.0) globalid (1.3.0) activesupport (>= 6.1) + http-2 (1.1.3) + httpx (1.7.5) + http-2 (>= 1.1.3) i18n (1.14.8) concurrent-ruby (~> 1.0) interception (0.5) io-console (0.8.2) + io-event (1.14.5) irb (1.17.0) pp (>= 0.6.0) prism (>= 1.3.0) @@ -165,6 +183,8 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) json (2.19.1) + jwt (2.10.2) + base64 language_server-protocol (3.17.0.5) launchy (3.0.1) addressable (~> 2.8) @@ -182,6 +202,7 @@ GEM marcel (1.1.0) matrix (0.4.2) method_source (1.1.0) + metrics (0.15.0) mini_mime (1.1.5) minitest (6.0.2) drb (~> 2.0) @@ -296,13 +317,23 @@ GEM erb psych (>= 4.0.0) tsort - react_on_rails (16.6.0.rc.0) + react_on_rails (16.5.1) addressable connection_pool execjs (~> 2.5) rails (>= 5.2) rainbow (~> 3.0) shakapacker (>= 6.0) + react_on_rails_pro (16.5.1) + addressable + async (>= 2.29) + connection_pool + execjs (~> 2.9) + http-2 (>= 1.1.1) + httpx (~> 1.5) + jwt (~> 2.7) + rainbow + react_on_rails (= 16.5.1) redcarpet (3.6.0) redis (5.3.0) redis-client (>= 0.22.0) @@ -425,6 +456,7 @@ GEM tins (1.33.0) bigdecimal sync + traces (0.18.2) tsort (0.2.0) turbo-rails (2.0.11) actionpack (>= 6.0.0) @@ -486,7 +518,7 @@ DEPENDENCIES rails-html-sanitizer rails_best_practices rainbow - react_on_rails (= 16.6.0.rc.0) + react_on_rails_pro (= 16.5.1) redcarpet redis (~> 5.0) rspec-rails (~> 6.0.0) diff --git a/Procfile.dev b/Procfile.dev index 102c0a8df..d37228687 100644 --- a/Procfile.dev +++ b/Procfile.dev @@ -12,3 +12,7 @@ rails: bundle exec thrust bin/rails server -p 3000 wp-client: RAILS_ENV=development NODE_ENV=development bin/shakapacker-dev-server # Server Rspack watcher for SSR bundle wp-server: SERVER_BUNDLE_ONLY=yes bin/shakapacker --watch +# RSC Rspack watcher for React Server Components bundle +wp-rsc: RSC_BUNDLE_ONLY=true bin/shakapacker --watch +# React on Rails Pro Node renderer for SSR and RSC payload generation +node-renderer: node react-on-rails-pro-node-renderer.js diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 507cc6cf7..f435f04eb 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -38,6 +38,8 @@ def simple; end def rescript; end + def server_components; end + private def set_comments diff --git a/app/views/pages/server_components.html.erb b/app/views/pages/server_components.html.erb new file mode 100644 index 000000000..d9643a213 --- /dev/null +++ b/app/views/pages/server_components.html.erb @@ -0,0 +1,6 @@ +<%= append_javascript_pack_tag('rsc-client-components') %> +<%= react_component("ServerComponentsPage", + prerender: false, + auto_load_bundle: false, + trace: Rails.env.development?, + id: "ServerComponentsPage-react-component-0") %> diff --git a/client/app/bundles/comments/components/Footer/ror_components/Footer.jsx b/client/app/bundles/comments/components/Footer/ror_components/Footer.jsx index 5e7f42104..d153dfb22 100644 --- a/client/app/bundles/comments/components/Footer/ror_components/Footer.jsx +++ b/client/app/bundles/comments/components/Footer/ror_components/Footer.jsx @@ -1,3 +1,5 @@ +'use client'; + import React from 'react'; import PropTypes from 'prop-types'; import BaseComponent from 'libs/components/BaseComponent'; diff --git a/client/app/bundles/comments/components/NavigationBar/NavigationBar.jsx b/client/app/bundles/comments/components/NavigationBar/NavigationBar.jsx index db2b4e53c..30b99f371 100644 --- a/client/app/bundles/comments/components/NavigationBar/NavigationBar.jsx +++ b/client/app/bundles/comments/components/NavigationBar/NavigationBar.jsx @@ -102,6 +102,14 @@ function NavigationBar(props) { Rescript +
  • + + RSC Demo + +
  • { + return ( +
    +
    +

    + React Server Components Demo +

    +

    + This page is rendered using React Server Components with React on Rails Pro. + Server components run on the server and stream their output to the client, keeping + heavy dependencies out of the browser bundle entirely. +

    +
    + +
    + {/* Server Info - uses Node.js os module (impossible on client) */} +
    +

    + Server Environment + + Server Only + +

    + +
    + + {/* Interactive toggle - demonstrates mixing server + client components */} +
    +

    + Interactive Client Component + + Client Hydrated + +

    + +
    +

    + This toggle is a 'use client' component, meaning it ships JavaScript + to the browser for interactivity. But the content inside is rendered on the server + and passed as children — a key RSC pattern called the donut pattern. +

    +
      +
    • The TogglePanel wrapper runs on the client (handles click events)
    • +
    • The children content is rendered on the server (no JS cost)
    • +
    • Heavy libraries used by server components never reach the browser
    • +
    +
    +
    +
    + + {/* Async data fetching with Suspense streaming */} +
    +

    + Streamed Comments + + Async + Suspense + +

    +

    + Comments are fetched directly on the server using the Rails API. + The page shell renders immediately while this section streams in progressively. +

    + + {[1, 2, 3].map((i) => ( +
    +
    +
    +
    + ))} +
    + } + > + + +
    + + {/* Architecture explanation */} +
    +

    + What makes this different? +

    +
    +
    +

    Smaller Client Bundle

    +

    + Libraries like lodash, marked, and Node.js os module + are used on this page but never downloaded by the browser. +

    +
    +
    +

    Direct Data Access

    +

    + Server components fetch data by calling your Rails API internally — no + client-side fetch waterfalls or loading spinners for initial data. +

    +
    +
    +

    Progressive Streaming

    +

    + The page shell renders instantly. Async components (like the comments feed) + stream in as their data resolves, with Suspense boundaries showing fallbacks. +

    +
    +
    +

    Selective Hydration

    +

    + Only client components (like the toggle above) receive JavaScript. + Everything else is pure HTML — zero hydration cost. +

    +
    +
    +
    +
    +
    + ); +}; + +export default ServerComponentsPage; diff --git a/client/app/bundles/server-components/components/CommentsFeed.jsx b/client/app/bundles/server-components/components/CommentsFeed.jsx new file mode 100644 index 000000000..1260ffdf8 --- /dev/null +++ b/client/app/bundles/server-components/components/CommentsFeed.jsx @@ -0,0 +1,125 @@ +// Server Component - fetches comments directly from the Rails API on the server. +// Uses marked for markdown rendering. Both fetch and marked stay server-side. + +import React from 'react'; +import { Marked } from 'marked'; +import { gfmHeadingId } from 'marked-gfm-heading-id'; +import sanitizeHtml from 'sanitize-html'; +import _ from 'lodash'; +import TogglePanel from './TogglePanel'; + +const marked = new Marked(); +marked.use(gfmHeadingId()); + +function resolveRailsBaseUrl() { + if (process.env.RAILS_INTERNAL_URL) { + return process.env.RAILS_INTERNAL_URL; + } + + // Local defaults are okay in development/test, but production should be explicit. + if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'test') { + return 'http://localhost:3000'; + } + + throw new Error('RAILS_INTERNAL_URL must be set outside development/test'); +} + +async function CommentsFeed() { + // Simulate network latency only when explicitly enabled for demos. + if (process.env.RSC_SUSPENSE_DEMO_DELAY === 'true') { + await new Promise((resolve) => { + setTimeout(resolve, 800); + }); + } + + let recentComments = []; + try { + // Fetch comments directly from the Rails API — no client-side fetch needed + const baseUrl = resolveRailsBaseUrl(); + const response = await fetch(`${baseUrl}/comments.json`); + if (!response.ok) { + throw new Error(`Failed to fetch comments: ${response.status} ${response.statusText}`); + } + const comments = await response.json(); + + // Use lodash to process (stays on server) + const sortedComments = _.orderBy(comments, ['created_at'], ['desc']); + recentComments = _.take(sortedComments, 10); + } catch (error) { + // eslint-disable-next-line no-console + console.error('CommentsFeed failed to load comments', error); + return ( +
    +

    Could not load comments right now. Please try again later.

    +
    + ); + } + + if (recentComments.length === 0) { + return ( +
    +

    + No comments yet. Add some comments from the{' '} + + home page + {' '} + to see them rendered here by server components. +

    +
    + ); + } + + return ( +
    + {recentComments.map((comment) => { + // Render markdown on the server using marked + sanitize-html. + // sanitize-html strips any dangerous HTML before rendering. + // These libraries (combined ~200KB) never reach the client. + const rawHtml = marked.parse(comment.text || ''); + const safeHtml = sanitizeHtml(rawHtml, { + allowedTags: sanitizeHtml.defaults.allowedTags.concat(['img']), + allowedAttributes: { + ...sanitizeHtml.defaults.allowedAttributes, + img: ['src', 'alt', 'title', 'width', 'height'], + }, + allowedSchemes: ['https', 'http'], + }); + + return ( +
    +
    + {comment.author} + + {new Date(comment.created_at).toLocaleDateString('en-US', { + month: 'short', + day: 'numeric', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + })} + +
    + + {/* Content is sanitized via sanitize-html before rendering */} + {/* eslint-disable-next-line react/no-danger */} +
    + +

    {comment.text}

    +
    + ); + })} +

    + {recentComments.length} comment{recentComments.length !== 1 ? 's' : ''} rendered on the server using{' '} + marked + sanitize-html (never sent to browser) +

    +
    + ); +} + +export default CommentsFeed; diff --git a/client/app/bundles/server-components/components/ServerInfo.jsx b/client/app/bundles/server-components/components/ServerInfo.jsx new file mode 100644 index 000000000..4475eb826 --- /dev/null +++ b/client/app/bundles/server-components/components/ServerInfo.jsx @@ -0,0 +1,58 @@ +// Server Component - uses Node.js os module, which only exists on the server. +// This component and its dependencies are never sent to the browser. + +import React from 'react'; +import os from 'os'; +import _ from 'lodash'; + +async function ServerInfo() { + const serverInfo = { + platform: os.platform(), + arch: os.arch(), + nodeVersion: process.version, + uptime: Math.floor(os.uptime() / 3600), + totalMemory: (os.totalmem() / (1024 * 1024 * 1024)).toFixed(1), + freeMemory: (os.freemem() / (1024 * 1024 * 1024)).toFixed(1), + cpus: os.cpus().length, + hostname: os.hostname(), + }; + + // Using lodash on the server — this 70KB+ library stays server-side + const infoEntries = _.toPairs(serverInfo); + const grouped = _.chunk(infoEntries, 4); + + const labels = { + platform: 'Platform', + arch: 'Architecture', + nodeVersion: 'Node.js', + uptime: 'Uptime (hrs)', + totalMemory: 'Total RAM (GB)', + freeMemory: 'Free RAM (GB)', + cpus: 'CPU Cores', + hostname: 'Hostname', + }; + + return ( +
    +

    + This data comes from the Node.js os module + — it runs only on the server. The lodash library + used to format it never reaches the browser. +

    +
    + {grouped.map((group) => ( +
    k).join('-')} className="space-y-1"> + {group.map(([key, value]) => ( +
    + {labels[key] || key} + {value} +
    + ))} +
    + ))} +
    +
    + ); +} + +export default ServerInfo; diff --git a/client/app/bundles/server-components/components/TogglePanel.jsx b/client/app/bundles/server-components/components/TogglePanel.jsx new file mode 100644 index 000000000..1336b56b3 --- /dev/null +++ b/client/app/bundles/server-components/components/TogglePanel.jsx @@ -0,0 +1,40 @@ +'use client'; + +import React, { useState } from 'react'; +import PropTypes from 'prop-types'; + +const TogglePanel = ({ title, children }) => { + const [isOpen, setIsOpen] = useState(false); + + return ( +
    + + {isOpen && ( +
    + {children} +
    + )} +
    + ); +}; + +TogglePanel.propTypes = { + title: PropTypes.string.isRequired, + children: PropTypes.node.isRequired, +}; + +export default TogglePanel; diff --git a/client/app/libs/requestsManager.js b/client/app/libs/requestsManager.js index 6b5fad453..c9209c7b4 100644 --- a/client/app/libs/requestsManager.js +++ b/client/app/libs/requestsManager.js @@ -1,5 +1,5 @@ import request from 'axios'; -import ReactOnRails from 'react-on-rails'; +import ReactOnRails from 'react-on-rails-pro'; const API_URL = 'comments.json'; diff --git a/client/app/packs/rsc-bundle.js b/client/app/packs/rsc-bundle.js new file mode 100644 index 000000000..b6fcae1bb --- /dev/null +++ b/client/app/packs/rsc-bundle.js @@ -0,0 +1,19 @@ +// RSC (React Server Components) bundle entry point. +// This file is only used by the RSC bundle configuration. +// It imports the same client component registrations as server-bundle.js, +// plus the server component registrations. + +// Import stores registration (has 'use client' — RSC loader replaces with client reference) +import './stores-registration'; + +// React Server Components registration (server-side) +// Note: server-bundle-generated.js is NOT imported here because it contains +// traditional SSR components (e.g., RouterApp.server.jsx) that use client-only +// React APIs (useState, Component, etc.) incompatible with the react-server condition. +// Client component references are handled automatically by the RSC loader/plugin. +import registerServerComponent from 'react-on-rails-pro/registerServerComponent/server'; +import ServerComponentsPage from '../bundles/server-components/ServerComponentsPage'; + +registerServerComponent({ + ServerComponentsPage, +}); diff --git a/client/app/packs/rsc-client-components.js b/client/app/packs/rsc-client-components.js new file mode 100644 index 000000000..55773819c --- /dev/null +++ b/client/app/packs/rsc-client-components.js @@ -0,0 +1,11 @@ +'use client'; + +// RSC client components registration. +// Components with 'use client' that are used in server components must be +// available in a client bundle chunk so the React flight client can load them. + +import ReactOnRails from 'react-on-rails-pro'; +import TogglePanel from '../bundles/server-components/components/TogglePanel'; + +// Register as a standard component so it's bundled in a client-accessible chunk +ReactOnRails.register({ TogglePanel }); diff --git a/client/app/packs/stimulus-bundle.js b/client/app/packs/stimulus-bundle.js index a11ccf149..920396bab 100644 --- a/client/app/packs/stimulus-bundle.js +++ b/client/app/packs/stimulus-bundle.js @@ -1,4 +1,7 @@ -import ReactOnRails from 'react-on-rails'; +'use client'; + +import ReactOnRails from 'react-on-rails-pro'; +import registerServerComponent from 'react-on-rails-pro/registerServerComponent/client'; import 'jquery-ujs'; import { Turbo } from '@hotwired/turbo-rails'; @@ -17,3 +20,9 @@ ReactOnRails.setOptions({ // Components are now auto-registered via ror_components directories // No need for manual registration + +// React Server Components registration (client-side) +registerServerComponent( + { rscPayloadGenerationUrlPath: 'rsc_payload/' }, + 'ServerComponentsPage', +); diff --git a/client/app/packs/stores-registration.js b/client/app/packs/stores-registration.js index 435653379..cecf0a958 100644 --- a/client/app/packs/stores-registration.js +++ b/client/app/packs/stores-registration.js @@ -1,4 +1,6 @@ -import ReactOnRails from 'react-on-rails'; +'use client'; + +import ReactOnRails from 'react-on-rails-pro'; import routerCommentsStore from '../bundles/comments/store/routerCommentsStore'; import commentsStore from '../bundles/comments/store/commentsStore'; diff --git a/config/initializers/react_on_rails_pro.rb b/config/initializers/react_on_rails_pro.rb new file mode 100644 index 000000000..c31b87ab1 --- /dev/null +++ b/config/initializers/react_on_rails_pro.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +ReactOnRailsPro.configure do |config| + # Node renderer for server-side rendering and RSC payload generation + use_node_renderer = Rails.env.development? || ENV["REACT_USE_NODE_RENDERER"] == "true" + + if use_node_renderer + renderer_host = ENV.fetch("RENDERER_HOST", "localhost") + renderer_port = ENV.fetch("RENDERER_PORT", "3800") + + config.server_renderer = "NodeRenderer" + config.renderer_url = ENV.fetch("REACT_RENDERER_URL", "http://#{renderer_host}:#{renderer_port}") + config.renderer_password = if Rails.env.local? + ENV.fetch("RENDERER_PASSWORD", "local-dev-renderer-password") + else + ENV.fetch("RENDERER_PASSWORD") + end + end + + # Enable React Server Components support + config.enable_rsc_support = true + + # RSC bundle file name (built by rscWebpackConfig.js) + config.rsc_bundle_js_file = "rsc-bundle.js" +end diff --git a/config/routes.rb b/config/routes.rb index 1d8c7b7a5..50a6b1e7e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,6 +3,9 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html + # React Server Components payload route + rsc_payload_route + # Serve websocket cable requests in-process # mount ActionCable.server => '/cable' @@ -11,6 +14,7 @@ get "simple", to: "pages#simple" get "rescript", to: "pages#rescript" get "no-router", to: "pages#no_router" + get "server-components", to: "pages#server_components" # React Router needs a wildcard get "react-router(/*all)", to: "pages#index" diff --git a/config/webpack/clientWebpackConfig.js b/config/webpack/clientWebpackConfig.js index 6352208fb..84cd35230 100644 --- a/config/webpack/clientWebpackConfig.js +++ b/config/webpack/clientWebpackConfig.js @@ -3,6 +3,7 @@ const commonWebpackConfig = require('./commonWebpackConfig'); const { getBundler } = require('./bundlerUtils'); +const { RspackRscPlugin } = require('./rspackRscPlugin'); const configureClient = () => { const bundler = getBundler(); @@ -16,11 +17,28 @@ const configureClient = () => { }), ); - // server-bundle is special and should ONLY be built by the serverConfig - // In case this entry is not deleted, a very strange "window" not found + // RSC: Generate react-client-manifest.json for client component resolution + clientConfig.plugins.push(new RspackRscPlugin({ isServer: false })); + + // server-bundle and rsc-bundle should ONLY be built by their respective configs. + // In case these entries are not deleted, a very strange "window" not found // error shows referring to window["webpackJsonp"]. That is because the // client config is going to try to load chunks. delete clientConfig.entry['server-bundle']; + delete clientConfig.entry['rsc-bundle']; + + // react-on-rails-pro includes server-side code that imports Node.js modules. + // Provide empty fallbacks for the client bundle so it doesn't fail to resolve them. + clientConfig.resolve = { + ...clientConfig.resolve, + fallback: { + ...clientConfig.resolve?.fallback, + path: false, + fs: false, + 'fs/promises': false, + stream: false, + }, + }; return clientConfig; }; diff --git a/config/webpack/commonWebpackConfig.js b/config/webpack/commonWebpackConfig.js index 1a99ddbc5..6aad08238 100644 --- a/config/webpack/commonWebpackConfig.js +++ b/config/webpack/commonWebpackConfig.js @@ -8,6 +8,13 @@ const commonOptions = { resolve: { // Add .res.js extension for ReScript-compiled modules (modern ReScript convention) extensions: ['.css', '.ts', '.tsx', '.res.js'], + // Alias react-on-rails to react-on-rails-pro so third-party packages + // (like rescript-react-on-rails) that import 'react-on-rails' get the Pro version. + // This avoids "Cannot mix react-on-rails (core) with react-on-rails-pro" errors. + alias: { + 'react-on-rails$': 'react-on-rails-pro', + 'react-on-rails/node_package': 'react-on-rails-pro/node_package', + }, }, }; diff --git a/config/webpack/rscWebpackConfig.js b/config/webpack/rscWebpackConfig.js new file mode 100644 index 000000000..83ffb504a --- /dev/null +++ b/config/webpack/rscWebpackConfig.js @@ -0,0 +1,112 @@ +// RSC (React Server Components) bundle configuration. +// +// This creates a third bundle alongside client and server bundles. +// The RSC bundle runs server components in the Node renderer and produces +// the Flight payload that React uses to hydrate on the client. +// +// Unlike the server bundle (which uses ExecJS), the RSC bundle targets Node.js +// and can use Node.js built-in modules like os, fs, path, etc. + +const path = require('path'); +const { config } = require('shakapacker'); +const commonWebpackConfig = require('./commonWebpackConfig'); +const { getBundler } = require('./bundlerUtils'); +const { RspackRscPlugin } = require('./rspackRscPlugin'); + +function extractLoader(rule, loaderName) { + if (!Array.isArray(rule.use)) return null; + return rule.use.find((item) => { + const testValue = typeof item === 'string' ? item : item?.loader; + return testValue && testValue.includes(loaderName); + }); +} + +const configureRsc = () => { + const bundler = getBundler(); + const rscConfig = commonWebpackConfig(); + + // Use the dedicated rsc-bundle entry point + const rscEntry = rscConfig.entry['rsc-bundle']; + if (!rscEntry) { + throw new Error('RSC bundle entry not found. Ensure client/app/packs/rsc-bundle.js exists.'); + } + rscConfig.entry = { 'rsc-bundle': rscEntry }; + + // Remove CSS extraction plugins (same as server config — CSS handled by client) + rscConfig.plugins = rscConfig.plugins.filter( + (plugin) => + plugin.constructor.name !== 'WebpackAssetsManifest' && + plugin.constructor.name !== 'MiniCssExtractPlugin' && + plugin.constructor.name !== 'CssExtractRspackPlugin' && + plugin.constructor.name !== 'ForkTsCheckerWebpackPlugin', + ); + + // Remove CSS extraction loaders from style rules + rscConfig.module.rules.forEach((rule) => { + if (Array.isArray(rule.use)) { + rule.use = rule.use.filter((item) => { + const testValue = typeof item === 'string' ? item : item?.loader; + return !( + testValue?.match(/mini-css-extract-plugin/) || + testValue?.match(/CssExtractRspackPlugin/) || + testValue?.includes('cssExtractLoader') || + testValue === 'style-loader' + ); + }); + const cssLoader = rule.use.find((item) => { + const testValue = typeof item === 'string' ? item : item?.loader; + return testValue?.includes('css-loader'); + }); + if (cssLoader?.options?.modules) { + cssLoader.options.modules = { ...cssLoader.options.modules, exportOnlyLocals: true }; + } + } else if ( + rule.use?.loader && + (rule.use.loader.includes('url-loader') || rule.use.loader.includes('file-loader')) + ) { + rule.use.options = { ...(rule.use.options || {}), emitFile: false }; + } + }); + + // Add the RSC WebpackLoader to transpiler rules. + // This loader handles 'use client' directive detection and server/client component separation. + rscConfig.module.rules.forEach((rule) => { + if (Array.isArray(rule.use)) { + const transpilerLoader = extractLoader(rule, 'swc-loader') || extractLoader(rule, 'babel-loader'); + if (transpilerLoader) { + rule.use.push({ + loader: 'react-on-rails-rsc/WebpackLoader', + }); + } + } + }); + + // Enable react-server condition for server component resolution + rscConfig.resolve = { + ...rscConfig.resolve, + conditionNames: ['react-server', '...'], + }; + + // No code splitting for the RSC bundle + rscConfig.optimization = { minimize: false }; + rscConfig.plugins.unshift(new bundler.optimize.LimitChunkCountPlugin({ maxChunks: 1 })); + + // Output to the same SSR directory as the server bundle + rscConfig.output = { + filename: 'rsc-bundle.js', + globalObject: 'this', + path: path.resolve(__dirname, '../../ssr-generated'), + publicPath: config.publicPath, + }; + + // Target Node.js so server-only modules (os, fs, stream, etc.) resolve correctly + rscConfig.target = 'node'; + rscConfig.devtool = process.env.NODE_ENV === 'production' ? 'source-map' : 'eval'; + + // RSC manifest plugin + rscConfig.plugins.push(new RspackRscPlugin({ isServer: true })); + + return rscConfig; +}; + +module.exports = configureRsc; diff --git a/config/webpack/rspackRscPlugin.js b/config/webpack/rspackRscPlugin.js new file mode 100644 index 000000000..331cc1170 --- /dev/null +++ b/config/webpack/rspackRscPlugin.js @@ -0,0 +1,153 @@ +// Rspack-compatible RSC manifest plugin. +// +// The standard RSCWebpackPlugin from react-on-rails-rsc uses webpack internals +// (ModuleDependency, NullDependency, contextModuleFactory.resolveDependencies) +// that are not available in Rspack. This lightweight plugin generates the +// react-client-manifest.json and react-server-client-manifest.json files +// that the React flight protocol needs to resolve client component references. + +const fs = require('fs'); +const { sources } = require('@rspack/core'); + +class RspackRscPlugin { + constructor(options) { + if (!options || typeof options.isServer !== 'boolean') { + throw new Error('RspackRscPlugin: isServer option (boolean) is required.'); + } + this.isServer = options.isServer; + this.clientManifestFilename = options.isServer + ? 'react-server-client-manifest.json' + : 'react-client-manifest.json'; + this.ssrManifestFilename = 'react-ssr-manifest.json'; + this.useClientCache = new Map(); + } + + apply(compiler) { + // Clear cache on each compilation so watch-mode picks up 'use client' changes + compiler.hooks.thisCompilation.tap('RspackRscPlugin-ClearCache', () => { + this.useClientCache.clear(); + }); + + compiler.hooks.thisCompilation.tap('RspackRscPlugin', (compilation) => { + compilation.hooks.processAssets.tap( + { + name: 'RspackRscPlugin', + stage: compilation.constructor.PROCESS_ASSETS_STAGE_REPORT || 5000, + }, + () => { + const manifest = {}; + + for (const chunk of compilation.chunks) { + const chunkFiles = []; + for (const file of chunk.files) { + if (file.endsWith('.js') && !file.endsWith('.hot-update.js')) { + chunkFiles.push(file); + break; + } + } + + const modules = compilation.chunkGraph + ? compilation.chunkGraph.getChunkModulesIterable(chunk) + : []; + + for (const mod of modules) { + this._processModule(mod, chunk, chunkFiles, manifest, compilation); + // Handle concatenated modules + if (mod.modules) { + for (const innerMod of mod.modules) { + this._processModule(innerMod, chunk, chunkFiles, manifest, compilation); + } + } + } + } + + compilation.emitAsset( + this.clientManifestFilename, + new sources.RawSource(JSON.stringify(manifest, null, 2)), + ); + + // Emit SSR manifest (maps module IDs to SSR module data) + if (!this.isServer) { + compilation.emitAsset( + this.ssrManifestFilename, + new sources.RawSource(JSON.stringify({}, null, 2)), + ); + } + }, + ); + }); + } + + _hasUseClientDirective(filePath) { + if (this.useClientCache.has(filePath)) return this.useClientCache.get(filePath); + + let result = false; + let fd; + try { + // Read the first ~200 bytes — 'use client' must be at the very top of the file. + fd = fs.openSync(filePath, 'r'); + const buf = Buffer.alloc(200); + fs.readSync(fd, buf, 0, 200, 0); + + const head = buf.toString('utf-8'); + // Allow comments before the directive. + result = /^(?:\s*(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/))*\s*['"]use client['"]/.test(head); + } catch (_) { + // file doesn't exist or can't be read + } finally { + if (fd !== undefined) { + try { + fs.closeSync(fd); + } catch (_) { + // no-op + } + } + } + + this.useClientCache.set(filePath, result); + return result; + } + + _processModule(mod, chunk, chunkFiles, manifest, compilation) { + const resource = mod.resource || mod.userRequest; + if (!resource || !resource.match(/\.(js|jsx|ts|tsx)$/)) return; + // Skip node_modules + if (resource.includes('node_modules')) return; + + // Check original file for 'use client' directive + if (!this._hasUseClientDirective(resource)) return; + + const moduleId = compilation.chunkGraph ? compilation.chunkGraph.getModuleId(mod) : mod.id; + + if (moduleId == null) return; + + const chunks = []; + for (const file of chunkFiles) { + chunks.push(chunk.id, file); + } + + // Build the module entry with all exported names + const ssrEntry = { + id: moduleId, + chunks: chunks, + name: '*', + async: false, + }; + + // Use resource path as the key (React flight protocol convention) + const key = resource; + if (!manifest[key]) { + manifest[key] = {}; + } + manifest[key]['*'] = ssrEntry; + manifest[key][''] = ssrEntry; + + // Also add default export entry + manifest[key]['default'] = { + ...ssrEntry, + name: 'default', + }; + } +} + +module.exports = { RspackRscPlugin }; diff --git a/config/webpack/serverWebpackConfig.js b/config/webpack/serverWebpackConfig.js index 8dada6496..dd20c06d8 100644 --- a/config/webpack/serverWebpackConfig.js +++ b/config/webpack/serverWebpackConfig.js @@ -6,6 +6,21 @@ const { config } = require('shakapacker'); const commonWebpackConfig = require('./commonWebpackConfig'); const { getBundler } = require('./bundlerUtils'); +/** + * Extract a specific loader from a webpack rule's use array. + * + * @param {Object} rule - Webpack rule with a use array + * @param {string} loaderName - Substring to match against loader names + * @returns {Object|null} The matching loader entry, or null + */ +function extractLoader(rule, loaderName) { + if (!Array.isArray(rule.use)) return null; + return rule.use.find((item) => { + const testValue = typeof item === 'string' ? item : item?.loader; + return testValue && testValue.includes(loaderName); + }); +} + /** * Generates the server-side rendering (SSR) bundle configuration. * @@ -43,13 +58,13 @@ 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`, ); } @@ -79,8 +94,7 @@ const configureServer = () => { 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 @@ -148,12 +162,42 @@ const configureServer = () => { // 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' + // react-on-rails-pro includes RSC-related modules that import Node.js builtins + // (path, fs, stream). These code paths aren't exercised in the SSR bundle, + // so provide empty fallbacks to satisfy the resolver without bundling them. + serverWebpackConfig.resolve = serverWebpackConfig.resolve || {}; + serverWebpackConfig.resolve.fallback = { + ...serverWebpackConfig.resolve.fallback, + path: false, + fs: false, + 'fs/promises': false, + stream: false, + }; + + // The Node renderer runs bundles in a VM sandbox that lacks browser globals + // like MessageChannel and TextEncoder. Inject polyfills at the top of the + // bundle so react-dom/server.browser can initialize. + serverWebpackConfig.plugins.push( + new bundler.BannerPlugin({ + banner: [ + 'if(typeof MessageChannel==="undefined"){', + ' globalThis.MessageChannel=class MessageChannel{', + ' constructor(){', + ' this.port1={onmessage:null};', + ' this.port2={postMessage:function(msg){', + ' var p=this._port1;if(p.onmessage)p.onmessage({data:msg});', + ' }};', + ' this.port2._port1=this.port1;', + ' }', + ' };', + '}', + ].join('\n'), + raw: true, + }), + ); return serverWebpackConfig; }; module.exports = configureServer; +module.exports.extractLoader = extractLoader; diff --git a/config/webpack/webpackConfig.js b/config/webpack/webpackConfig.js index 4f68574e2..df036a145 100644 --- a/config/webpack/webpackConfig.js +++ b/config/webpack/webpackConfig.js @@ -3,30 +3,37 @@ const clientWebpackConfig = require('./clientWebpackConfig'); const serverWebpackConfig = require('./serverWebpackConfig'); +const rscWebpackConfig = require('./rscWebpackConfig'); const webpackConfig = (envSpecific) => { - const clientConfig = clientWebpackConfig(); - const serverConfig = serverWebpackConfig(); - - if (envSpecific) { - envSpecific(clientConfig, serverConfig); - } - let result; // For HMR, need to separate the the client and server webpack configurations if (process.env.WEBPACK_SERVE || process.env.CLIENT_BUNDLE_ONLY) { + const clientConfig = clientWebpackConfig(); + if (envSpecific) envSpecific(clientConfig, null); // eslint-disable-next-line no-console console.log('[React on Rails] Creating only the client bundles.'); result = clientConfig; } else if (process.env.SERVER_BUNDLE_ONLY) { + const serverConfig = serverWebpackConfig(); + if (envSpecific) envSpecific(null, serverConfig); // eslint-disable-next-line no-console console.log('[React on Rails] Creating only the server bundle.'); result = serverConfig; + } else if (process.env.RSC_BUNDLE_ONLY) { + const rscConfig = rscWebpackConfig(); + if (envSpecific) envSpecific(null, null, rscConfig); + // eslint-disable-next-line no-console + console.log('[React on Rails] Creating only the RSC bundle.'); + result = rscConfig; } else { - // default is the standard client and server build + const clientConfig = clientWebpackConfig(); + const serverConfig = serverWebpackConfig(); + const rscConfig = rscWebpackConfig(); + if (envSpecific) envSpecific(clientConfig, serverConfig, rscConfig); // eslint-disable-next-line no-console - console.log('[React on Rails] Creating both client and server bundles.'); - result = [clientConfig, serverConfig]; + console.log('[React on Rails] Creating client, server, and RSC bundles.'); + result = [clientConfig, serverConfig, rscConfig]; } // To debug, uncomment next line and inspect "result" diff --git a/package.json b/package.json index d52d2980c..697827ebe 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 ./react-on-rails-pro-node-renderer.js" }, "dependencies": { "@babel/cli": "^7.21.0", @@ -44,10 +45,10 @@ "@hotwired/stimulus-webpack-helpers": "^1.0.1", "@hotwired/turbo-rails": "^7.3.0", "@rails/actioncable": "7.0.5", - "@rspack/cli": "2.0.0-beta.7", - "@rspack/core": "2.0.0-beta.7", "@rescript/core": "^0.5.0", "@rescript/react": "^0.11.0", + "@rspack/cli": "2.0.0-beta.7", + "@rspack/core": "2.0.0-beta.7", "@swc/core": "^1.13.5", "ajv": "^8.17.1", "autoprefixer": "^10.4.14", @@ -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": "16.6.0-rc.0", + "react-on-rails-pro": "16.5.1", + "react-on-rails-pro-node-renderer": "16.5.1", + "react-on-rails-rsc": "19.0.5-rc.1", "react-redux": "^8.1.0", "react-router": "^6.13.0", "react-router-dom": "^6.13.0", diff --git a/react-on-rails-pro-node-renderer.js b/react-on-rails-pro-node-renderer.js new file mode 100644 index 000000000..d80045899 --- /dev/null +++ b/react-on-rails-pro-node-renderer.js @@ -0,0 +1,24 @@ +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'); +} + +const config = { + serverBundleCachePath: path.resolve(__dirname, '.node-renderer-bundles'), + logLevel: process.env.RENDERER_LOG_LEVEL || 'debug', + password: rendererPassword, + port: process.env.RENDERER_PORT || 3800, + supportModules: true, + workersCount: Number(process.env.NODE_RENDERER_CONCURRENCY || 3), +}; + +if (process.env.CI) { + config.workersCount = 2; +} + +reactOnRailsProNodeRenderer(config); diff --git a/yarn.lock b/yarn.lock index 14b990d67..88c9651b4 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.1: + version "5.8.4" + resolved "https://registry.npmjs.org/fastify/-/fastify-5.8.4.tgz#9ad9ebeea57980cf8722b5c44ca27ea9255cf2d5" + integrity sha512-sa42J1xylbBAYUWALSBoyXKPDUvM3OoNOibIefA+Oha57FryXKKCZarA1iDntOCWp3O35voZLuDg2mdODXtPzQ== + 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,7 +7483,7 @@ 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" integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== @@ -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" @@ -7603,6 +7901,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" @@ -8264,6 +8615,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" @@ -8323,6 +8684,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" @@ -8365,12 +8731,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.4" + resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.0.4.tgz#792d2868dc672c6f8abfce62bdb250e913dcfe2b" + integrity sha512-JiVlwQwuINIQf2+XUjtRFtLxhTE6hcyX7ZyCmY0HM7I/Kgi7qyXThkzwzg6uCfu3rTg9Ofe1x8qWYrfqthIrzg== dependencies: - scheduler "^0.27.0" + scheduler "^0.25.0" react-intl@^6.4.4: version "6.8.9" @@ -8403,10 +8769,39 @@ 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@16.6.0-rc.0: - version "16.6.0-rc.0" - resolved "https://registry.npmjs.org/react-on-rails/-/react-on-rails-16.6.0-rc.0.tgz#ed0ed7085133905ad1e243cc97233e97d10a1c99" - integrity sha512-fSEomzwojgWob6uTWSfkbpP+XE++8kQBjNFpTT7V419QOON1daIHypQwA9gc8L8uX1If5r8hmAs57iWyGWmJuQ== +react-on-rails-pro-node-renderer@16.5.1: + version "16.5.1" + resolved "https://registry.npmjs.org/react-on-rails-pro-node-renderer/-/react-on-rails-pro-node-renderer-16.5.1.tgz#34d08578981593d567838cc82c77b3fd4d03df47" + integrity sha512-QkW+BUhczPbgJ/kLJzEuMoL08wQiZUox7HG1ZBi1cZdjNxGPTcZC7qrD8+NEPgjAoUGZazCcv7DR0kTpYfPURw== + dependencies: + "@fastify/formbody" "^7.4.0 || ^8.0.2" + "@fastify/multipart" "^8.3.1 || ^9.0.3" + fastify "^5.8.1" + fs-extra "^11.2.0" + jsonwebtoken "^9.0.3" + lockfile "^1.0.4" + pino "^9.0.0" + +react-on-rails-pro@16.5.1: + version "16.5.1" + resolved "https://registry.npmjs.org/react-on-rails-pro/-/react-on-rails-pro-16.5.1.tgz#6b2503e7db55a3ff088d51675ccc345b8adb95a5" + integrity sha512-IhE1QklbvWRq4CEZayDUoAKzYbqICdUsQuhC4+NUF1/K0Z6e+a8VJtDK/8nMUZBIDjR/bomE+dez+rnF9F+sHw== + dependencies: + react-on-rails "16.5.1" + +react-on-rails-rsc@19.0.5-rc.1: + version "19.0.5-rc.1" + resolved "https://registry.npmjs.org/react-on-rails-rsc/-/react-on-rails-rsc-19.0.5-rc.1.tgz#a08818b9995bdaf93cdb2b04cb2e7cd5251be08b" + integrity sha512-Qf+pT82eKsicLW29/2Mfz6H3Cq+2rn5tDGSTy2tdvMGsQhq5gVFwnhLKE66FasH/gvFjoTIZJmUFy7PLmHoMMw== + dependencies: + acorn-loose "^8.3.0" + neo-async "^2.6.1" + webpack-sources "^3.2.0" + +react-on-rails@16.5.1: + version "16.5.1" + resolved "https://registry.npmjs.org/react-on-rails/-/react-on-rails-16.5.1.tgz#7fc4eb502e48445ab4f01ae039a25e2aa72447d4" + integrity sha512-IrfmuY5z0GN596nyE27teLbBTvxcaYR+MVjMGbkmoDV79/Tm2MsWt3hdPeYqD7gXu0AQagd+oHvYuyxfSJ4RGw== react-proxy@^1.1.7: version "1.1.8" @@ -8471,10 +8866,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.4" + resolved "https://registry.npmjs.org/react/-/react-19.0.4.tgz#8031673e73cbb8ecba2c35c8c461396aa38dc69d" + integrity sha512-6RpEg9/n0sThnO+2CaMLWuvL1iyctm9/lcSTwvmyCoJYD5eiIrwxevXtrMqrtUr96HCdQB8/Yf+oK1QGy8kXEQ== read-cache@^1.0.0: version "1.0.0" @@ -8517,6 +8912,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" @@ -8727,6 +9127,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" @@ -8750,6 +9155,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" @@ -8794,7 +9204,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== @@ -8826,6 +9236,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.0" + resolved "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.0.tgz#758fd224d066f5abe24f67bd574a01c9dd447f51" + integrity sha512-pNHAuBW7TrcleFHsxBr5QMi/Iyp0ENjUKz7GCcX1UO7cMh+NmVK6HxQckNL1tJp1XAJVjG6B8OKIPqodqj9rtw== + 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" @@ -8878,10 +9300,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" @@ -8912,6 +9334,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" @@ -8940,6 +9367,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" @@ -9023,6 +9455,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" @@ -9167,6 +9604,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" @@ -9237,6 +9681,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" @@ -9655,6 +10104,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" @@ -9689,6 +10152,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" @@ -10101,7 +10569,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==