diff --git a/docs/decisions/0008-stylesheet-import-in-site-config.md b/docs/decisions/0008-stylesheet-import-in-site-config.md index 4df16b2e..a2248e24 100644 --- a/docs/decisions/0008-stylesheet-import-in-site-config.md +++ b/docs/decisions/0008-stylesheet-import-in-site-config.md @@ -1,35 +1,53 @@ -# Shell stylesheet must be imported in site.config file. +# Shell style manifest must be imported in site.config file. ## Summary -A project must import the stylesheet for the application shell in its site.config file. +A site must import the shell's style manifest (`@openedx/frontend-base/shell/style`) from its site.config file. ## Context -There is a particular quirk of the stylesheet loaders for webpack (style-loader and/or css-loader) where the import of stylesheets into JavaScript files must take place in a JS file in the project, not in library dependency like frontend-base. Further, the stylesheet imported into JS must _itself_ be a part of the project. +A composing site needs a single, project-owned entry point for its global CSS. Two concerns drive this: -If, for instance, we try to import a stylesheet from frontend-base (shell, header, footer, etc.) inside a React component inside the shell, webpack silently ignores the import and refuses to load the stylesheet. If we try to import a stylesheet from frontend-base directly into the site.config file in the project, that will also fail with webpack silently ignoring the stylesheet. If, however, frontend-base exports the stylesheet and it's loaded into a SCSS file in the project and _that_ is imported into site.config, everything works correctly. +1. **CSS ownership.** Paragon's base CSS declares CSS custom properties on `:root`. If a lazy-loaded app chunk ships its own copy of Paragon's base CSS, its `:root` declarations run after the site's brand overrides and clobber them globally. The composing site must therefore be the **single owner** of shell, Paragon base, and brand CSS; apps must not re-bundle these. Having the site import the shell's style manifest from its site.config makes this ownership explicit and keeps the rule easy to audit. -This slight indirection through a SCSS file in the project is necessary, and arguably desirable. It ensure as common, unified entry point for SCSS from dependencies of the project. SCSS from the project or micro-frontend itself can be imported into its own components, or can be imported into this top-level SCSS file as desired. Further, this ensures that every aspect of the style of a project or MFE can easily be customized since the stylesheet is supplied through the site.config file. +2. **Layer classification by resource path.** The build pipeline wraps each stylesheet in a CSS cascade layer based on where it was resolved from. For that to classify Paragon's base CSS into the `shell` layer rather than the layer of whichever SCSS file happened to `@use` it, each import must be its own webpack module. The shell's style manifest is a small TS file that imports Paragon's CSS and the shell's own SCSS side by side, so every entry is seen by webpack as an independent compilation unit and classified on its own merits. + +3. **PostCSS-pass scoping.** Paragon exposes responsive breakpoints as `@custom-media` declarations, substituted at build time by `postcss-custom-media`. Substitution only works if the declarations are visible to the same PostCSS pass as the `@media` rule that references them. The shell manifest pulls Paragon's core CSS into the site's build, so any `@media (--pgn-size-breakpoint-*)` reference in a site-level stylesheet resolves correctly. App stylesheets are separate PostCSS passes and handle this themselves; see [the theming guide](../how_tos/theming.md#custom-media-breakpoints) for details. + +An earlier version of this ADR claimed that webpack's stylesheet loaders silently ignore imports sourced from library dependencies. That is no longer accurate with the current loader configuration (see `tools/webpack/common-config/all/getStylesheetRule.ts`). Importing the manifest from site.config remains the correct pattern, but for the ownership, classification, and scoping reasons above rather than a loader quirk. ## Decision -As a best practice, a project should have a top-level SCSS file as a peer to the site.config file. This SCSS file should import the stylesheet from the frontend-base shell application. It should, in turn, be imported into the site.config file. +A project's site.config file must import the shell's style manifest (`@openedx/frontend-base/shell/style`). If the site has additional global styles, it can import its own SCSS file from site.config alongside (or in place of) a brand package. -## Implementation +The shell manifest must be imported **only once**, by the composing site. Apps that are consumed by a site must not import the manifest (or any other source of Paragon base styles) from their runtime code, because doing so causes lazy-loaded app chunks to re-declare Paragon's `:root` CSS custom properties and clobber the site's brand overrides globally. Apps may still import the manifest from `site.config.dev.tsx` (not shipped in `dist/`) so that they render correctly when run standalone. See [the theming guide](../how_tos/theming.md#css-ownership) and [the migration guide](../how_tos/migrate-frontend-app.md#separate-runtime-styles-from-the-dev-harness) for details. -The `site.scss` file should import the stylesheet from the shell: +## Cascade layers -```diff -+ @import '@openedx/frontend-base/shell/app.scss'; +The build pipeline wraps each stylesheet in a CSS cascade layer based on the resolved resource path: -// other styles -``` +| Layer | Sources | +| --------- | ----------------------------------------------------------------- | +| `paragon` | `@openedx/paragon` | +| `shell` | `@openedx/frontend-base` | +| `app` | any other stylesheet resolved from `node_modules` | +| `site` | stylesheets outside `node_modules` (the composing site's source) | +| `brand` | `@(open)?edx/brand*` packages | + +The order declared at the top of `shell/style.scss` is `@layer paragon, shell, app, site, brand;` so the cascade resolves in that order: `brand` wins over `site`, `site` wins over `app`, `app` wins over `shell`, and `shell` wins over `paragon`. -The site.config file should then import the top-level SCSS file: +`brand` is last because, in production, brand CSS is injected at runtime via `` tags that bypass webpack entirely and therefore land **unlayered**. Unlayered rules beat every layered rule regardless of declared order, so runtime brand wins over the site's own CSS. Putting build-time brand imports (e.g. a dev harness that `@use`s a brand package directly) in the last layer keeps dev harness behavior consistent with production: brand overrides apply on top of everything the site declares. + +This is enforced by a PostCSS plugin (`tools/webpack/common-config/all/postcssWrapLayer.ts`) applied as the final step of the CSS pipeline. `@charset`, `@import`, `@use`, `@forward`, and existing `@layer` nodes are preserved at the root; everything else is moved into the layer block. + +Apps must still follow the ownership rule above. The layering is a safety net: if an app chunk does re-ship Paragon's `:root` declarations, its `app`-layer block loses to the site's `site`-layer tokens and any brand overrides. Relying on the safety net rather than the ownership rule still wastes bytes on the wire. + +## Implementation + +The site.config file should import the shell's style manifest: ```diff -+ import './site.scss'; ++ import '@openedx/frontend-base/shell/style'; const siteConfig = { // config document @@ -37,3 +55,5 @@ const siteConfig = { export default siteConfig; ``` + +If the site has its own global styles, it can add an `@use` or `@import` of a local SCSS file after the manifest import. diff --git a/docs/how_tos/migrate-frontend-app.md b/docs/how_tos/migrate-frontend-app.md index 6a30ee11..74cbe012 100644 --- a/docs/how_tos/migrate-frontend-app.md +++ b/docs/how_tos/migrate-frontend-app.md @@ -199,12 +199,11 @@ Define the public API for your package: ```json "exports": { - ".": "./dist/index.js", - "./app.scss": "./dist/app.scss" + ".": "./dist/index.js" }, ``` -The `exports` map decouples your public API from the internal `dist/` directory structure. Consumers import from clean paths (e.g., `@openedx/frontend-app-yourapp/app.scss`) and the map resolves them to the actual files in `dist/`. If your app has SCSS files that downstream site projects need to `@use`, add them as exports as shown above. +The `exports` map decouples your public API from the internal `dist/` directory structure. Consumers import from clean paths and the map resolves them to the actual files in `dist/`. files ----- @@ -770,10 +769,10 @@ Observe the following file and directory structure. Not counting any extra file ``` src (...) +├── sass ├── slots ├── widgets ├── Main.jsx -├── app.scss ├── app.ts ├── constants.ts ├── index.ts @@ -781,7 +780,8 @@ src ├── providers.ts ├── routes.tsx ├── setupTest.tsx -└── slots.tsx +├── slots.tsx +└── style.scss ``` A brief explanation of the new ones: @@ -795,6 +795,8 @@ A brief explanation of the new ones: - `providers.ts`: where global context providers are defined - `routes.tsx`: where the app's routes are declared - `slots.tsx`: what slots the app _uses_; this is distinct from the slots the app _offers_, which are defined in the `slots` directory +- `style.scss`: app-scoped runtime styles, imported from component code (an internal implementation detail, not exported) +- `sass`: partials used by `style.scss`, if any Create, rename, and/or move file contents around to match. Refer to a previously converted MFE (such as [Learner Dashboard](https://github.com/openedx/frontend-app-learner-dashboard/tree/frontend-base/src)) for examples. @@ -871,27 +873,38 @@ This may require a little interpretation. In spirit, the modules of your app ar These modules should be unopinionated about the path prefix where they are mounted. -Create an app.scss file -======================= +Separate runtime styles from the dev harness +============================================ -This is required for running the app in dev mode. +Frontend apps deal with two distinct sets of styles, and the distinction matters for the styling of the composing site: -Create a new `app.scss` file at the top of your application. It's responsible for: +1. **Runtime styles**: App-scoped SCSS that lives inside `src/` and is imported from the app's component code. It is an internal implementation detail of the package, never exposed through the `exports` map, and loaded automatically whenever the app's code runs (including as a lazy-loaded route in a larger site). +2. **Dev harness imports**: The shell's style manifest (`@openedx/frontend-base/shell/style`) loads Paragon's base CSS and the shell's own styles. It is imported only from `site.config.dev.tsx`, so it runs when the app is run standalone for development. -1. Using the shell's stylesheet, which includes Paragon's core stylesheet. -2. Using the stylesheets from your application, if any. +> [!IMPORTANT] +> Runtime styles must NOT import `@openedx/frontend-base/shell/style` (or any other source of Paragon base styles). Paragon's base styles set CSS custom properties at `:root`. If a lazy-loaded app chunk re-injects those declarations, they clobber any brand overrides that the composing site has already applied, breaking theming globally. See [the theming guide](./theming.md#css-ownership) for details. -For example: +Runtime styles +-------------- -``` -@use "@openedx/frontend-base/shell/app.scss"; -@use "sass/style"; +Keep app-scoped SCSS under `src/` and import it directly from the component code that needs it. The suggested layout is a single `src/style.scss` entry point, with any partials under `src/sass/`: + +```ts +import './style.scss'; ``` -You must then import this file from your `site.config.dev.tsx` file: +Any file layout works, as long as the styles are loaded by the app's own JS/TS modules rather than exposed to consumers. + +> [!IMPORTANT] +> Any SCSS entry that uses `@media (--pgn-size-breakpoint-*)` must `@use "@openedx/paragon/styles/css/core/custom-media-breakpoints.css"` at the top. That includes `src/style.scss` and every component-level `index.scss` imported directly from JS: each is its own PostCSS pass and does not inherit the declarations from siblings. Missing `@use`s fail silently (the rule never matches any viewport). See [the theming guide](./theming.md#custom-media-breakpoints) for the full rationale. + +Dev harness imports +------------------- + +Import the shell's style manifest from `site.config.dev.tsx` (NOT from `site.config.build.tsx` or any file that ships). This loads the global styles your app needs when it runs standalone: ```diff -+ import './app.scss'; ++ import '@openedx/frontend-base/shell/style'; const siteConfig: SiteConfig = { // config document @@ -900,6 +913,8 @@ const siteConfig: SiteConfig = { export default siteConfig; ``` +Dev harnesses should NOT import brand packages: running against unbranded Paragon defaults surfaces styling bugs that brand overrides would otherwise hide, and it keeps brand assets out of the app's dependency tree entirely. + Document module-specific configuration needs ============================================ diff --git a/docs/how_tos/theming.md b/docs/how_tos/theming.md index c86d775b..a30e17ac 100644 --- a/docs/how_tos/theming.md +++ b/docs/how_tos/theming.md @@ -38,6 +38,90 @@ and instead use a pre-compiled CSS file. In doing so, this allows making changes to the site theme without needing to necessarily re-build and re-deploy all consuming applications. +### CSS ownership + +A composing site is the **single owner** of global styles. In practice, this +means: + +- The site (and only the site) imports `@openedx/frontend-base/shell/style` + (or any other source of Paragon's base CSS) from its `site.config` file. +- The site (and only the site) loads brand overrides, either through the + runtime `theme` configuration described below or by importing brand CSS + after the shell manifest. +- Apps keep their own SCSS as an internal implementation detail (imported from + the app's component code, never exported through `package.json`). These + styles must contain only app-scoped rules: they must not re-bundle shell or + Paragon base styles, and they must not import brand packages at runtime. + +This matters because Paragon's base CSS declares CSS custom properties on +`:root`. CSS custom properties live on the element they target, and `var()` +resolves at use-time against the current computed value. Without layering, a +lazy-loaded app chunk shipping its own copy of Paragon's base CSS would run +its `:root` declarations after the site's brand overrides and clobber those +overrides globally. + +The build pipeline wraps every stylesheet in a CSS cascade layer based on its +source: Paragon goes in `paragon`, `frontend-base` goes in `shell`, +`node_modules` stylesheets go in `app`, the site's own source goes in +`site`, and `@(open)?edx/brand*` packages go in `brand`. The declared order +is `@layer paragon, shell, app, site, brand;`, so brand tokens and site +overrides out-rank anything an app might accidentally redeclare. + +`brand` comes last to match production: in production, brand CSS is +injected at runtime via `` tags that bypass webpack's layering and +land **unlayered**, which beats every layered rule regardless of declared +order. Putting build-time brand imports (e.g. a dev harness that `@use`s a +brand package) in the last layer keeps dev harness behavior consistent: +brand always wins. A site that needs to override runtime brand CSS must do +so with unlayered rules of its own (or `!important`). See [ADR 0008](../decisions/0008-stylesheet-import-in-site-config.md#cascade-layers) +for details. + +Apps should still keep shell and brand out of their runtime SCSS: the layer +ordering protects token correctness, but shipping duplicate Paragon base CSS +wastes bandwidth and build time. + +Apps should follow the split described in the +[migration guide](./migrate-frontend-app.md#separate-runtime-styles-from-the-dev-harness): +a runtime stylesheet with app-scoped rules, plus a separate dev harness +stylesheet that loads the shell stylesheet only when the app runs standalone. + +### Custom media breakpoints + +Paragon exposes responsive breakpoints as `@custom-media` declarations (e.g., +`--pgn-size-breakpoint-min-width-xl`). Browsers do not implement +`@custom-media` natively; the build pipeline substitutes references at +compile time via `postcss-custom-media`. Substitution only works if the +declarations are visible to the **same PostCSS pass** as the `@media` rule +that references them. + +Because apps no longer transitively import Paragon's core CSS through the +shell, each SCSS entry that uses `@media (--pgn-size-breakpoint-*)` must +import the declarations itself: + +```scss +@use "@openedx/paragon/styles/css/core/custom-media-breakpoints.css"; +``` + +"Each SCSS entry" means every file that is its own PostCSS pass. In +practice, that is: + +- The app's top-level runtime stylesheet (e.g., `src/style.scss`). Covers + every partial it `@use`s via Sass. +- Any component-level `.scss` file imported directly from a JS/TS module + (`import './index.scss'`). Each of these is a separate PostCSS pass and + needs its own `@use`. + +Silent failure is the main hazard: a missing `@use` does not produce a +build error. The unresolved `@media (--pgn-size-breakpoint-*)` simply +never matches any viewport, so the rule quietly does not apply. When +debugging a style that "should apply but doesn't," the fix is usually to +add the `@use` at the top of the stylesheet containing the `@media` rule. + +The composing site's `@openedx/frontend-base/shell/style` manifest already +pulls in Paragon's core CSS (which carries the declarations), so any +`@media (--pgn-size-breakpoint-*)` in a site-level stylesheet resolves +correctly without the explicit `@use`. + ### Dark mode and theme variant preferences `@openedx/frontend-base` supports both `light` (required) and `dark` (optional) diff --git a/package.json b/package.json index 4bb72daa..9e23e82a 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ ".": "./dist/index.js", "./tools": "./dist/tools/index.js", "./tools/tsconfig.json": "./dist/tools/typescript/tsconfig.json", - "./shell/app.scss": "./dist/shell/app.scss", + "./shell/style": "./dist/shell/style.js", "./shell/site": "./dist/shell/site.js" }, "files": [ @@ -53,7 +53,8 @@ }, "sideEffects": [ "*.css", - "*.scss" + "*.scss", + "**/shell/style.{js,ts}" ], "homepage": "https://github.com/openedx/frontend-base#readme", "dependencies": { diff --git a/shell/site.config.dev.tsx b/shell/site.config.dev.tsx index 88c6d33f..75c579e8 100644 --- a/shell/site.config.dev.tsx +++ b/shell/site.config.dev.tsx @@ -2,7 +2,7 @@ import { EnvironmentTypes, SiteConfig } from '../types'; import { devFooterApp, devHeaderApp, devHomeApp, devUserApp, slotShowcaseApp } from './dev'; import { footerApp, headerApp, shellApp } from '.'; -import './app.scss'; +import './style'; const siteConfig: SiteConfig = { apps: [ diff --git a/shell/app.scss b/shell/style.scss similarity index 93% rename from shell/app.scss rename to shell/style.scss index a4c574fd..f4104c68 100644 --- a/shell/app.scss +++ b/shell/style.scss @@ -1,5 +1,4 @@ -@use "@openedx/paragon/dist/core.min.css"; -@use "@openedx/paragon/dist/light.min.css"; +@layer paragon, shell, app, site, brand; .flex-basis-0 { flex-basis: 0 !important; diff --git a/shell/style.ts b/shell/style.ts new file mode 100644 index 00000000..bc030061 --- /dev/null +++ b/shell/style.ts @@ -0,0 +1,9 @@ +/* + * Shell style manifest. Each import is a separate webpack module, which + * keeps Paragon's CSS and the shell's own SCSS as independent compilation + * units: the build pipeline wraps each in the `shell` cascade layer by + * resource path. See ADR 0008. + */ +import '@openedx/paragon/dist/core.min.css'; +import '@openedx/paragon/dist/light.min.css'; +import './style.scss'; diff --git a/test-site/package-lock.json b/test-site/package-lock.json index fbc48795..7d6704df 100644 --- a/test-site/package-lock.json +++ b/test-site/package-lock.json @@ -9,7 +9,6 @@ "version": "1.0.0", "license": "AGPL-3.0", "dependencies": { - "@edx/brand": "npm:@openedx/brand-openedx@^1.2.3", "@openedx/frontend-base": "file:../pack/openedx-frontend-base.tgz", "@openedx/paragon": "^23", "react": "^18", @@ -1843,9 +1842,9 @@ "license": "MIT" }, "node_modules/@bundled-es-modules/glob/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -1951,19 +1950,19 @@ } }, "node_modules/@bundled-es-modules/memfs/node_modules/memfs": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.1.tgz", - "integrity": "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", + "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-fsa": "4.57.1", - "@jsonjoy.com/fs-node": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-to-fsa": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/fs-print": "4.57.1", - "@jsonjoy.com/fs-snapshot": "4.57.1", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-to-fsa": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", @@ -1989,42 +1988,40 @@ } }, "node_modules/@chevrotain/cst-dts-gen": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.2.0.tgz", - "integrity": "sha512-ssJFvn/UXhQQeICw3SR/fZPmYVj+JM2mP+Lx7bZ51cOeHaMWOKp3AUMuyM3QR82aFFXTfcAp67P5GpPjGmbZWQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-12.0.0.tgz", + "integrity": "sha512-fSL4KXjTl7cDgf0B5Rip9Q05BOrYvkJV/RrBTE/bKDN096E4hN/ySpcBK5B24T76dlQ2i32Zc3PAE27jFnFrKg==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/gast": "11.2.0", - "@chevrotain/types": "11.2.0", - "lodash-es": "4.17.23" + "@chevrotain/gast": "12.0.0", + "@chevrotain/types": "12.0.0" } }, "node_modules/@chevrotain/gast": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.2.0.tgz", - "integrity": "sha512-c+KoD6eSI1xjAZZoNUW+V0l13UEn+a4ShmUrjIKs1BeEWCji0Kwhmqn5FSx1K4BhWL7IQKlV7wLR4r8lLArORQ==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-12.0.0.tgz", + "integrity": "sha512-1ne/m3XsIT8aEdrvT33so0GUC+wkctpUPK6zU9IlOyJLUbR0rg4G7ZiApiJbggpgPir9ERy3FRjT6T7lpgetnQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/types": "11.2.0", - "lodash-es": "4.17.23" + "@chevrotain/types": "12.0.0" } }, "node_modules/@chevrotain/regexp-to-ast": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.2.0.tgz", - "integrity": "sha512-lG73pBFqbXODTbXhdZwv0oyUaI+3Irm+uOv5/W79lI3g5hasYaJnVJOm3H2NkhA0Ef4XLBU4Scr7TJDJwgFkAw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-12.0.0.tgz", + "integrity": "sha512-p+EW9MaJwgaHguhoqwOtx/FwuGr+DnNn857sXWOi/mClXIkPGl3rn7hGNWvo31HA3vyeQxjqe+H36yZJwYU8cA==", "license": "Apache-2.0" }, "node_modules/@chevrotain/types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.2.0.tgz", - "integrity": "sha512-vBMSj/lz/LqolbGQEHB0tlpW5BnljHVtp+kzjQfQU+5BtGMTuZCPVgaAjtKvQYXnHb/8i/02Kii00y0tsuwfsw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-12.0.0.tgz", + "integrity": "sha512-S+04vjFQKeuYw0/eW3U52LkAHQsB1ASxsPGsLPUyQgrZ2iNNibQrsidruDzjEX2JYfespXMG0eZmXlhA6z7nWA==", "license": "Apache-2.0" }, "node_modules/@chevrotain/utils": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.2.0.tgz", - "integrity": "sha512-+7whECg4yNWHottjvr2To2BRxL4XJVjIyyv5J4+bJ0iMOVU8j/8n1qPDLZS/90W/BObDR8VNL46lFbzY/Hosmw==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-12.0.0.tgz", + "integrity": "sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==", "license": "Apache-2.0" }, "node_modules/@csstools/cascade-layer-name-parser": { @@ -2123,13 +2120,6 @@ "node": ">=10.0.0" } }, - "node_modules/@edx/brand": { - "name": "@openedx/brand-openedx", - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@openedx/brand-openedx/-/brand-openedx-1.2.3.tgz", - "integrity": "sha512-Dn9CtpC8fovh++Xi4NF5NJoeR9yU2yXZnV9IujxIyGd/dn0Phq5t6dzJVfupwq09mpDnzJv7egA8Znz/3ljO+w==", - "license": "GPL-3.0-or-later" - }, "node_modules/@edx/browserslist-config": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@edx/browserslist-config/-/browserslist-config-1.5.1.tgz", @@ -2147,9 +2137,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "license": "MIT", "optional": true, "dependencies": { @@ -2236,9 +2226,9 @@ "license": "MIT" }, "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -2317,9 +2307,9 @@ "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -2397,9 +2387,9 @@ } }, "node_modules/@formatjs/cli": { - "version": "6.14.1", - "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.14.1.tgz", - "integrity": "sha512-afBgzduP4lrOEm1FxMAVMXKi2FNE7KDOWJ7SyhvEI7zRbHhA7qVkb2DbxpPUhtDg0BrdHjdp8ppVyVVNYaFaaA==", + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/@formatjs/cli/-/cli-6.14.2.tgz", + "integrity": "sha512-0aZLIRcrxIf6QU2u04bIorYIVA1Sm8hHlHzlK9ROGB2BLN0WbiBCwK3jAPV9um+pPS6O4ZFT6K9c2j/3iMkmSw==", "license": "MIT", "bin": { "formatjs": "bin/formatjs" @@ -2664,27 +2654,40 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -3216,9 +3219,9 @@ } }, "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", "license": "MIT", "engines": { "node": ">=8" @@ -3634,13 +3637,13 @@ } }, "node_modules/@jsonjoy.com/fs-core": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.1.tgz", - "integrity": "sha512-YrEi/ZPmgc+GfdO0esBF04qv8boK9Dg9WpRQw/+vM8Qt3nnVIJWIa8HwZ/LXVZ0DB11XUROM8El/7yYTJX+WtA==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-core/-/fs-core-4.57.2.tgz", + "integrity": "sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", "thingies": "^2.5.0" }, "engines": { @@ -3655,14 +3658,14 @@ } }, "node_modules/@jsonjoy.com/fs-fsa": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.1.tgz", - "integrity": "sha512-ooEPvSW/HQDivPDPZMibHGKZf/QS4WRir1czGZmXmp3MsQqLECZEpN0JobrD8iV9BzsuwdIv+PxtWX9WpPLsIA==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-fsa/-/fs-fsa-4.57.2.tgz", + "integrity": "sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", "thingies": "^2.5.0" }, "engines": { @@ -3677,16 +3680,16 @@ } }, "node_modules/@jsonjoy.com/fs-node": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.1.tgz", - "integrity": "sha512-3YaKhP8gXEKN+2O49GLNfNb5l2gbnCFHyAaybbA2JkkbQP3dpdef7WcUaHAulg/c5Dg4VncHsA3NWAUSZMR5KQ==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node/-/fs-node-4.57.2.tgz", + "integrity": "sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/fs-print": "4.57.1", - "@jsonjoy.com/fs-snapshot": "4.57.1", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", "glob-to-regex.js": "^1.0.0", "thingies": "^2.5.0" }, @@ -3702,9 +3705,9 @@ } }, "node_modules/@jsonjoy.com/fs-node-builtins": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.1.tgz", - "integrity": "sha512-XHkFKQ5GSH3uxm8c3ZYXVrexGdscpWKIcMWKFQpMpMJc8gA3AwOMBJXJlgpdJqmrhPyQXxaY9nbkNeYpacC0Og==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-builtins/-/fs-node-builtins-4.57.2.tgz", + "integrity": "sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==", "license": "Apache-2.0", "engines": { "node": ">=10.0" @@ -3718,14 +3721,14 @@ } }, "node_modules/@jsonjoy.com/fs-node-to-fsa": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.1.tgz", - "integrity": "sha512-pqGHyWWzNck4jRfaGV39hkqpY5QjRUQ/nRbNT7FYbBa0xf4bDG+TE1Gt2KWZrSkrkZZDE3qZUjYMbjwSliX6pg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-to-fsa/-/fs-node-to-fsa-4.57.2.tgz", + "integrity": "sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-fsa": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1" + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2" }, "engines": { "node": ">=10.0" @@ -3739,12 +3742,12 @@ } }, "node_modules/@jsonjoy.com/fs-node-utils": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.1.tgz", - "integrity": "sha512-vp+7ZzIB8v43G+GLXTS4oDUSQmhAsRz532QmmWBbdYA20s465JvwhkSFvX9cVTqRRAQg+vZ7zWDaIEh0lFe2gw==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-node-utils/-/fs-node-utils-4.57.2.tgz", + "integrity": "sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-builtins": "4.57.1" + "@jsonjoy.com/fs-node-builtins": "4.57.2" }, "engines": { "node": ">=10.0" @@ -3758,12 +3761,12 @@ } }, "node_modules/@jsonjoy.com/fs-print": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.1.tgz", - "integrity": "sha512-Ynct7ZJmfk6qoXDOKfpovNA36ITUx8rChLmRQtW08J73VOiuNsU8PB6d/Xs7fxJC2ohWR3a5AqyjmLojfrw5yw==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-print/-/fs-print-4.57.2.tgz", + "integrity": "sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.2", "tree-dump": "^1.1.0" }, "engines": { @@ -3778,13 +3781,13 @@ } }, "node_modules/@jsonjoy.com/fs-snapshot": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.1.tgz", - "integrity": "sha512-/oG8xBNFMbDXTq9J7vepSA1kerS5vpgd3p5QZSPd+nX59uwodGJftI51gDYyHRpP57P3WCQf7LHtBYPqwUg2Bg==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/@jsonjoy.com/fs-snapshot/-/fs-snapshot-4.57.2.tgz", + "integrity": "sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==", "license": "Apache-2.0", "dependencies": { "@jsonjoy.com/buffers": "^17.65.0", - "@jsonjoy.com/fs-node-utils": "4.57.1", + "@jsonjoy.com/fs-node-utils": "4.57.2", "@jsonjoy.com/json-pack": "^17.65.0", "@jsonjoy.com/util": "^17.65.0" }, @@ -4065,7 +4068,7 @@ "node_modules/@openedx/frontend-base": { "version": "0.0.0-dev", "resolved": "file:../pack/openedx-frontend-base.tgz", - "integrity": "sha512-jnbN762mXfH+KkZxTTVoxfly0wqgh3izbErSt8qda9nM4woFyyP90cX95ijS3RiaHLsg1GmW2glfAI3OuyaYQw==", + "integrity": "sha512-8DTn3WjNSR9pzvD7u571JAMJFKee+PLJu6sqdG6fcWPsPiJy6qKaCOpAa80NbxME1NPBaAm219pcVsSSj3IqHg==", "license": "AGPL-3.0", "dependencies": { "@babel/core": "^7.24.9", @@ -4079,6 +4082,7 @@ "@formatjs/ts-transformer": "^3.13.14", "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", "@stylistic/eslint-plugin": "^2.9.0", + "@tanstack/react-query-devtools": "^5.99.0", "@types/eslint__js": "^8.42.3", "@types/gradient-string": "^1.1.6", "@types/lodash.keyby": "^4.6.9", @@ -4155,12 +4159,10 @@ "webpack-remove-empty-scripts": "1.0.4" }, "bin": { - "intl-imports.js": "dist/tools/cli/intl-imports.js", - "openedx": "dist/tools/cli/openedx.js", - "transifex-utils.js": "dist/tools/cli/transifex-utils.js" + "openedx": "dist/tools/cli/openedx.js" }, "peerDependencies": { - "@openedx/paragon": "^23.4.5", + "@openedx/paragon": "^23.20.0", "@tanstack/react-query": "^5.81.2", "react": "^18.3.1", "react-dom": "^18.3.1", @@ -4469,9 +4471,9 @@ } }, "node_modules/@openedx/paragon": { - "version": "23.20.0", - "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-23.20.0.tgz", - "integrity": "sha512-2LOzROn0mkNpeaHjaGMnwgJYIY7CZSHFh7iYq3VB7IViMGo6eD+LboPoY5T48pnYVdDTTgiN3YLTI0h+mt5CwQ==", + "version": "23.21.1", + "resolved": "https://registry.npmjs.org/@openedx/paragon/-/paragon-23.21.1.tgz", + "integrity": "sha512-jD7fIaILgqiiDXuUTg5hXgZxI8Ei5x2l2fzSElCTGY7qEYQwd/F3am9UFDp/60mp84L9AlyNW21Hlj0X4e8lYA==", "license": "Apache-2.0", "workspaces": [ "example", @@ -4483,17 +4485,17 @@ "dependencies": { "@popperjs/core": "^2.11.4", "@tokens-studio/sd-transforms": "^1.2.4", - "axios": "^0.30.2", + "axios": "^1.0.0", "bootstrap": "^4.6.2", "chalk": "^4.1.2", "child_process": "^1.0.2", - "chroma-js": "^2.4.2", + "chroma-js": "^3.0.0", "classnames": "^2.3.1", "cli-progress": "^3.12.0", "commander": "^9.4.1", "email-prop-type": "^3.0.0", "file-selector": "^0.10.0", - "glob": "^8.0.3", + "glob": "^13.0.0", "inquirer": "^8.2.5", "js-toml": "^1.0.0", "lodash.uniqby": "^4.7.0", @@ -4535,62 +4537,46 @@ "react-intl": "^5.25.1 || ^6.4.0" } }, - "node_modules/@openedx/paragon/node_modules/axios": { - "version": "0.30.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.30.3.tgz", - "integrity": "sha512-5/tmEb6TmE/ax3mdXBc/Mi6YdPGxQsv+0p5YlciXWt3PHIn0VamqCXhRMtScnwY3lbgSXLneOuXAKUhgmSRpwg==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.4", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/@openedx/paragon/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/@openedx/paragon/node_modules/brace-expansion": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.3.tgz", - "integrity": "sha512-MCV/fYJEbqx68aE58kv2cA/kiky1G8vux3OR6/jbS+jIMe/6fJWa0DTzJU7dqijOWYwHi1t29FlfYI9uytqlpA==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/@openedx/paragon/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", - "license": "ISC", + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, "engines": { - "node": ">=12" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@openedx/paragon/node_modules/minimatch": { - "version": "5.1.9", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", - "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", - "license": "ISC", + "node_modules/@openedx/paragon/node_modules/lru-cache": { + "version": "11.3.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.3.5.tgz", + "integrity": "sha512-NxVFwLAnrd9i7KUBxC4DrUhmgjzOs+1Qm50D3oF1/oL+r1NpZ4gA7xvG0/zJ8evR7zIKn4vLf7qTNduWFtCrRw==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@openedx/paragon/node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@openedx/paragon/node_modules/postcss-custom-media": { @@ -4621,12 +4607,6 @@ "postcss": "^8.4" } }, - "node_modules/@openedx/paragon/node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, "node_modules/@openedx/paragon/node_modules/uuid": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", @@ -5241,9 +5221,9 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.96.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.96.2.tgz", - "integrity": "sha512-hzI6cTVh4KNRk8UtoIBS7Lv9g6BnJPXvBKsvYH1aGWvv0347jT3BnSvztOE+kD76XGvZnRC/t6qdW1CaIfwCeA==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.2.tgz", + "integrity": "sha512-1HunU0bXVsR1ZJMZbcOPE6VtaBJxsW809RE9xPe4Gz7MlB0GWwQvuTPhMoEmQ/hIzFKJ/DWAuttIe7BOaWx0tA==", "license": "MIT", "peer": true, "funding": { @@ -5251,14 +5231,24 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tanstack/query-devtools": { + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.99.2.tgz", + "integrity": "sha512-TEF1d+RYO9l8oeCwgzmOHIgKwAzXQmw2s/ny2bW8qeg2OMkkLjALfVEivgCMR3OL/jVdMmeTPX56WrV+uvYJFg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/react-query": { - "version": "5.96.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.96.2.tgz", - "integrity": "sha512-sYyzzJT4G0g02azzJ8o55VFFV31XvFpdUpG+unxS0vSaYsJnSPKGoI6WdPwUucJL1wpgGfwfmntNX/Ub1uOViA==", + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.2.tgz", + "integrity": "sha512-vM91UEe45QUS9ED6OklsVL15i8qKcRqNwpWzPTVWvRPRSEgDudDgHpvyTjcdlwHcrKNa80T+xXYcchT2noPnZA==", "license": "MIT", "peer": true, "dependencies": { - "@tanstack/query-core": "5.96.2" + "@tanstack/query-core": "5.99.2" }, "funding": { "type": "github", @@ -5268,6 +5258,23 @@ "react": "^18 || ^19" } }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.99.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.99.2.tgz", + "integrity": "sha512-8txkK9A9XBNTB8RoxVgfp6W3qwBr25tNP10L4yu3KuyhAdEvccECfIRzesSwMVk/wpVVioAr+hbMtUkMMF+WVw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.99.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.99.2", + "react": "^18 || ^19" + } + }, "node_modules/@tokens-studio/sd-transforms": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@tokens-studio/sd-transforms/-/sd-transforms-1.3.0.tgz", @@ -5745,16 +5752,16 @@ "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.1.tgz", - "integrity": "sha512-eSkwoemjo76bdXl2MYqtxg51HNwUSkWfODUOQ3PaTLZGh9uIWWFZIjyjaJnex7wXDu+TRx+ATsnSxdN9YWfRTQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.0.tgz", + "integrity": "sha512-HyAZtpdkgZwpq8Sz3FSUvCR4c+ScbuWa9AksK2Jweub7w4M3yTz4O11AqVJzLYjy/B9ZWPyc81I+mOdJU/bDQw==", "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.58.1", - "@typescript-eslint/type-utils": "8.58.1", - "@typescript-eslint/utils": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/type-utils": "8.59.0", + "@typescript-eslint/utils": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -5767,7 +5774,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.58.1", + "@typescript-eslint/parser": "^8.59.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -5782,15 +5789,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.1.tgz", - "integrity": "sha512-gGkiNMPqerb2cJSVcruigx9eHBlLG14fSdPdqMoOcBfh+vvn4iCq2C8MzUB89PrxOXk0y3GZ1yIWb9aOzL93bw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.0.tgz", + "integrity": "sha512-TI1XGwKbDpo9tRW8UDIXCOeLk55qe9ZFGs8MTKU6/M08HWTw52DD/IYhfQtOEhEdPhLMT26Ka/x7p70nd3dzDg==", "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.58.1", - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/typescript-estree": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1", + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3" }, "engines": { @@ -5806,13 +5813,13 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.1.tgz", - "integrity": "sha512-gfQ8fk6cxhtptek+/8ZIqw8YrRW5048Gug8Ts5IYcMLCw18iUgrZAEY/D7s4hkI0FxEfGakKuPK/XUMPzPxi5g==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.0.tgz", + "integrity": "sha512-Lw5ITrR5s5TbC19YSvlr63ZfLaJoU6vtKTHyB0GQOpX0W7d5/Ir6vUahWi/8Sps/nOukZQ0IB3SmlxZnjaKVnw==", "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.58.1", - "@typescript-eslint/types": "^8.58.1", + "@typescript-eslint/tsconfig-utils": "^8.59.0", + "@typescript-eslint/types": "^8.59.0", "debug": "^4.4.3" }, "engines": { @@ -5827,13 +5834,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.1.tgz", - "integrity": "sha512-TPYUEqJK6avLcEjumWsIuTpuYODTTDAtoMdt8ZZa93uWMTX13Nb8L5leSje1NluammvU+oI3QRr5lLXPgihX3w==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.0.tgz", + "integrity": "sha512-UzR16Ut8IpA3Mc4DbgAShlPPkVm8xXMWafXxB0BocaVRHs8ZGakAxGRskF7FId3sdk9lgGD73GSFaWmWFDE4dg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1" + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5844,9 +5851,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.1.tgz", - "integrity": "sha512-JAr2hOIct2Q+qk3G+8YFfqkqi7sC86uNryT+2i5HzMa2MPjw4qNFvtjnw1IiA1rP7QhNKVe21mSSLaSjwA1Olw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.0.tgz", + "integrity": "sha512-91Sbl3s4Kb3SybliIY6muFBmHVv+pYXfybC4Oolp3dvk8BvIE3wOPc+403CWIT7mJNkfQRGtdqghzs2+Z91Tqg==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5860,14 +5867,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.1.tgz", - "integrity": "sha512-HUFxvTJVroT+0rXVJC7eD5zol6ID+Sn5npVPWoFuHGg9Ncq5Q4EYstqR+UOqaNRFXi5TYkpXXkLhoCHe3G0+7w==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.0.tgz", + "integrity": "sha512-3TRiZaQSltGqGeNrJzzr1+8YcEobKH9rHnqIp/1psfKFmhRQDNMGP5hBufanYTGznwShzVLs3Mz+gDN7HkWfXg==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/typescript-estree": "8.58.1", - "@typescript-eslint/utils": "8.58.1", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -5884,9 +5891,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.1.tgz", - "integrity": "sha512-io/dV5Aw5ezwzfPBBWLoT+5QfVtP8O7q4Kftjn5azJ88bYyp/ZMCsyW1lpKK46EXJcaYMZ1JtYj+s/7TdzmQMw==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.0.tgz", + "integrity": "sha512-nLzdsT1gdOgFxxxwrlNVUBzSNBEEHJ86bblmk4QAS6stfig7rcJzWKqCyxFy3YRRHXDWEkb2NralA1nOYkkm/A==", "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5897,15 +5904,15 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.1.tgz", - "integrity": "sha512-w4w7WR7GHOjqqPnvAYbazq+Y5oS68b9CzasGtnd6jIeOIeKUzYzupGTB2T4LTPSv4d+WPeccbxuneTFHYgAAWg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.0.tgz", + "integrity": "sha512-O9Re9P1BmBLFJyikRbQpLku/QA3/AueZNO9WePLBwQrvkixTmDe8u76B6CYUAITRl/rHawggEqUGn5QIkVRLMw==", "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.58.1", - "@typescript-eslint/tsconfig-utils": "8.58.1", - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/visitor-keys": "8.58.1", + "@typescript-eslint/project-service": "8.59.0", + "@typescript-eslint/tsconfig-utils": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/visitor-keys": "8.59.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -5936,15 +5943,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.1.tgz", - "integrity": "sha512-Ln8R0tmWC7pTtLOzgJzYTXSCjJ9rDNHAqTaVONF4FEi2qwce8mD9iSOxOpLFFvWp/wBFlew0mjM1L1ihYWfBdQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.0.tgz", + "integrity": "sha512-I1R/K7V07XsMJ12Oaxg/O9GfrysGTmCRhvZJBv0RE0NcULMzjqVpR5kRRQjHsz3J/bElU7HwCO7zkqL+MSUz+g==", "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.58.1", - "@typescript-eslint/types": "8.58.1", - "@typescript-eslint/typescript-estree": "8.58.1" + "@typescript-eslint/scope-manager": "8.59.0", + "@typescript-eslint/types": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5959,12 +5966,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.1.tgz", - "integrity": "sha512-y+vH7QE8ycjoa0bWciFg7OpFcipUuem1ujhrdLtq1gByKwfbC7bPeKsiny9e0urg93DqwGcHey+bGRKCnF1nZQ==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.0.tgz", + "integrity": "sha512-/uejZt4dSere1bx12WLlPfv8GktzcaDtuJ7s42/HEZ5zGj9oxRaD4bj7qwSunXkf+pbAhFt2zjpHYUiT5lHf0Q==", "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.58.1", + "@typescript-eslint/types": "8.59.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -6684,13 +6691,13 @@ "license": "MIT" }, "node_modules/asn1js": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.7.tgz", - "integrity": "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.10.tgz", + "integrity": "sha512-S2s3aOytiKdFRdulw2qPE51MzjzVOisppcVv7jVFR+Kw0kxwvFrDcYA0h7Ndqbmj0HkMIXYWaoj7fli8kgx1eg==", "license": "BSD-3-Clause", "dependencies": { "pvtsutils": "^1.3.6", - "pvutils": "^1.1.3", + "pvutils": "^1.1.5", "tslib": "^2.8.1" }, "engines": { @@ -6765,9 +6772,9 @@ } }, "node_modules/autoprefixer": { - "version": "10.4.27", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", - "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", "funding": [ { "type": "opencollective", @@ -6784,8 +6791,8 @@ ], "license": "MIT", "dependencies": { - "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001774", + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -6816,18 +6823,18 @@ } }, "node_modules/axe-core": { - "version": "4.11.2", - "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.2.tgz", - "integrity": "sha512-byD6KPdvo72y/wj2T/4zGEvvlis+PsZsn/yPS3pEO+sFpcrqRpX/TJCxvVaEsNeMrfQbCr7w163YqoD9IYwHXw==", + "version": "4.11.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.3.tgz", + "integrity": "sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg==", "license": "MPL-2.0", "engines": { "node": ">=4" } }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.2.tgz", + "integrity": "sha512-wLrXxPtcrPTsNlJmKjkPnNPK2Ihe0hn0wGSaTEiHRPxwjvJwT3hKmXF4dpqxmPO9SoNb2FsYXj/xEo0gHN+D5A==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", @@ -7048,9 +7055,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.16", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.16.tgz", - "integrity": "sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==", + "version": "2.10.20", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.20.tgz", + "integrity": "sha512-1AaXxEPfXT+GvTBJFuy4yXVHWJBXa4OdbIebGN/wX5DlsIkU0+wzGnd2lOzokSk51d5LUmqjgBLRLlypLUqInQ==", "license": "Apache-2.0", "bin": { "baseline-browser-mapping": "dist/cli.cjs" @@ -7309,14 +7316,14 @@ "license": "MIT" }, "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "license": "MIT", "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", "set-function-length": "^1.2.2" }, "engines": { @@ -7396,9 +7403,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001787", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001787.tgz", - "integrity": "sha512-mNcrMN9KeI68u7muanUpEejSLghOKlVhRqS/Za2IeyGllJ9I9otGpR9g3nsw7n4W378TE/LyIteA0+/FOZm4Kg==", + "version": "1.0.30001790", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001790.tgz", + "integrity": "sha512-bOoxfJPyYo+ds6W0YfptaCWbFnJYjh2Y1Eow5lRv+vI2u8ganPZqNm1JwNh0t2ELQCqIWg4B3dWEusgAmsoyOw==", "funding": [ { "type": "opencollective", @@ -7468,17 +7475,19 @@ "license": "MIT" }, "node_modules/chevrotain": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.2.0.tgz", - "integrity": "sha512-mHCHTxM51nCklUw9RzRVc0DLjAh/SAUPM4k/zMInlTIo25ldWXOZoPt7XEIk/LwoT4lFVmJcu9g5MHtx371x3A==", + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-12.0.0.tgz", + "integrity": "sha512-csJvb+6kEiQaqo1woTdSAuOWdN0WTLIydkKrBnS+V5gZz0oqBrp4kQ35519QgK6TpBThiG3V1vNSHlIkv4AglQ==", "license": "Apache-2.0", "dependencies": { - "@chevrotain/cst-dts-gen": "11.2.0", - "@chevrotain/gast": "11.2.0", - "@chevrotain/regexp-to-ast": "11.2.0", - "@chevrotain/types": "11.2.0", - "@chevrotain/utils": "11.2.0", - "lodash-es": "4.17.23" + "@chevrotain/cst-dts-gen": "12.0.0", + "@chevrotain/gast": "12.0.0", + "@chevrotain/regexp-to-ast": "12.0.0", + "@chevrotain/types": "12.0.0", + "@chevrotain/utils": "12.0.0" + }, + "engines": { + "node": ">=22.0.0" } }, "node_modules/child_process": { @@ -7504,9 +7513,9 @@ } }, "node_modules/chroma-js": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-2.6.0.tgz", - "integrity": "sha512-BLHvCB9s8Z1EV4ethr6xnkl/P2YRFOGqfgvuMG/MyCbZPrTA+NeiByY6XvgF0zP4/2deU2CXnWyMa3zu1LqQ3A==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/chroma-js/-/chroma-js-3.2.0.tgz", + "integrity": "sha512-os/OippSlX1RlWWr+QDPcGUZs0uoqr32urfxESG9U93lhUfbnlyckte84Q8P1UQY/qth983AS1JONKmLS4T0nw==", "license": "(BSD-3-Clause AND Apache-2.0)" }, "node_modules/chrome-trace-event": { @@ -8809,9 +8818,9 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.334", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.334.tgz", - "integrity": "sha512-mgjZAz7Jyx1SRCwEpy9wefDS7GvNPazLthHg8eQMJ76wBdGQQDW33TCrUTvQ4wzpmOrv2zrFoD3oNufMdyMpog==", + "version": "1.5.343", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.343.tgz", + "integrity": "sha512-YHnQ3MXI08icvL9ZKnEBy05F2EQ8ob01UaMOuMbM8l+4UcAq6MPPbBTJBbsBUg3H8JeZNt+O4fjsoWth3p6IFg==", "license": "ISC" }, "node_modules/email-prop-type": { @@ -9018,15 +9027,15 @@ } }, "node_modules/es-iterator-helpers": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.1.tgz", - "integrity": "sha512-zWwRvqWiuBPr0muUG/78cW3aHROFCNIQ3zpmYDpwdbnt2m+xlNyRWpHBpa2lJjSBit7BQ+RXA1iwbSmu5yJ/EQ==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.3.2.tgz", + "integrity": "sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", + "call-bind": "^1.0.9", "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.24.1", + "es-abstract": "^1.24.2", "es-errors": "^1.3.0", "es-set-tostringtag": "^2.1.0", "function-bind": "^1.1.2", @@ -9038,8 +9047,7 @@ "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "iterator.prototype": "^1.1.5", - "math-intrinsics": "^1.1.0", - "safe-array-concat": "^1.1.3" + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -9305,9 +9313,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -9377,9 +9385,9 @@ "license": "MIT" }, "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -9456,9 +9464,9 @@ "license": "MIT" }, "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -10075,9 +10083,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "funding": [ { "type": "individual", @@ -10171,9 +10179,9 @@ "license": "MIT" }, "node_modules/fork-ts-checker-webpack-plugin/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -10571,9 +10579,9 @@ "license": "MIT" }, "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -10817,9 +10825,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -12950,12 +12958,12 @@ "license": "MIT" }, "node_modules/js-toml": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/js-toml/-/js-toml-1.0.3.tgz", - "integrity": "sha512-sgyRKshBUSPIlUrbVXYQHReVZUXKHTldaW+Fj7KSan21vgnmMpuAAo00rBvm7W4HQrvZSvv186wNHlIjMPYC/A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/js-toml/-/js-toml-1.1.0.tgz", + "integrity": "sha512-ttRqC9rcSsV7diH6CsneXqDJWb0l9Guv2dA/0a3SQwdldCk6qsXN4O7Tg5UQevove7CXaYCcsqBn9wEQiIM3ZQ==", "license": "MIT", "dependencies": { - "chevrotain": "^11.1.1", + "chevrotain": "^12.0.0", "xregexp": "^5.1.2" } }, @@ -13085,9 +13093,9 @@ } }, "node_modules/jsonfile": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", - "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -13301,12 +13309,6 @@ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "license": "MIT" }, - "node_modules/lodash-es": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", - "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==", - "license": "MIT" - }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", @@ -13841,9 +13843,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.37", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", - "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", "license": "MIT" }, "node_modules/normalize-path": { @@ -14668,9 +14670,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.10.tgz", + "integrity": "sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==", "funding": [ { "type": "opencollective", @@ -15402,9 +15404,9 @@ } }, "node_modules/prettier": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", - "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" @@ -15799,9 +15801,9 @@ "license": "MIT" }, "node_modules/react-dev-utils/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -16585,9 +16587,9 @@ "license": "MIT" }, "node_modules/recursive-readdir/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -16775,11 +16777,12 @@ "license": "MIT" }, "node_modules/resolve": { - "version": "1.22.11", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", - "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", "license": "MIT", "dependencies": { + "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" @@ -17000,14 +17003,14 @@ } }, "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", "license": "MIT", "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", "has-symbols": "^1.1.0", "isarray": "^2.0.5" }, @@ -18752,9 +18755,9 @@ "license": "MIT" }, "node_modules/tapable": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", - "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", "license": "MIT", "engines": { "node": ">=6" @@ -18890,9 +18893,9 @@ "license": "MIT" }, "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz", + "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -19323,15 +19326,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.58.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.1.tgz", - "integrity": "sha512-gf6/oHChByg9HJvhMO1iBexJh12AqqTfnuxscMDOVqfJW3htsdRJI/GfPpHTTcyeB8cSTUY2JcZmVgoyPqcrDg==", + "version": "8.59.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.0.tgz", + "integrity": "sha512-BU3ONW9X+v90EcCH9ZS6LMackcVtxRLlI3XrYyqZIwVSHIk7Qf7bFw1z0M9Q0IUxhTMZCf8piY9hTYaNEIASrw==", "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.58.1", - "@typescript-eslint/parser": "8.58.1", - "@typescript-eslint/typescript-estree": "8.58.1", - "@typescript-eslint/utils": "8.58.1" + "@typescript-eslint/eslint-plugin": "8.59.0", + "@typescript-eslint/parser": "8.59.0", + "@typescript-eslint/typescript-estree": "8.59.0", + "@typescript-eslint/utils": "8.59.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -19776,9 +19779,9 @@ } }, "node_modules/webpack": { - "version": "5.106.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.0.tgz", - "integrity": "sha512-Pkx5joZ9RrdgO5LBkyX1L2ZAJeK/Taz3vqZ9CbcP0wS5LEMx5QkKsEwLl29QJfihZ+DKRBFldzy1O30pJ1MDpA==", + "version": "5.106.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", + "integrity": "sha512-wGN3qcrBQIFmQ/c0AiOAQBvrZ5lmY8vbbMv4Mxfgzqd/B6+9pXtLo73WuS1dSGXM5QYY3hZnIbvx+K1xxe6FyA==", "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", @@ -19797,9 +19800,8 @@ "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.3.1", - "mime-types": "^2.1.27", + "mime-db": "^1.54.0", "neo-async": "^2.6.2", "schema-utils": "^4.3.3", "tapable": "^2.3.0", @@ -19963,19 +19965,19 @@ } }, "node_modules/webpack-dev-middleware/node_modules/memfs": { - "version": "4.57.1", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.1.tgz", - "integrity": "sha512-WvzrWPwMQT+PtbX2Et64R4qXKK0fj/8pO85MrUCzymX3twwCiJCdvntW3HdhG1teLJcHDDLIKx5+c3HckWYZtQ==", + "version": "4.57.2", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.57.2.tgz", + "integrity": "sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==", "license": "Apache-2.0", "dependencies": { - "@jsonjoy.com/fs-core": "4.57.1", - "@jsonjoy.com/fs-fsa": "4.57.1", - "@jsonjoy.com/fs-node": "4.57.1", - "@jsonjoy.com/fs-node-builtins": "4.57.1", - "@jsonjoy.com/fs-node-to-fsa": "4.57.1", - "@jsonjoy.com/fs-node-utils": "4.57.1", - "@jsonjoy.com/fs-print": "4.57.1", - "@jsonjoy.com/fs-snapshot": "4.57.1", + "@jsonjoy.com/fs-core": "4.57.2", + "@jsonjoy.com/fs-fsa": "4.57.2", + "@jsonjoy.com/fs-node": "4.57.2", + "@jsonjoy.com/fs-node-builtins": "4.57.2", + "@jsonjoy.com/fs-node-to-fsa": "4.57.2", + "@jsonjoy.com/fs-node-utils": "4.57.2", + "@jsonjoy.com/fs-print": "4.57.2", + "@jsonjoy.com/fs-snapshot": "4.57.2", "@jsonjoy.com/json-pack": "^1.11.0", "@jsonjoy.com/util": "^1.9.0", "glob-to-regex.js": "^1.0.1", diff --git a/test-site/package.json b/test-site/package.json index 68c4111c..7beb0122 100644 --- a/test-site/package.json +++ b/test-site/package.json @@ -14,7 +14,6 @@ "author": "Open edX Community", "license": "AGPL-3.0", "dependencies": { - "@edx/brand": "npm:@openedx/brand-openedx@^1.2.3", "@openedx/frontend-base": "file:../pack/openedx-frontend-base.tgz", "@openedx/paragon": "^23", "react": "^18", diff --git a/test-site/site.config.build.tsx b/test-site/site.config.build.tsx index 2e6dfaf6..e992ea9b 100644 --- a/test-site/site.config.build.tsx +++ b/test-site/site.config.build.tsx @@ -2,7 +2,7 @@ import { footerApp, headerApp, shellApp, EnvironmentTypes, SiteConfig } from '@o import { authenticatedPageConfig, examplePageConfig, iframeWidgetConfig } from './src'; -import './src/site.scss'; +import '@openedx/frontend-base/shell/style'; const siteConfig: SiteConfig = { siteId: 'test', diff --git a/test-site/site.config.dev.tsx b/test-site/site.config.dev.tsx index 770bbc3a..fae9839f 100644 --- a/test-site/site.config.dev.tsx +++ b/test-site/site.config.dev.tsx @@ -2,7 +2,7 @@ import { footerApp, headerApp, shellApp, EnvironmentTypes, SiteConfig } from '@o import { authenticatedPageConfig, examplePageConfig, iframeWidgetConfig } from './src'; -import './src/site.scss'; +import '@openedx/frontend-base/shell/style'; const siteConfig: SiteConfig = { siteId: 'test', diff --git a/test-site/src/example-page/ExamplePage.tsx b/test-site/src/example-page/ExamplePage.tsx index 0993b0dc..7e317e1f 100644 --- a/test-site/src/example-page/ExamplePage.tsx +++ b/test-site/src/example-page/ExamplePage.tsx @@ -10,6 +10,7 @@ import { Container } from '@openedx/paragon'; import { useEffect } from 'react'; import { Link } from 'react-router-dom'; import messages from '../messages'; +import './style.scss'; import Image from './Image'; import appleImg from './apple.jpg'; import appleUrl from './apple.svg'; diff --git a/test-site/src/site.scss b/test-site/src/example-page/style.scss similarity index 65% rename from test-site/src/site.scss rename to test-site/src/example-page/style.scss index 23790707..b47d2cbb 100644 --- a/test-site/src/site.scss +++ b/test-site/src/example-page/style.scss @@ -1,5 +1,3 @@ -@use '@openedx/frontend-base/shell/app.scss'; - .red-text { color: var(--pgn-color-red); } diff --git a/tools/webpack/common-config/all/getStylesheetRule.ts b/tools/webpack/common-config/all/getStylesheetRule.ts index 958d70e2..5ce0e030 100644 --- a/tools/webpack/common-config/all/getStylesheetRule.ts +++ b/tools/webpack/common-config/all/getStylesheetRule.ts @@ -5,50 +5,91 @@ import path from 'path'; import PostCssCustomMediaCSS from 'postcss-custom-media'; import PostCssRTLCSS from 'postcss-rtlcss'; import { RuleSetRule } from 'webpack'; +import postcssWrapLayer from './postcssWrapLayer'; -/** +/* + * Resource matchers for each cascade layer. + * Order declared at the site level: `paragon, shell, app, site, brand` (later wins). + * + * - paragon: @openedx/paragon base styles. First so everything else can override. + * - shell: frontend-base shell styles. + * - app: catch-all for any other stylesheet pulled from node_modules. + * Keeps apps from clobbering site or brand overrides without requiring + * app packages to follow any naming convention or self-declaration. + * - site: the composing site's own source tree (anything outside node_modules). + * - brand: @openedx/brand-* packages, last so build-time brand matches the + * precedence of runtime brand CSS (which is injected unlayered via + * tags and thus beats every layered rule). + */ +const PARAGON_RESOURCE = /@openedx[\\/]paragon[\\/]/; +const SHELL_RESOURCE = /(@openedx[\\/]frontend-base|frontend-base[\\/]shell)[\\/]/; +const BRAND_RESOURCE = /@(open)?edx[\\/]brand(-[^\\/]+)?[\\/]/; +const NODE_MODULES = /[\\/]node_modules[\\/]/; + +/* * There are a few things we need to do here. * * - We only want to use MiniCssExtractPlugin on dependencies in dev, but not on our source code. * - We only want CssNano in production. + * - Each resource class (paragon, shell, app, site, brand) is wrapped in its + * own cascade layer by a PostCSS plugin so the cascade order is + * `paragon, shell, app, site, brand`. */ export default function getStylesheetRule(mode: 'dev' | 'production'): RuleSetRule { + return { + test: /(.scss|.css)$/, + oneOf: [ + { + resource: PARAGON_RESOURCE, + // We need Paragon to not elide CSS: we have to be able to import it + // directly from shell/style.ts + sideEffects: true, + use: [ + MiniCssExtractPlugin.loader, + ...getStyleUseConfig(mode, 'paragon'), + ], + }, + { + resource: SHELL_RESOURCE, + use: [ + MiniCssExtractPlugin.loader, + ...getStyleUseConfig(mode, 'shell'), + ], + }, + { + resource: BRAND_RESOURCE, + use: [ + MiniCssExtractPlugin.loader, + ...getStyleUseConfig(mode, 'brand'), + ], + }, + { + resource: { not: [NODE_MODULES] }, + use: [ + getFirstLoader(mode), + ...getStyleUseConfig(mode, 'site'), + ], + }, + { + use: [ + getFirstLoader(mode), + ...getStyleUseConfig(mode, 'app'), + ], + }, + ], + }; +} + +function getFirstLoader(mode: 'dev' | 'production') { + // In dev we keep site/app styles in JS bundles so hot-reloading works; + // in production everything is extracted to CSS files. if (mode === 'production') { - // In the production case, all files should go through MiniCssExtractPlugin. - return { - test: /(.scss|.css)$/, - use: [ - MiniCssExtractPlugin.loader, - ...getStyleUseConfig(mode), - ], - }; - } else { - // In the dev case, only our @openedx dependencies go through MiniCssExtractPlugin. - // We are not extracting CSS from the javascript bundles in development because extracting - // prevents hot-reloading from working, it increases build time, and we don't care about - // flash-of-unstyled-content issues in development. - return { - test: /(.scss|.css)$/, - oneOf: [ - { - resource: /(@openedx\/paragon|@(open)?edx\/brand)/, - use: [ - MiniCssExtractPlugin.loader, - ...getStyleUseConfig(mode), - ], - }, - { - use: [ - require.resolve('style-loader'), // creates style nodes from JS strings - ...getStyleUseConfig(mode), - ], - }, - ] - }; + return MiniCssExtractPlugin.loader; } + return require.resolve('style-loader'); } -function getStyleUseConfig(mode: 'dev' | 'production') { +function getStyleUseConfig(mode: 'dev' | 'production', layer: string) { return [ { loader: require.resolve('css-loader'), // translates CSS into CommonJS @@ -67,7 +108,7 @@ function getStyleUseConfig(mode: 'dev' | 'production') { loader: require.resolve('postcss-loader'), options: { postcssOptions: { - plugins: getPostCssLoaderPlugins(mode), // Different behavior for dev and production. + plugins: getPostCssLoaderPlugins(mode, layer), }, }, }, @@ -90,10 +131,7 @@ function getStyleUseConfig(mode: 'dev' | 'production') { ]; } -/** - * This exists just to conditionally include CssNano in production. - */ -function getPostCssLoaderPlugins(mode: 'dev' | 'production') { +function getPostCssLoaderPlugins(mode: 'dev' | 'production', layer: string) { const plugins: any[] = [ PostCssAutoprefixerPlugin({ remove: false, // Prevents removing vendor prefixes @@ -107,5 +145,6 @@ function getPostCssLoaderPlugins(mode: 'dev' | 'production') { } plugins.push(PostCssCustomMediaCSS()); + plugins.push(postcssWrapLayer({ layer })); return plugins; } diff --git a/tools/webpack/common-config/all/postcssWrapLayer.test.ts b/tools/webpack/common-config/all/postcssWrapLayer.test.ts new file mode 100644 index 00000000..79e4804c --- /dev/null +++ b/tools/webpack/common-config/all/postcssWrapLayer.test.ts @@ -0,0 +1,73 @@ +import postcss from 'postcss'; +import postcssWrapLayer from './postcssWrapLayer'; + +async function run(input: string, layer = 'shell') { + const result = await postcss([postcssWrapLayer({ layer })]).process(input, { from: undefined }); + return result.css.trim(); +} + +describe('postcssWrapLayer', () => { + it('wraps a plain rule in the named layer', async () => { + const output = await run('.foo { color: red; }'); + expect(output).toMatch(/^@layer shell\s*\{[\s\S]*\.foo[\s\S]*\}\s*$/); + }); + + it('leaves @charset at the root', async () => { + const output = await run('@charset "UTF-8";\n.foo { color: red; }'); + expect(output).toMatch(/^@charset "UTF-8";/); + expect(output).toContain('@layer shell{'); + expect(output).toContain('.foo'); + }); + + it('leaves @import at the root', async () => { + const output = await run('@import "other.css";\n.foo { color: red; }'); + expect(output).toMatch(/^@import "other\.css";/); + expect(output).toContain('@layer shell{'); + }); + + it('leaves @use at the root', async () => { + const output = await run('@use "other";\n.foo { color: red; }'); + expect(output).toMatch(/^@use "other";/); + expect(output).toContain('@layer shell{'); + }); + + it('leaves @forward at the root', async () => { + const output = await run('@forward "other";\n.foo { color: red; }'); + expect(output).toMatch(/^@forward "other";/); + expect(output).toContain('@layer shell{'); + }); + + it('leaves @layer order statements at the root', async () => { + const output = await run('@layer shell, app, brand, site;\n.foo { color: red; }'); + expect(output).toMatch(/^@layer shell, app, brand, site;/); + expect(output).toContain('@layer shell{'); + // The order statement is not nested inside the wrap. + expect(output).not.toMatch(/@layer shell\s*\{[\s\S]*@layer shell, app, brand, site;/); + }); + + it('leaves existing @layer blocks at the root (does not nest them)', async () => { + const output = await run('@layer paragon { .bar { color: blue; } }\n.foo { color: red; }'); + // The paragon block stays at the root. + expect(output).toMatch(/^@layer paragon \{/); + // The .foo rule is wrapped in shell; the paragon block is not inside it. + expect(output).not.toMatch(/@layer shell\s*\{[\s\S]*@layer paragon/); + expect(output).toMatch(/@layer shell\s*\{/); + }); + + it('emits nothing extra when there is no wrappable content', async () => { + const output = await run('@layer shell, app, brand, site;'); + expect(output).toBe('@layer shell, app, brand, site;'); + expect(output).not.toContain('@layer shell{'); + }); + + it('wraps multiple sibling rules in a single layer block in source order', async () => { + const output = await run('.a { color: red; }\n.b { color: blue; }\n.c { color: green; }'); + expect(output.match(/@layer shell\s*\{/g)).toHaveLength(1); + const a = output.indexOf('.a'); + const b = output.indexOf('.b'); + const c = output.indexOf('.c'); + expect(a).toBeGreaterThan(-1); + expect(b).toBeGreaterThan(a); + expect(c).toBeGreaterThan(b); + }); +}); diff --git a/tools/webpack/common-config/all/postcssWrapLayer.ts b/tools/webpack/common-config/all/postcssWrapLayer.ts new file mode 100644 index 00000000..cf90810b --- /dev/null +++ b/tools/webpack/common-config/all/postcssWrapLayer.ts @@ -0,0 +1,30 @@ +import { AtRule, Plugin, Root } from 'postcss'; + +/* + * Wraps every top-level rule in the stylesheet in a single `@layer { ... }` + * block, so that downstream cascade-layer ordering applies uniformly to everything + * produced by this compilation unit. + * + * Nodes left at the root (not moved into the layer): + * - `@charset`, `@import`, `@use`, `@forward` (must stay at the top of a stylesheet) + * - `@layer` statements and blocks (already layered, or declaring layer order) + */ +export default function postcssWrapLayer({ layer }: { layer: string }): Plugin { + const keepAtTop = new Set(['charset', 'import', 'use', 'forward', 'layer']); + return { + postcssPlugin: 'postcss-wrap-layer', + Once(root: Root) { + const layerRule = new AtRule({ name: 'layer', params: layer }); + root.each(node => { + if (node.type === 'atrule' && keepAtTop.has(node.name)) { + return; + } + node.remove(); + layerRule.append(node); + }); + if (layerRule.nodes && layerRule.nodes.length > 0) { + root.append(layerRule); + } + }, + }; +}