diff --git a/.github/renovate.json5 b/.github/renovate.json5 index 6e142edf8a7..997812735de 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -119,7 +119,7 @@ "rimraf", "ssh-encoding", "ssh-key", - "@storybook/web-components-webpack5", + "@storybook/web-components-vite", "tabbable", "tldts", "wait-on", @@ -311,26 +311,24 @@ "@compodoc/compodoc", "@ng-select/ng-select", "@storybook/addon-a11y", - "@storybook/addon-actions", "@storybook/addon-designs", - "@storybook/addon-essentials", - "@storybook/addon-interactions", + "@storybook/addon-docs", "@storybook/addon-links", "@storybook/test-runner", "@storybook/addon-themes", "@storybook/angular", - "@storybook/manager-api", - "@storybook/theming", "@types/react", "autoprefixer", "bootstrap", "chromatic", "ngx-toastr", + "path-browserify", "react", "react-dom", "remark-gfm", "storybook", "tailwindcss", + "vite-tsconfig-paths", "zone.js", "@tailwindcss/container-queries", ], diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml index 03a09ac8c48..949263b34b7 100644 --- a/.github/workflows/build-desktop.yml +++ b/.github/workflows/build-desktop.yml @@ -186,7 +186,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -342,7 +342,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -490,7 +490,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -756,7 +756,7 @@ jobs: node-version: ${{ env._NODE_VERSION }} - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1007,7 +1007,7 @@ jobs: run: python3 -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1244,7 +1244,7 @@ jobs: run: python3 -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target @@ -1516,7 +1516,7 @@ jobs: run: python3 -m pip install setuptools - name: Cache Rust dependencies - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: | apps/desktop/desktop_native -> target diff --git a/.github/workflows/repository-management.yml b/.github/workflows/repository-management.yml index faf119cce2b..0a343be878c 100644 --- a/.github/workflows/repository-management.yml +++ b/.github/workflows/repository-management.yml @@ -29,7 +29,7 @@ on: default: false target_ref: default: "main" - description: "Branch/Tag to target for cut" + description: "Branch/Tag to target for cut (ignored if not cutting rc)" required: true type: string version_number_override: @@ -102,11 +102,12 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for committing and pushing to current branch - name: Check out branch uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 with: - ref: main + ref: ${{ github.ref }} token: ${{ steps.app-token.outputs.token }} persist-credentials: true @@ -467,6 +468,7 @@ jobs: with: app-id: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-ID }} private-key: ${{ steps.get-kv-secrets.outputs.BW-GHAPP-KEY }} + permission-contents: write # for creating and pushing new branch - name: Check out target ref uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5.0.1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f53bfc39d36..faee7220e7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -148,7 +148,7 @@ jobs: components: llvm-tools - name: Cache cargo registry - uses: Swatinem/rust-cache@f13886b937689c021905a6b90929199931d60db1 # v2.8.1 + uses: Swatinem/rust-cache@779680da715d629ac1d338a641029a2f4372abb5 # v2.8.2 with: workspaces: "apps/desktop/desktop_native -> target" diff --git a/.storybook/main.ts b/.storybook/main.ts index d3811bb178d..e1f3561a1b7 100644 --- a/.storybook/main.ts +++ b/.storybook/main.ts @@ -28,15 +28,13 @@ const config: StorybookConfig = { ], addons: [ getAbsolutePath("@storybook/addon-links"), - getAbsolutePath("@storybook/addon-essentials"), getAbsolutePath("@storybook/addon-a11y"), getAbsolutePath("@storybook/addon-designs"), - getAbsolutePath("@storybook/addon-interactions"), getAbsolutePath("@storybook/addon-themes"), { // @storybook/addon-docs is part of @storybook/addon-essentials - // eslint-disable-next-line storybook/no-uninstalled-addons - name: "@storybook/addon-docs", + + name: getAbsolutePath("@storybook/addon-docs"), options: { mdxPluginOptions: { mdxCompileOptions: { @@ -60,6 +58,10 @@ const config: StorybookConfig = { webpackFinal: async (config, { configType }) => { if (config.resolve) { config.resolve.plugins = [new TsconfigPathsPlugin()] as any; + config.resolve.fallback = { + ...config.resolve.fallback, + path: require.resolve("path-browserify"), + }; } return config; }, diff --git a/.storybook/manager.js b/.storybook/manager.js index e0ec04fd375..7ba923a0b2d 100644 --- a/.storybook/manager.js +++ b/.storybook/manager.js @@ -1,5 +1,5 @@ -import { addons } from "@storybook/manager-api"; -import { create } from "@storybook/theming/create"; +import { addons } from "storybook/manager-api"; +import { create } from "storybook/theming"; const lightTheme = create({ base: "light", diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index 0b14f9d7444..266cf79d8b1 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -28,7 +28,7 @@ const preview: Preview = { ], parameters: { a11y: { - element: "#storybook-root", + context: "#storybook-root", }, controls: { matchers: { @@ -49,7 +49,7 @@ const preview: Preview = { }, }, backgrounds: { - disable: true, + disabled: true, }, }, tags: ["autodocs"], diff --git a/angular.json b/angular.json index 87ee7dc57b2..e1cc2aad82a 100644 --- a/angular.json +++ b/angular.json @@ -220,5 +220,31 @@ } } } + }, + "schematics": { + "@schematics/angular:component": { + "type": "component" + }, + "@schematics/angular:directive": { + "type": "directive" + }, + "@schematics/angular:service": { + "type": "service" + }, + "@schematics/angular:guard": { + "typeSeparator": "." + }, + "@schematics/angular:interceptor": { + "typeSeparator": "." + }, + "@schematics/angular:module": { + "typeSeparator": "." + }, + "@schematics/angular:pipe": { + "typeSeparator": "." + }, + "@schematics/angular:resolver": { + "typeSeparator": "." + } } } diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 21149499485..6a7df1678bf 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -1475,6 +1475,15 @@ "ppremiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpEmergency": { "message": "Emergency access." }, diff --git a/apps/browser/src/autofill/background/abstractions/overlay.background.ts b/apps/browser/src/autofill/background/abstractions/overlay.background.ts index 96809fa26b2..c8c938a99b2 100644 --- a/apps/browser/src/autofill/background/abstractions/overlay.background.ts +++ b/apps/browser/src/autofill/background/abstractions/overlay.background.ts @@ -69,8 +69,8 @@ export type FieldRect = { }; export type InlineMenuPosition = { - button?: InlineMenuElementPosition; - list?: InlineMenuElementPosition; + button?: InlineMenuElementPosition | null; + list?: InlineMenuElementPosition | null; }; export type NewLoginCipherData = { diff --git a/apps/browser/src/autofill/background/overlay.background.ts b/apps/browser/src/autofill/background/overlay.background.ts index af8141f1ab8..04a53395130 100644 --- a/apps/browser/src/autofill/background/overlay.background.ts +++ b/apps/browser/src/autofill/background/overlay.background.ts @@ -1424,11 +1424,11 @@ export class OverlayBackground implements OverlayBackgroundInterface { } /** - * calculates the postion and width for multi-input totp field inline menu - * @param totpFieldArray - the totp fields used to evaluate the position of the menu + * calculates the position and width for multi-input TOTP field inline menu + * @param totpFieldArray - the TOTP fields used to evaluate the position of the menu */ private calculateTotpMultiInputMenuBounds(totpFieldArray: AutofillField[]) { - // Filter the fields based on the provided totpfields + // Filter the fields based on the provided TOTP fields const filteredObjects = this.allFieldData.filter((obj) => totpFieldArray.some((o) => o.opid === obj.opid), ); @@ -1451,8 +1451,8 @@ export class OverlayBackground implements OverlayBackgroundInterface { } /** - * calculates the postion for multi-input totp field inline button - * @param totpFieldArray - the totp fields used to evaluate the position of the menu + * calculates the position for multi-input TOTP field inline button + * @param totpFieldArray - the TOTP fields used to evaluate the position of the menu */ private calculateTotpMultiInputButtonBounds(totpFieldArray: AutofillField[]) { const filteredObjects = this.allFieldData.filter((obj) => diff --git a/apps/browser/src/autofill/content/components/.lit-storybook/main.ts b/apps/browser/src/autofill/content/components/.lit-storybook/main.ts index 53c06264672..4e37cf85244 100644 --- a/apps/browser/src/autofill/content/components/.lit-storybook/main.ts +++ b/apps/browser/src/autofill/content/components/.lit-storybook/main.ts @@ -1,13 +1,9 @@ import { createRequire } from "module"; -import { dirname, join, resolve } from "path"; -import { fileURLToPath } from "url"; +import { dirname, join } from "path"; -import type { StorybookConfig } from "@storybook/web-components-webpack5"; +import type { StorybookConfig } from "@storybook/web-components-vite"; import remarkGfm from "remark-gfm"; -import TsconfigPathsPlugin from "tsconfig-paths-webpack-plugin"; - -const currentFile = fileURLToPath(import.meta.url); -const currentDirectory = dirname(currentFile); +import tsconfigPaths from "vite-tsconfig-paths"; const require = createRequire(import.meta.url); @@ -18,10 +14,8 @@ const config: StorybookConfig = { stories: ["../lit-stories/**/*.lit-stories.@(js|jsx|ts|tsx)", "../lit-stories/**/*.mdx"], addons: [ getAbsolutePath("@storybook/addon-links"), - getAbsolutePath("@storybook/addon-essentials"), getAbsolutePath("@storybook/addon-a11y"), getAbsolutePath("@storybook/addon-designs"), - getAbsolutePath("@storybook/addon-interactions"), { name: "@storybook/addon-docs", options: { @@ -34,10 +28,8 @@ const config: StorybookConfig = { }, ], framework: { - name: getAbsolutePath("@storybook/web-components-webpack5"), - options: { - legacyRootApi: true, - }, + name: getAbsolutePath("@storybook/web-components-vite"), + options: {}, }, core: { disableTelemetry: true, @@ -46,33 +38,12 @@ const config: StorybookConfig = { ...existingConfig, FLAGS: JSON.stringify({}), }), - webpackFinal: async (config) => { - if (config.resolve) { - config.resolve.plugins = [ - new TsconfigPathsPlugin({ - configFile: resolve(currentDirectory, "../../../../../tsconfig.json"), - }), - ] as any; - } - - if (config.module && config.module.rules) { - config.module.rules.push({ - test: /\.(ts|tsx)$/, - exclude: /node_modules/, - use: [ - { - loader: require.resolve("ts-loader"), - }, - ], - }); - config.module.rules.push({ - test: /\.scss$/, - use: [require.resolve("css-loader"), require.resolve("sass-loader")], - }); - } - return config; + viteFinal: async (config) => { + return { + ...config, + plugins: [...(config.plugins ?? []), tsconfigPaths()], + }; }, - docs: {}, }; export default config; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx index fcec5bb7a82..8193ea01ee5 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/action-button.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./action-button.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx index b5ea41b283c..6a01540b51b 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/badge-button.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./badge-button.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx index a298594e17f..f36cb74d684 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/body.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./body.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx index 03a7b72001a..0728e44c7b2 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/close-button.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./close-button.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx index a5a791ffbe1..dbe93663726 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/edit-button.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./edit-button.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx index 6a816f811e0..a717b34cc33 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/footer.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./footer.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx index ebe35a3dd9b..376c6c6889f 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/header.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls, Primary } from "@storybook/addon-docs"; +import { Meta, Controls, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./header.lit-stories"; diff --git a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx index 7ec18d0f7bb..e9b328e2967 100644 --- a/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx +++ b/apps/browser/src/autofill/content/components/lit-stories/.lit-docs/icons.mdx @@ -1,4 +1,4 @@ -import { Meta, Controls } from "@storybook/addon-docs"; +import { Meta, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./icons.lit-stories"; diff --git a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts index b341be28ebb..6ad069ad56e 100644 --- a/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/abstractions/fido2.background.ts @@ -13,7 +13,6 @@ type SharedFido2ScriptRegistrationOptions = SharedFido2ScriptInjectionDetails & matches: string[]; excludeMatches: string[]; allFrames: true; - world?: "MAIN" | "ISOLATED"; }; type Fido2ExtensionMessage = { diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts index 76ad78a6cd8..752851b3d37 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.spec.ts @@ -203,7 +203,6 @@ describe("Fido2Background", () => { { file: Fido2ContentScript.PageScriptDelayAppend }, { file: Fido2ContentScript.ContentScript }, ], - world: "ISOLATED", ...sharedRegistrationOptions, }); }); diff --git a/apps/browser/src/autofill/fido2/background/fido2.background.ts b/apps/browser/src/autofill/fido2/background/fido2.background.ts index 0ee7a43767f..22ee4a1822d 100644 --- a/apps/browser/src/autofill/fido2/background/fido2.background.ts +++ b/apps/browser/src/autofill/fido2/background/fido2.background.ts @@ -176,7 +176,6 @@ export class Fido2Background implements Fido2BackgroundInterface { { file: await this.getFido2PageScriptAppendFileName() }, { file: Fido2ContentScript.ContentScript }, ], - world: "ISOLATED", ...this.sharedRegistrationOptions, }); } diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts index 0b10841e390..b444c967080 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script-append.mv2.spec.ts @@ -29,48 +29,38 @@ describe("FIDO2 page-script for manifest v2", () => { expect(window.document.createElement).not.toHaveBeenCalled(); }); - it("appends the `page-script.js` file to the document head when the contentType is `text/html`", async () => { - const scriptContents = "test-script-contents"; + it("appends the `page-script.js` file to the document head when the contentType is `text/html`", () => { jest.spyOn(window.document.head, "prepend").mockImplementation((node) => { createdScriptElement = node as HTMLScriptElement; return node; }); - window.fetch = jest.fn().mockResolvedValue({ - text: () => Promise.resolve(scriptContents), - } as Response); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports require("./fido2-page-script-delay-append.mv2.ts"); - await jest.runAllTimersAsync(); expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(window.document.head.prepend).toHaveBeenCalledWith(expect.any(HTMLScriptElement)); - expect(createdScriptElement.innerHTML).toBe(scriptContents); + expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); }); - it("appends the `page-script.js` file to the document element if the head is not available", async () => { - const scriptContents = "test-script-contents"; + it("appends the `page-script.js` file to the document element if the head is not available", () => { window.document.documentElement.removeChild(window.document.head); jest.spyOn(window.document.documentElement, "prepend").mockImplementation((node) => { createdScriptElement = node as HTMLScriptElement; return node; }); - window.fetch = jest.fn().mockResolvedValue({ - text: () => Promise.resolve(scriptContents), - } as Response); // FIXME: Remove when updating file. Eslint update // eslint-disable-next-line @typescript-eslint/no-require-imports require("./fido2-page-script-delay-append.mv2.ts"); - await jest.runAllTimersAsync(); expect(window.document.createElement).toHaveBeenCalledWith("script"); expect(chrome.runtime.getURL).toHaveBeenCalledWith(Fido2ContentScript.PageScript); expect(window.document.documentElement.prepend).toHaveBeenCalledWith( expect.any(HTMLScriptElement), ); - expect(createdScriptElement.innerHTML).toBe(scriptContents); + expect(createdScriptElement.src).toBe(`chrome-extension://id/${Fido2ContentScript.PageScript}`); }); }); diff --git a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts index 8c0d17c7e21..ffa6f7051c3 100644 --- a/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts +++ b/apps/browser/src/autofill/fido2/content/fido2-page-script-delay-append.mv2.ts @@ -2,26 +2,17 @@ * This script handles injection of the FIDO2 override page script into the document. * This is required for manifest v2, but will be removed when we migrate fully to manifest v3. */ -void (async function (globalContext) { +(function (globalContext) { if (globalContext.document.contentType !== "text/html") { return; } const script = globalContext.document.createElement("script"); + // We're removing stack trace information in the page script instead + // eslint-disable-next-line @bitwarden/platform/no-page-script-url-leakage + script.src = chrome.runtime.getURL("content/fido2-page-script.js"); script.async = false; - const pageScriptUrl = chrome.runtime.getURL("content/fido2-page-script.js"); - // Inject the script contents directly to avoid leaking the extension URL - try { - const response = await fetch(pageScriptUrl); - const scriptContents = await response.text(); - script.innerHTML = scriptContents; - } catch { - // eslint-disable-next-line no-console - console.error("Failed to load FIDO2 page script contents. Injection failed."); - return; - } - // We are ensuring that the script injection is delayed in the event that we are loading // within an iframe element. This prevents an issue with web mail clients that load content // using ajax within iframes. In particular, Zimbra web mail client was observed to have this issue. diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts index 3bb86ee7876..f1ed6875f90 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.spec.ts @@ -1,7 +1,7 @@ import { mock } from "jest-mock-extended"; import { EVENTS } from "@bitwarden/common/autofill/constants"; -import { ThemeType } from "@bitwarden/common/platform/enums"; +import { ThemeTypes } from "@bitwarden/common/platform/enums"; import { AutofillOverlayPort } from "../../../enums/autofill-overlay.enum"; import { createPortSpyMock } from "../../../spec/autofill-mocks"; @@ -66,17 +66,38 @@ describe("AutofillInlineMenuIframeService", () => { ); }); - // TODO CG - This test is brittle and failing due to how we are calling the private method. This needs to be reworked - it.skip("creates an aria alert element if the ariaAlert param is passed", () => { - const ariaAlert = "aria alert"; + it("creates an aria alert element if the ariaAlert param is passed to AutofillInlineMenuIframeService", () => { jest.spyOn(autofillInlineMenuIframeService as any, "createAriaAlertElement"); autofillInlineMenuIframeService.initMenuIframe(); - expect(autofillInlineMenuIframeService["createAriaAlertElement"]).toHaveBeenCalledWith( - ariaAlert, + expect(autofillInlineMenuIframeService["createAriaAlertElement"]).toHaveBeenCalled(); + expect(autofillInlineMenuIframeService["ariaAlertElement"]).toBeDefined(); + expect(autofillInlineMenuIframeService["ariaAlertElement"].getAttribute("role")).toBe( + "alert", ); - expect(autofillInlineMenuIframeService["ariaAlertElement"]).toMatchSnapshot(); + expect(autofillInlineMenuIframeService["ariaAlertElement"].getAttribute("aria-live")).toBe( + "polite", + ); + expect(autofillInlineMenuIframeService["ariaAlertElement"].getAttribute("aria-atomic")).toBe( + "true", + ); + }); + + it("does not create an aria alert element if the ariaAlert param is not passed to AutofillInlineMenuIframeService", () => { + const shadowWithoutAlert = document.createElement("div").attachShadow({ mode: "open" }); + const serviceWithoutAlert = new AutofillInlineMenuIframeService( + shadowWithoutAlert, + AutofillOverlayPort.Button, + { height: "0px" }, + "title", + ); + jest.spyOn(serviceWithoutAlert as any, "createAriaAlertElement"); + + serviceWithoutAlert.initMenuIframe(); + + expect(serviceWithoutAlert["createAriaAlertElement"]).not.toHaveBeenCalled(); + expect(serviceWithoutAlert["ariaAlertElement"]).toBeUndefined(); }); describe("on load of the iframe source", () => { @@ -200,7 +221,7 @@ describe("AutofillInlineMenuIframeService", () => { sendPortMessage(portSpy, { command: "updateAutofillInlineMenuPosition" }); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).not.toHaveBeenCalled(); }); @@ -216,7 +237,7 @@ describe("AutofillInlineMenuIframeService", () => { expect(autofillInlineMenuIframeService["portKey"]).toBe(portKey); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]); }); }); @@ -234,14 +255,14 @@ describe("AutofillInlineMenuIframeService", () => { it("passes the message on to the iframe element", () => { const message = { command: "initAutofillInlineMenuList", - theme: ThemeType.Light, + theme: ThemeTypes.Light, }; sendPortMessage(portSpy, message); expect(updateElementStylesSpy).not.toHaveBeenCalled(); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).toHaveBeenCalledWith(message, autofillInlineMenuIframeService["extensionOrigin"]); }); @@ -249,18 +270,18 @@ describe("AutofillInlineMenuIframeService", () => { window.matchMedia = jest.fn(() => mock({ matches: false })); const message = { command: "initAutofillInlineMenuList", - theme: ThemeType.System, + theme: ThemeTypes.System, }; sendPortMessage(portSpy, message); expect(window.matchMedia).toHaveBeenCalledWith("(prefers-color-scheme: dark)"); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).toHaveBeenCalledWith( { command: "initAutofillInlineMenuList", - theme: ThemeType.Light, + theme: ThemeTypes.Light, }, autofillInlineMenuIframeService["extensionOrigin"], ); @@ -270,18 +291,18 @@ describe("AutofillInlineMenuIframeService", () => { window.matchMedia = jest.fn(() => mock({ matches: true })); const message = { command: "initAutofillInlineMenuList", - theme: ThemeType.System, + theme: ThemeTypes.System, }; sendPortMessage(portSpy, message); expect(window.matchMedia).toHaveBeenCalledWith("(prefers-color-scheme: dark)"); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).toHaveBeenCalledWith( { command: "initAutofillInlineMenuList", - theme: ThemeType.Dark, + theme: ThemeTypes.Dark, }, autofillInlineMenuIframeService["extensionOrigin"], ); @@ -290,7 +311,7 @@ describe("AutofillInlineMenuIframeService", () => { it("updates the border to match the `dark` theme", () => { const message = { command: "initAutofillInlineMenuList", - theme: ThemeType.Dark, + theme: ThemeTypes.Dark, }; sendPortMessage(portSpy, message); @@ -364,6 +385,219 @@ describe("AutofillInlineMenuIframeService", () => { autofillInlineMenuIframeService["handleFadeInInlineMenuIframe"], ).toHaveBeenCalled(); }); + + it("closes the inline menu when iframe is outside the viewport (bottom)", () => { + const viewportHeight = 800; + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: 0, + left: 0, + right: 100, + bottom: viewportHeight + 1, + height: 98, + width: 262, + } as DOMRect); + Object.defineProperty(globalThis.window, "innerHeight", { + value: viewportHeight, + writable: true, + configurable: true, + }); + Object.defineProperty(globalThis.window, "innerWidth", { + value: 1200, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); + + it("closes the inline menu when iframe is outside the viewport (right)", () => { + const viewportWidth = 1200; + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: 0, + left: 0, + right: viewportWidth + 1, + bottom: 100, + height: 98, + width: 262, + } as DOMRect); + Object.defineProperty(globalThis.window, "innerHeight", { + value: 800, + writable: true, + configurable: true, + }); + Object.defineProperty(globalThis.window, "innerWidth", { + value: viewportWidth, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); + + it("closes the inline menu when iframe is outside the viewport (left)", () => { + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: 0, + left: -1, + right: 0, + bottom: 100, + height: 98, + width: 262, + } as DOMRect); + Object.defineProperty(globalThis.window, "innerHeight", { + value: 800, + writable: true, + configurable: true, + }); + Object.defineProperty(globalThis.window, "innerWidth", { + value: 1200, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); + + it("closes the inline menu when iframe is outside the viewport (top)", () => { + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: -1, + left: 0, + right: 100, + bottom: 0, + height: 98, + width: 262, + } as DOMRect); + Object.defineProperty(globalThis.window, "innerHeight", { + value: 800, + writable: true, + configurable: true, + }); + Object.defineProperty(globalThis.window, "innerWidth", { + value: 1200, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); + + it("allows iframe (do not close) when it has no dimensions", () => { + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: 0, + left: 0, + right: 0, + bottom: 0, + height: 0, + width: 0, + } as DOMRect); + + Object.defineProperty(globalThis.window, "innerHeight", { + value: 800, + writable: true, + configurable: true, + }); + + Object.defineProperty(globalThis.window, "innerWidth", { + value: 1200, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).not.toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); + + it("uses visualViewport when available", () => { + jest.spyOn(globalThis.document, "hasFocus").mockReturnValue(true); + jest + .spyOn(autofillInlineMenuIframeService["iframe"], "getBoundingClientRect") + .mockReturnValue({ + top: 0, + left: 0, + right: 100, + bottom: 700, + height: 98, + width: 262, + } as DOMRect); + + Object.defineProperty(globalThis.window, "visualViewport", { + value: { + height: 600, + width: 1200, + } as VisualViewport, + writable: true, + configurable: true, + }); + + Object.defineProperty(globalThis.window, "innerHeight", { + value: 800, + writable: true, + configurable: true, + }); + + Object.defineProperty(globalThis.window, "innerWidth", { + value: 1200, + writable: true, + configurable: true, + }); + + sendPortMessage(portSpy, { + command: "updateAutofillInlineMenuPosition", + styles: {}, + }); + + expect(sendExtensionMessageSpy).toHaveBeenCalledWith("closeAutofillInlineMenu", { + forceCloseInlineMenu: true, + }); + }); }); it("updates the visibility of the iframe", () => { @@ -381,7 +615,7 @@ describe("AutofillInlineMenuIframeService", () => { }); expect( - autofillInlineMenuIframeService["iframe"].contentWindow.postMessage, + autofillInlineMenuIframeService["iframe"].contentWindow?.postMessage, ).toHaveBeenCalledWith( { command: "updateAutofillInlineMenuColorScheme", diff --git a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts index 8b1423b1290..64ef7d180ed 100644 --- a/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts +++ b/apps/browser/src/autofill/overlay/inline-menu/iframe-content/autofill-inline-menu-iframe.service.ts @@ -282,6 +282,15 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe const styles = this.fadeInTimeout ? Object.assign(position, { opacity: "0" }) : position; this.updateElementStyles(this.iframe, styles); + const elementHeightCompletelyInViewport = this.isElementCompletelyWithinViewport( + this.iframe.getBoundingClientRect(), + ); + + if (!elementHeightCompletelyInViewport) { + this.forceCloseInlineMenu(); + return; + } + if (this.fadeInTimeout) { this.handleFadeInInlineMenuIframe(); } @@ -289,6 +298,42 @@ export class AutofillInlineMenuIframeService implements AutofillInlineMenuIframe this.announceAriaAlert(this.ariaAlert, 2000); } + /** + * Check if element is completely within the browser viewport. + */ + private isElementCompletelyWithinViewport(elementPosition: DOMRect) { + // An element that lacks size should be considered within the viewport + if (!elementPosition.height || !elementPosition.width) { + return true; + } + + const [viewportHeight, viewportWidth] = this.getViewportSize(); + + const rightSideIsWithinViewport = (elementPosition.right || 0) <= viewportWidth; + const leftSideIsWithinViewport = (elementPosition.left || 0) >= 0; + const topSideIsWithinViewport = (elementPosition.top || 0) >= 0; + const bottomSideIsWithinViewport = (elementPosition.bottom || 0) <= viewportHeight; + + return ( + rightSideIsWithinViewport && + leftSideIsWithinViewport && + topSideIsWithinViewport && + bottomSideIsWithinViewport + ); + } + + /** Use Visual Viewport API if available (better for mobile/zoom) */ + private getViewportSize(): [ + VisualViewport["height"] | Window["innerHeight"], + VisualViewport["width"] | Window["innerWidth"], + ] { + if ("visualViewport" in globalThis.window && globalThis.window.visualViewport) { + return [globalThis.window.visualViewport.height, globalThis.window.visualViewport.width]; + } + + return [globalThis.window.innerHeight, globalThis.window.innerWidth]; + } + /** * Gets the page color scheme meta tag and sends a message to the iframe * to update its color scheme. Will default to "normal" if the meta tag diff --git a/apps/browser/src/autofill/services/collect-autofill-content.service.ts b/apps/browser/src/autofill/services/collect-autofill-content.service.ts index 6f2c00a4dd4..367599f7ad0 100644 --- a/apps/browser/src/autofill/services/collect-autofill-content.service.ts +++ b/apps/browser/src/autofill/services/collect-autofill-content.service.ts @@ -1400,7 +1400,7 @@ export class CollectAutofillContentService implements CollectAutofillContentServ this.intersectionObserver = new IntersectionObserver(this.handleFormElementIntersection, { root: null, rootMargin: "0px", - threshold: 1.0, + threshold: 0.9999, // Safari doesn't seem to function properly with a threshold of 1, }); } diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.html b/apps/browser/src/billing/popup/settings/premium-v2.component.html index 47d72751af3..fea3e558057 100644 --- a/apps/browser/src/billing/popup/settings/premium-v2.component.html +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.html @@ -12,7 +12,7 @@
  • - {{ "ppremiumSignUpStorage" | i18n }} + {{ "premiumSignUpStorageV2" | i18n: `${storageProvidedGb} GB` }}
  • {{ "premiumSignUpTwoStepOptions" | i18n }} diff --git a/apps/browser/src/billing/popup/settings/premium-v2.component.ts b/apps/browser/src/billing/popup/settings/premium-v2.component.ts index b858b74242d..0c246d734e5 100644 --- a/apps/browser/src/billing/popup/settings/premium-v2.component.ts +++ b/apps/browser/src/billing/popup/settings/premium-v2.component.ts @@ -1,13 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule, CurrencyPipe, Location } from "@angular/common"; -import { Component } from "@angular/core"; +import { Component, OnInit } from "@angular/core"; import { RouterModule } from "@angular/router"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/billing/components/premium.component"; import { JslibModule } from "@bitwarden/angular/jslib.module"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -44,7 +45,7 @@ import { PopupPageComponent } from "../../../platform/popup/layout/popup-page.co SectionComponent, ], }) -export class PremiumV2Component extends BasePremiumComponent { +export class PremiumV2Component extends BasePremiumComponent implements OnInit { priceString: string; constructor( @@ -59,6 +60,7 @@ export class PremiumV2Component extends BasePremiumComponent { billingAccountProfileStateService: BillingAccountProfileStateService, toastService: ToastService, accountService: AccountService, + billingApiService: BillingApiServiceAbstraction, ) { super( i18nService, @@ -70,15 +72,18 @@ export class PremiumV2Component extends BasePremiumComponent { billingAccountProfileStateService, toastService, accountService, + billingApiService, ); - + } + async ngOnInit() { + await super.ngOnInit(); // Support old price string. Can be removed in future once all translations are properly updated. const thePrice = this.currencyPipe.transform(this.price, "$"); // Safari extension crashes due to $1 appearing in the price string ($10.00). Escape the $ to fix. const formattedPrice = this.platformUtilsService.isSafari() ? thePrice.replace("$", "$$$") : thePrice; - this.priceString = i18nService.t("premiumPriceV2", formattedPrice); + this.priceString = this.i18nService.t("premiumPriceV2", formattedPrice); if (this.priceString.indexOf("%price%") > -1) { this.priceString = this.priceString.replace("%price%", thePrice); } diff --git a/apps/browser/src/platform/popup/layout/popup-layout.mdx b/apps/browser/src/platform/popup/layout/popup-layout.mdx index a2725350a8f..0a1c80b13f6 100644 --- a/apps/browser/src/platform/popup/layout/popup-layout.mdx +++ b/apps/browser/src/platform/popup/layout/popup-layout.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./popup-layout.stories"; diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 91b6f0ff105..f16d82d0810 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -1,5 +1,4 @@ -import { DOCUMENT } from "@angular/common"; -import { inject, Inject, Injectable } from "@angular/core"; +import { inject, Inject, Injectable, DOCUMENT } from "@angular/core"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; import { TwoFactorService } from "@bitwarden/common/auth/two-factor"; diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts index 1bffcd9ad51..f2c9d470816 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.spec.ts @@ -381,4 +381,88 @@ describe("AddEditV2Component", () => { expect(navigate).toHaveBeenCalledWith(["/tabs/vault"]); }); }); + + describe("reloadAddEditCipherData", () => { + beforeEach(fakeAsync(() => { + addEditCipherInfo$.next({ + cipher: { + name: "InitialName", + type: CipherType.Login, + login: { + password: "initialPassword", + username: "initialUsername", + uris: [{ uri: "https://initial.com" }], + }, + }, + } as AddEditCipherInfo); + queryParams$.next({}); + tick(); + + cipherServiceMock.setAddEditCipherInfo.mockClear(); + })); + + it("replaces all initialValues with new data, clearing stale fields", fakeAsync(() => { + const newCipherInfo = { + cipher: { + name: "UpdatedName", + type: CipherType.Login, + login: { + password: "updatedPassword", + uris: [{ uri: "https://updated.com" }], + }, + }, + } as AddEditCipherInfo; + + addEditCipherInfo$.next(newCipherInfo); + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(component.config.initialValues).toEqual({ + name: "UpdatedName", + password: "updatedPassword", + loginUri: "https://updated.com", + } as OptionalInitialValues); + + expect(cipherServiceMock.setAddEditCipherInfo).toHaveBeenCalledWith(null, "UserId"); + })); + + it("does not reload data if config is not set", fakeAsync(() => { + component.config = null; + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(cipherServiceMock.setAddEditCipherInfo).not.toHaveBeenCalled(); + })); + + it("does not reload data if latestCipherInfo is null", fakeAsync(() => { + addEditCipherInfo$.next(null); + + const messageListener = component["messageListener"]; + messageListener({ command: "reloadAddEditCipherData" }); + tick(); + + expect(component.config.initialValues).toEqual({ + name: "InitialName", + password: "initialPassword", + username: "initialUsername", + loginUri: "https://initial.com", + } as OptionalInitialValues); + + expect(cipherServiceMock.setAddEditCipherInfo).not.toHaveBeenCalled(); + })); + + it("ignores messages with different commands", fakeAsync(() => { + const initialValues = component.config.initialValues; + + const messageListener = component["messageListener"]; + messageListener({ command: "someOtherCommand" }); + tick(); + + expect(component.config.initialValues).toBe(initialValues); + })); + }); }); diff --git a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts index 60e44cefbdf..22aad854dd0 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/add-edit/add-edit-v2.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component, OnInit } from "@angular/core"; +import { Component, OnInit, OnDestroy } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { FormsModule } from "@angular/forms"; import { ActivatedRoute, Params, Router } from "@angular/router"; @@ -158,7 +158,7 @@ export type AddEditQueryParams = Partial>; IconButtonModule, ], }) -export class AddEditV2Component implements OnInit { +export class AddEditV2Component implements OnInit, OnDestroy { headerText: string; config: CipherFormConfig; canDeleteCipher$: Observable; @@ -200,12 +200,58 @@ export class AddEditV2Component implements OnInit { this.subscribeToParams(); } + private messageListener: (message: any) => void; + async ngOnInit() { this.fido2PopoutSessionData = await firstValueFrom(this.fido2PopoutSessionData$); if (BrowserPopupUtils.inPopout(window)) { this.popupCloseWarningService.enable(); } + + // Listen for messages to reload cipher data when the pop up is already open + this.messageListener = async (message: any) => { + if (message?.command === "reloadAddEditCipherData") { + try { + await this.reloadCipherData(); + } catch (error) { + this.logService.error("Failed to reload cipher data", error); + } + } + }; + BrowserApi.addListener(chrome.runtime.onMessage, this.messageListener); + } + + ngOnDestroy() { + if (this.messageListener) { + BrowserApi.removeListener(chrome.runtime.onMessage, this.messageListener); + } + } + + /** + * Reloads the cipher data when the popup is already open and new form data is submitted. + * This completely replaces the initialValues to clear any stale data from the previous submission. + */ + private async reloadCipherData() { + if (!this.config) { + return; + } + + const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + + const latestCipherInfo = await firstValueFrom( + this.cipherService.addEditCipherInfo$(activeUserId), + ); + + if (latestCipherInfo != null) { + this.config = { + ...this.config, + initialValues: mapAddEditCipherInfoToInitialValues(latestCipherInfo), + }; + + // Be sure to clear the "cached" cipher info, so it doesn't get used again + await this.cipherService.setAddEditCipherInfo(null, activeUserId); + } } /** diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.spec.ts new file mode 100644 index 00000000000..84ce58913a9 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.spec.ts @@ -0,0 +1,425 @@ +import { CommonModule } from "@angular/common"; +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { of } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + CipherViewLike, + CipherViewLikeUtils, +} from "@bitwarden/common/vault/utils/cipher-view-like-utils"; +import { IconButtonModule, ItemModule, MenuModule } from "@bitwarden/components"; +import { CipherListView, CopyableCipherFields } from "@bitwarden/sdk-internal"; + +import { VaultPopupCopyButtonsService } from "../../../services/vault-popup-copy-buttons.service"; + +import { ItemCopyActionsComponent } from "./item-copy-actions.component"; + +describe("ItemCopyActionsComponent", () => { + let fixture: ComponentFixture; + let component: ItemCopyActionsComponent; + + let i18nService: jest.Mocked; + + beforeEach(async () => { + i18nService = { + t: jest.fn((key: string) => `translated-${key}`), + } as unknown as jest.Mocked; + + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + JslibModule, + ItemModule, + IconButtonModule, + MenuModule, + ItemCopyActionsComponent, // standalone + ], + providers: [ + { provide: I18nService, useValue: i18nService }, + { + provide: VaultPopupCopyButtonsService, + useValue: { + showQuickCopyActions$: of(true), + } satisfies Partial, + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(ItemCopyActionsComponent); + component = fixture.componentInstance; + + // Default cipher so tests can override as needed + component.cipher = { + name: "My cipher", + viewPassword: true, + login: { username: null, password: null, totp: null }, + card: { code: null, number: null }, + identity: { + fullAddressForCopy: null, + email: null, + username: null, + phone: null, + }, + sshKey: { + privateKey: null, + publicKey: null, + keyFingerprint: null, + }, + notes: null, + copyableFields: [], + } as unknown as CipherViewLike; + }); + + afterEach(() => { + jest.restoreAllMocks(); + }); + + describe("findSingleCopyableItem", () => { + beforeEach(() => { + jest + .spyOn(CipherViewLikeUtils, "hasCopyableValue") + .mockImplementation( + (cipher: CipherViewLike & { __copyable?: Record }, field) => { + return Boolean(cipher.__copyable?.[field]); + }, + ); + }); + + it("returns the single item with value and translates its key", () => { + const items = [ + { key: "copyUsername", field: "username" as const }, + { key: "copyPassword", field: "password" as const }, + ]; + + (component.cipher as any).__copyable = { + username: true, + password: false, + }; + + const result = component.findSingleCopyableItem(items); + + expect(result).toEqual({ + key: "translated-copyUsername", + field: "username", + }); + expect(i18nService.t).toHaveBeenCalledWith("copyUsername"); + }); + + it("returns null when no items have a value", () => { + const items = [ + { key: "copyUsername", field: "username" as const }, + { key: "copyPassword", field: "password" as const }, + ]; + + (component.cipher as any).__copyable = { + username: false, + password: false, + }; + + const result = component.findSingleCopyableItem(items); + + expect(result).toBeNull(); + }); + + it("returns null when more than one item has a value", () => { + const items = [ + { key: "copyUsername", field: "username" as const }, + { key: "copyPassword", field: "password" as const }, + ]; + + (component.cipher as any).__copyable = { + username: true, + password: true, + }; + + const result = component.findSingleCopyableItem(items); + + expect(result).toBeNull(); + }); + }); + + describe("singleCopyableLogin", () => { + beforeEach(() => { + jest + .spyOn(CipherViewLikeUtils, "hasCopyableValue") + .mockImplementation( + (cipher: CipherViewLike & { __copyable?: Record }, field) => { + return Boolean(cipher.__copyable?.[field]); + }, + ); + }); + + it("returns username with special-case logic when password is hidden and both username/password exist and no totp", () => { + (component.cipher as CipherView).viewPassword = false; + + (component.cipher as any).__copyable = { + username: true, + password: true, + totp: false, + }; + + const result = component.singleCopyableLogin; + + expect(result).toEqual({ + key: "translated-copyUsername", + field: "username", + }); + expect(i18nService.t).toHaveBeenCalledWith("copyUsername"); + }); + + it("returns null when password is hidden but multiple fields exist, ensuring username and totp are shown in the menu UI ", () => { + (component.cipher as CipherView).viewPassword = false; + + (component.cipher as any).__copyable = { + username: true, + password: true, + totp: true, + }; + + const result = component.singleCopyableLogin; + + expect(result).toBeNull(); + }); + + it("falls back to findSingleCopyableItem when password is visible", () => { + const findSingleCopyableItemSpy = jest.spyOn(component, "findSingleCopyableItem"); + (component.cipher as CipherView).viewPassword = true; + + void component.singleCopyableLogin; + expect(findSingleCopyableItemSpy).toHaveBeenCalled(); + }); + }); + + describe("singleCopyableCard", () => { + beforeEach(() => { + jest + .spyOn(CipherViewLikeUtils, "hasCopyableValue") + .mockImplementation( + (cipher: CipherViewLike & { __copyable?: Record }, field) => { + return Boolean(cipher.__copyable?.[field]); + }, + ); + }); + + it("returns security code when it is the only available card value", () => { + (component.cipher as any).__copyable = { + securityCode: true, + cardNumber: false, + }; + + const result = component.singleCopyableCard; + + expect(result).toEqual({ + key: "translated-securityCode", + field: "securityCode", + }); + expect(i18nService.t).toHaveBeenCalledWith("securityCode"); + }); + + it("returns null when both card number and security code are available", () => { + (component.cipher as any).__copyable = { + securityCode: true, + cardNumber: true, + }; + + const result = component.singleCopyableCard; + + expect(result).toBeNull(); + }); + }); + + describe("singleCopyableIdentity", () => { + beforeEach(() => { + jest + .spyOn(CipherViewLikeUtils, "hasCopyableValue") + .mockImplementation( + (cipher: CipherViewLike & { __copyable?: Record }, field) => { + return Boolean(cipher.__copyable?.[field]); + }, + ); + }); + + it("returns the only copyable identity field", () => { + (component.cipher as any).__copyable = { + address: false, + email: true, + username: false, + phone: false, + }; + + const result = component.singleCopyableIdentity; + + expect(result).toEqual({ + key: "translated-email", + field: "email", + }); + expect(i18nService.t).toHaveBeenCalledWith("email"); + }); + + it("returns null when multiple identity fields are available", () => { + (component.cipher as any).__copyable = { + address: true, + email: true, + username: false, + phone: false, + }; + + const result = component.singleCopyableIdentity; + + expect(result).toBeNull(); + }); + }); + + describe("has*Values in non-list view", () => { + beforeEach(() => { + jest.spyOn(CipherViewLikeUtils, "isCipherListView").mockReturnValue(false); + }); + + it("computes hasLoginValues from login fields", () => { + (component.cipher as CipherView).login = { + username: "user", + password: null, + totp: null, + } as any; + + expect(component.hasLoginValues).toBe(true); + + (component.cipher as CipherView).login = { + username: null, + password: null, + totp: null, + } as any; + + expect(component.hasLoginValues).toBe(false); + }); + + it("computes hasCardValues from card fields", () => { + (component.cipher as CipherView).card = { code: "123", number: null } as any; + + expect(component.hasCardValues).toBe(true); + + (component.cipher as CipherView).card = { code: null, number: null } as any; + + expect(component.hasCardValues).toBe(false); + }); + + it("computes hasIdentityValues from identity fields", () => { + (component.cipher as CipherView).identity = { + fullAddressForCopy: null, + email: "test@example.com", + username: null, + phone: null, + } as any; + + expect(component.hasIdentityValues).toBe(true); + + (component.cipher as CipherView).identity = { + fullAddressForCopy: null, + email: null, + username: null, + phone: null, + } as any; + + expect(component.hasIdentityValues).toBe(false); + }); + + it("computes hasSecureNoteValue from notes", () => { + (component.cipher as CipherView).notes = "Some note" as any; + expect(component.hasSecureNoteValue).toBe(true); + + (component.cipher as CipherView).notes = null as any; + expect(component.hasSecureNoteValue).toBe(false); + }); + + it("computes hasSshKeyValues from sshKey fields", () => { + (component.cipher as CipherView).sshKey = { + privateKey: "priv", + publicKey: null, + keyFingerprint: null, + } as any; + + expect(component.hasSshKeyValues).toBe(true); + + (component.cipher as CipherView).sshKey = { + privateKey: null, + publicKey: null, + keyFingerprint: null, + } as any; + + expect(component.hasSshKeyValues).toBe(false); + }); + }); + + describe("has*Values in list view", () => { + beforeEach(() => { + jest.spyOn(CipherViewLikeUtils, "isCipherListView").mockReturnValue(true); + }); + + it("uses copyableFields for login values", () => { + (component.cipher as CipherListView).copyableFields = [ + "LoginUsername", + "CardNumber", + ] as CopyableCipherFields[]; + + expect(component.hasLoginValues).toBe(true); + + (component.cipher as CipherListView).copyableFields = [ + "CardNumber", + ] as CopyableCipherFields[]; + + expect(component.hasLoginValues).toBe(false); + }); + + it("uses copyableFields for card values", () => { + (component.cipher as CipherListView).copyableFields = [ + "CardSecurityCode", + ] as CopyableCipherFields[]; + + expect(component.hasCardValues).toBe(true); + + (component.cipher as CipherListView).copyableFields = [ + "LoginUsername", + ] as CopyableCipherFields[]; + + expect(component.hasCardValues).toBe(false); + }); + + it("uses copyableFields for identity values", () => { + (component.cipher as CipherListView).copyableFields = [ + "IdentityEmail", + ] as CopyableCipherFields[]; + + expect(component.hasIdentityValues).toBe(true); + + (component.cipher as CipherListView).copyableFields = [ + "LoginUsername", + ] as CopyableCipherFields[]; + + expect(component.hasIdentityValues).toBe(false); + }); + + it("uses copyableFields for secure note value", () => { + (component.cipher as CipherListView).copyableFields = [ + "SecureNotes", + ] as CopyableCipherFields[]; + expect(component.hasSecureNoteValue).toBe(true); + + (component.cipher as CipherListView).copyableFields = [ + "LoginUsername", + ] as CopyableCipherFields[]; + expect(component.hasSecureNoteValue).toBe(false); + }); + + it("uses copyableFields for ssh key values", () => { + (component.cipher as CipherListView).copyableFields = ["SshKey"] as CopyableCipherFields[]; + expect(component.hasSshKeyValues).toBe(true); + + (component.cipher as CipherListView).copyableFields = [ + "LoginUsername", + ] as CopyableCipherFields[]; + expect(component.hasSshKeyValues).toBe(false); + }); + }); +}); diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts index e24db60a55a..e1398ac9f1f 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/item-copy-action/item-copy-actions.component.ts @@ -54,17 +54,20 @@ export class ItemCopyActionsComponent { { key: "copyPassword", field: "password" }, { key: "copyVerificationCode", field: "totp" }, ]; - // If both the password and username are visible but the password is hidden, return the username + // If both the password and username are visible but the password is hidden and there's no + // totp code to copy return the username if ( !this.cipher.viewPassword && CipherViewLikeUtils.hasCopyableValue(this.cipher, "username") && - CipherViewLikeUtils.hasCopyableValue(this.cipher, "password") + CipherViewLikeUtils.hasCopyableValue(this.cipher, "password") && + !CipherViewLikeUtils.hasCopyableValue(this.cipher, "totp") ) { return { key: this.i18nService.t("copyUsername"), field: "username" as const, }; } + return this.findSingleCopyableItem(loginItems); } diff --git a/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts b/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts index 4597c004290..3389228dda4 100644 --- a/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts +++ b/apps/browser/src/vault/popup/utils/vault-popout-window.spec.ts @@ -2,6 +2,7 @@ import { mock } from "jest-mock-extended"; import { CipherType } from "@bitwarden/common/vault/enums"; +import { BrowserApi } from "../../../platform/browser/browser-api"; import BrowserPopupUtils from "../../../platform/browser/browser-popup-utils"; import { @@ -23,6 +24,19 @@ describe("VaultPopoutWindow", () => { .spyOn(BrowserPopupUtils, "closeSingleActionPopout") .mockImplementation(); + beforeEach(() => { + jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([]); + jest.spyOn(BrowserApi, "updateWindowProperties").mockResolvedValue(); + global.chrome = { + ...global.chrome, + runtime: { + ...global.chrome?.runtime, + sendMessage: jest.fn().mockResolvedValue(undefined), + getURL: jest.fn((path) => `chrome-extension://extension-id/${path}`), + }, + }; + }); + afterEach(() => { jest.clearAllMocks(); }); @@ -123,6 +137,32 @@ describe("VaultPopoutWindow", () => { }, ); }); + + it("sends a message to refresh data when the popup is already open", async () => { + const existingPopupTab = { + id: 123, + windowId: 456, + url: `chrome-extension://extension-id/popup/index.html#/edit-cipher?singleActionPopout=${VaultPopoutType.addEditVaultItem}_${CipherType.Login}`, + } as chrome.tabs.Tab; + + jest.spyOn(BrowserApi, "tabsQuery").mockResolvedValue([existingPopupTab]); + const sendMessageSpy = jest.spyOn(chrome.runtime, "sendMessage"); + const updateWindowSpy = jest.spyOn(BrowserApi, "updateWindowProperties"); + + await openAddEditVaultItemPopout( + mock({ windowId: 1, url: "https://jest-testing-website.com" }), + { + cipherType: CipherType.Login, + }, + ); + + expect(openPopoutSpy).not.toHaveBeenCalled(); + expect(sendMessageSpy).toHaveBeenCalledWith({ + command: "reloadAddEditCipherData", + data: { cipherId: undefined, cipherType: CipherType.Login }, + }); + expect(updateWindowSpy).toHaveBeenCalledWith(456, { focused: true }); + }); }); describe("closeAddEditVaultItemPopout", () => { diff --git a/apps/browser/src/vault/popup/utils/vault-popout-window.ts b/apps/browser/src/vault/popup/utils/vault-popout-window.ts index 3dae96b6cc7..cccf005cd2e 100644 --- a/apps/browser/src/vault/popup/utils/vault-popout-window.ts +++ b/apps/browser/src/vault/popup/utils/vault-popout-window.ts @@ -115,10 +115,26 @@ async function openAddEditVaultItemPopout( addEditCipherUrl += formatQueryString("uri", url); } - await BrowserPopupUtils.openPopout(addEditCipherUrl, { - singleActionKey, - senderWindowId: windowId, - }); + const extensionUrl = chrome.runtime.getURL("popup/index.html"); + const existingPopupTabs = await BrowserApi.tabsQuery({ url: `${extensionUrl}*` }); + const existingPopup = existingPopupTabs.find((tab) => + tab.url?.includes(`singleActionPopout=${singleActionKey}`), + ); + // Check if the an existing popup is already open + try { + await chrome.runtime.sendMessage({ + command: "reloadAddEditCipherData", + data: { cipherId, cipherType }, + }); + await BrowserApi.updateWindowProperties(existingPopup.windowId, { + focused: true, + }); + } catch { + await BrowserPopupUtils.openPopout(addEditCipherUrl, { + singleActionKey, + senderWindowId: windowId, + }); + } } /** diff --git a/apps/cli/package.json b/apps/cli/package.json index adddc99b4d7..d041f818c29 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -88,7 +88,7 @@ "proper-lockfile": "4.1.2", "rxjs": "7.8.1", "semver": "7.7.3", - "tldts": "7.0.18", + "tldts": "7.0.19", "zxcvbn": "4.4.2" } } diff --git a/apps/cli/src/program.ts b/apps/cli/src/program.ts index a5f12b34035..a47278db089 100644 --- a/apps/cli/src/program.ts +++ b/apps/cli/src/program.ts @@ -277,6 +277,11 @@ export class Program extends BaseProgram { }) .option("--check", "Check lock status.", async () => { await this.exitIfNotAuthed(); + const userId = (await firstValueFrom(this.serviceContainer.accountService.activeAccount$)) + ?.id; + await this.serviceContainer.userAutoUnlockKeyService.setUserKeyInMemoryIfAutoUserKeySet( + userId, + ); const authStatus = await this.serviceContainer.authService.getAuthStatus(); if (authStatus === AuthenticationStatus.Unlocked) { diff --git a/apps/desktop/desktop_native/Cargo.lock b/apps/desktop/desktop_native/Cargo.lock index f6380c747d8..3b9b8c2db27 100644 --- a/apps/desktop/desktop_native/Cargo.lock +++ b/apps/desktop/desktop_native/Cargo.lock @@ -1863,9 +1863,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" dependencies = [ "cfg-if", "downcast", @@ -1877,9 +1877,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" dependencies = [ "cfg-if", "proc-macro2", diff --git a/apps/desktop/desktop_native/autotype/Cargo.toml b/apps/desktop/desktop_native/autotype/Cargo.toml index 267074d0bc8..580df30e72d 100644 --- a/apps/desktop/desktop_native/autotype/Cargo.toml +++ b/apps/desktop/desktop_native/autotype/Cargo.toml @@ -9,7 +9,7 @@ publish.workspace = true anyhow = { workspace = true } [target.'cfg(windows)'.dependencies] -mockall = "=0.13.1" +mockall = "=0.14.0" serial_test = "=3.2.0" tracing.workspace = true windows = { workspace = true, features = [ diff --git a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs index 10f30f5ee4f..b2f4c6b82df 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/type_input.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/type_input.rs @@ -272,6 +272,7 @@ mod tests { #[serial] fn send_input_succeeds() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 1); send_input::(vec![build_unicode_input( @@ -279,6 +280,8 @@ mod tests { 0, )]) .unwrap(); + + drop(ctxi); } #[test] @@ -288,9 +291,11 @@ mod tests { )] fn send_input_fails_sent_zero() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 0); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); send_input::(vec![build_unicode_input( @@ -298,6 +303,9 @@ mod tests { 0, )]) .unwrap(); + + drop(ctxge); + drop(ctxi); } #[test] @@ -305,9 +313,11 @@ mod tests { #[should_panic(expected = "SendInput does not match expected. sent: 2, expected: 1")] fn send_input_fails_sent_mismatch() { let ctxi = MockInputOperations::send_input_context(); + ctxi.checkpoint(); ctxi.expect().returning(|_| 2); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); send_input::(vec![build_unicode_input( @@ -315,5 +325,8 @@ mod tests { 0, )]) .unwrap(); + + drop(ctxge); + drop(ctxi); } } diff --git a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs index d56a811ab5c..4fc0b3bb3ad 100644 --- a/apps/desktop/desktop_native/autotype/src/windows/window_title.rs +++ b/apps/desktop/desktop_native/autotype/src/windows/window_title.rs @@ -186,6 +186,7 @@ mod tests { let mut mock_handle = MockWindowHandleOperations::new(); let ctxse = MockErrorOperations::set_last_error_context(); + ctxse.checkpoint(); ctxse .expect() .once() @@ -198,6 +199,7 @@ mod tests { .returning(|| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(0)); let len = get_window_title_length::( @@ -206,6 +208,9 @@ mod tests { .unwrap(); assert_eq!(len, 0); + + drop(ctxge); + drop(ctxse); } #[test] @@ -215,6 +220,7 @@ mod tests { let mut mock_handle = MockWindowHandleOperations::new(); let ctxse = MockErrorOperations::set_last_error_context(); + ctxse.checkpoint(); ctxse.expect().with(predicate::eq(0)).returning(|_| {}); mock_handle @@ -223,13 +229,18 @@ mod tests { .returning(|| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); get_window_title_length::(&mock_handle) .unwrap(); + + drop(ctxge); + drop(ctxse); } #[test] + #[serial] fn get_window_title_succeeds() { let mut mock_handle = MockWindowHandleOperations::new(); @@ -246,11 +257,11 @@ mod tests { .unwrap(); assert_eq!(title.len(), 43); // That extra slot in the buffer for null char - assert_eq!(title, "*******************************************"); } #[test] + #[serial] fn get_window_title_returns_empty_string() { let mock_handle = MockWindowHandleOperations::new(); @@ -273,10 +284,13 @@ mod tests { .returning(|_| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(1)); get_window_title::(&mock_handle, 42) .unwrap(); + + drop(ctxge); } #[test] @@ -290,9 +304,12 @@ mod tests { .returning(|_| Ok(0)); let ctxge = MockErrorOperations::get_last_error_context(); + ctxge.checkpoint(); ctxge.expect().returning(|| WIN32_ERROR(0)); get_window_title::(&mock_handle, 42) .unwrap(); + + drop(ctxge); } } diff --git a/apps/desktop/src/app/app-routing.module.ts b/apps/desktop/src/app/app-routing.module.ts index b6e86ba19ff..8fab7df1cd8 100644 --- a/apps/desktop/src/app/app-routing.module.ts +++ b/apps/desktop/src/app/app-routing.module.ts @@ -14,6 +14,7 @@ import { } from "@bitwarden/angular/auth/guards"; import { ChangePasswordComponent } from "@bitwarden/angular/auth/password-management/change-password"; import { SetInitialPasswordComponent } from "@bitwarden/angular/auth/password-management/set-initial-password/set-initial-password.component"; +import { canAccessFeature } from "@bitwarden/angular/platform/guard/feature-flag.guard"; import { DevicesIcon, RegistrationUserAddIcon, @@ -39,15 +40,19 @@ import { TwoFactorAuthGuard, NewDeviceVerificationComponent, } from "@bitwarden/auth/angular"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { AnonLayoutWrapperComponent, AnonLayoutWrapperData } from "@bitwarden/components"; import { LockComponent, ConfirmKeyConnectorDomainComponent } from "@bitwarden/key-management-ui"; import { maxAccountsGuardFn } from "../auth/guards/max-accounts.guard"; import { RemovePasswordComponent } from "../key-management/key-connector/remove-password.component"; import { VaultV2Component } from "../vault/app/vault/vault-v2.component"; +import { VaultComponent } from "../vault/app/vault-v3/vault.component"; import { Fido2PlaceholderComponent } from "./components/fido2placeholder.component"; +import { DesktopLayoutComponent } from "./layout/desktop-layout.component"; import { SendComponent } from "./tools/send/send.component"; +import { SendV2Component } from "./tools/send-v2/send-v2.component"; /** * Data properties acceptable for use in route objects in the desktop @@ -99,7 +104,10 @@ const routes: Routes = [ { path: "vault", component: VaultV2Component, - canActivate: [authGuard], + canActivate: [ + authGuard, + canAccessFeature(FeatureFlag.DesktopUiMigrationMilestone1, false, "new-vault", false), + ], }, { path: "send", @@ -325,6 +333,21 @@ const routes: Routes = [ }, ], }, + { + path: "", + component: DesktopLayoutComponent, + canActivate: [authGuard], + children: [ + { + path: "new-vault", + component: VaultComponent, + }, + { + path: "new-sends", + component: SendV2Component, + }, + ], + }, ]; @NgModule({ diff --git a/apps/desktop/src/app/layout/desktop-layout.component.html b/apps/desktop/src/app/layout/desktop-layout.component.html new file mode 100644 index 00000000000..94b9201ae21 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.html @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/apps/desktop/src/app/layout/desktop-layout.component.spec.ts b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts new file mode 100644 index 00000000000..cc2f7e58dfb --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.spec.ts @@ -0,0 +1,61 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { RouterModule } from "@angular/router"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { NavigationModule } from "@bitwarden/components"; + +import { DesktopLayoutComponent } from "./desktop-layout.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopLayoutComponent", () => { + let component: DesktopLayoutComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopLayoutComponent, RouterModule.forRoot([]), NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopLayoutComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-layout component", () => { + const compiled = fixture.nativeElement; + const layoutElement = compiled.querySelector("bit-layout"); + + expect(layoutElement).toBeTruthy(); + }); + + it("supports content projection for side-nav", () => { + const compiled = fixture.nativeElement; + const ngContent = compiled.querySelectorAll("ng-content"); + + expect(ngContent).toBeTruthy(); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-layout.component.ts b/apps/desktop/src/app/layout/desktop-layout.component.ts new file mode 100644 index 00000000000..006055f475f --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-layout.component.ts @@ -0,0 +1,19 @@ +import { Component } from "@angular/core"; +import { RouterModule } from "@angular/router"; + +import { PasswordManagerLogo } from "@bitwarden/assets/svg"; +import { LayoutComponent, NavigationModule } from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; + +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-layout", + imports: [RouterModule, I18nPipe, LayoutComponent, NavigationModule, DesktopSideNavComponent], + templateUrl: "./desktop-layout.component.html", +}) +export class DesktopLayoutComponent { + protected readonly logo = PasswordManagerLogo; +} diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.html b/apps/desktop/src/app/layout/desktop-side-nav.component.html new file mode 100644 index 00000000000..ede3f9131b7 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.html @@ -0,0 +1,3 @@ + + + diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts new file mode 100644 index 00000000000..4d5c3a90253 --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.spec.ts @@ -0,0 +1,74 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock } from "jest-mock-extended"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { NavigationModule } from "@bitwarden/components"; + +import { DesktopSideNavComponent } from "./desktop-side-nav.component"; + +Object.defineProperty(window, "matchMedia", { + writable: true, + value: jest.fn().mockImplementation((query) => ({ + matches: true, + media: query, + onchange: null, + addListener: jest.fn(), + removeListener: jest.fn(), + addEventListener: jest.fn(), + removeEventListener: jest.fn(), + dispatchEvent: jest.fn(), + })), +}); + +describe("DesktopSideNavComponent", () => { + let component: DesktopSideNavComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DesktopSideNavComponent, NavigationModule], + providers: [ + { + provide: I18nService, + useValue: mock(), + }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(DesktopSideNavComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("renders bit-side-nav component", () => { + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement).toBeTruthy(); + }); + + it("uses primary variant by default", () => { + expect(component.variant()).toBe("primary"); + }); + + it("accepts variant input", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + expect(component.variant()).toBe("secondary"); + }); + + it.skip("passes variant to bit-side-nav", () => { + fixture.componentRef.setInput("variant", "secondary"); + fixture.detectChanges(); + + const compiled = fixture.nativeElement; + const sideNavElement = compiled.querySelector("bit-side-nav"); + + expect(sideNavElement.getAttribute("ng-reflect-variant")).toBe("secondary"); + }); +}); diff --git a/apps/desktop/src/app/layout/desktop-side-nav.component.ts b/apps/desktop/src/app/layout/desktop-side-nav.component.ts new file mode 100644 index 00000000000..b0d9fd16fcc --- /dev/null +++ b/apps/desktop/src/app/layout/desktop-side-nav.component.ts @@ -0,0 +1,14 @@ +import { CommonModule } from "@angular/common"; +import { ChangeDetectionStrategy, Component, input } from "@angular/core"; + +import { NavigationModule, SideNavVariant } from "@bitwarden/components"; + +@Component({ + selector: "app-side-nav", + templateUrl: "desktop-side-nav.component.html", + imports: [CommonModule, NavigationModule], + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class DesktopSideNavComponent { + readonly variant = input("primary"); +} diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index 73c4d38d3b2..17115825bf6 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -1,5 +1,4 @@ -import { DOCUMENT } from "@angular/common"; -import { Inject, Injectable } from "@angular/core"; +import { Inject, Injectable, DOCUMENT } from "@angular/core"; import { firstValueFrom } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.html b/apps/desktop/src/app/tools/send-v2/send-v2.component.html new file mode 100644 index 00000000000..20cac15138a --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.html @@ -0,0 +1,110 @@ +
    +
    +
    +
    + +
    +
    + + + +

    {{ "noItemsInList" | i18n }}

    +
    +
    + +
    +
    + + +
    diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts new file mode 100644 index 00000000000..5798df0989d --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.spec.ts @@ -0,0 +1,364 @@ +import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { mock, MockProxy } from "jest-mock-extended"; +import { BehaviorSubject, of } from "rxjs"; + +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SendType } from "@bitwarden/common/tools/send/enums/send-type"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { DialogService, ToastService } from "@bitwarden/components"; + +import * as utils from "../../../utils"; +import { SearchBarService } from "../../layout/search/search-bar.service"; +import { AddEditComponent } from "../send/add-edit.component"; + +import { SendV2Component } from "./send-v2.component"; + +// Mock the invokeMenu utility function +jest.mock("../../../utils", () => ({ + invokeMenu: jest.fn(), +})); + +describe("SendV2Component", () => { + let component: SendV2Component; + let fixture: ComponentFixture; + let sendService: MockProxy; + let searchBarService: MockProxy; + let broadcasterService: MockProxy; + let accountService: MockProxy; + let policyService: MockProxy; + + beforeEach(async () => { + sendService = mock(); + searchBarService = mock(); + broadcasterService = mock(); + accountService = mock(); + policyService = mock(); + + // Mock sendViews$ observable + sendService.sendViews$ = of([]); + searchBarService.searchText$ = new BehaviorSubject(""); + + // Mock activeAccount$ observable for parent class ngOnInit + accountService.activeAccount$ = of({ id: "test-user-id" } as any); + policyService.policyAppliesToUser$ = jest.fn().mockReturnValue(of(false)); + + await TestBed.configureTestingModule({ + imports: [SendV2Component], + providers: [ + { provide: SendService, useValue: sendService }, + { provide: I18nService, useValue: mock() }, + { provide: PlatformUtilsService, useValue: mock() }, + { provide: EnvironmentService, useValue: mock() }, + { provide: BroadcasterService, useValue: broadcasterService }, + { provide: SearchService, useValue: mock() }, + { provide: PolicyService, useValue: policyService }, + { provide: SearchBarService, useValue: searchBarService }, + { provide: LogService, useValue: mock() }, + { provide: SendApiService, useValue: mock() }, + { provide: DialogService, useValue: mock() }, + { provide: ToastService, useValue: mock() }, + { provide: AccountService, useValue: accountService }, + ], + }).compileComponents(); + + fixture = TestBed.createComponent(SendV2Component); + component = fixture.componentInstance; + }); + + it("creates component", () => { + expect(component).toBeTruthy(); + }); + + it("initializes with correct default action", () => { + expect(component.action).toBe(""); + }); + + it("subscribes to broadcaster service on init", async () => { + await component.ngOnInit(); + expect(broadcasterService.subscribe).toHaveBeenCalledWith( + "SendV2Component", + expect.any(Function), + ); + }); + + it("unsubscribes from broadcaster service on destroy", () => { + component.ngOnDestroy(); + expect(broadcasterService.unsubscribe).toHaveBeenCalledWith("SendV2Component"); + }); + + it("enables search bar on init", async () => { + await component.ngOnInit(); + expect(searchBarService.setEnabled).toHaveBeenCalledWith(true); + }); + + it("disables search bar on destroy", () => { + component.ngOnDestroy(); + expect(searchBarService.setEnabled).toHaveBeenCalledWith(false); + }); + + describe("addSend", () => { + it("sets action to Add", async () => { + await component.addSend(); + expect(component.action).toBe("add"); + }); + + it("calls resetAndLoad on addEditComponent when component exists", async () => { + const mockAddEdit = mock(); + component.addEditComponent = mockAddEdit; + + await component.addSend(); + + expect(mockAddEdit.resetAndLoad).toHaveBeenCalled(); + }); + + it("does not throw when addEditComponent is null", async () => { + component.addEditComponent = null; + await expect(component.addSend()).resolves.not.toThrow(); + }); + }); + + describe("cancel", () => { + it("resets action to None", () => { + component.action = "edit"; + component.sendId = "test-id"; + + component.cancel(new SendView()); + + expect(component.action).toBe(""); + expect(component.sendId).toBeNull(); + }); + }); + + describe("deletedSend", () => { + it("refreshes the list and resets action and sendId", async () => { + component.action = "edit"; + component.sendId = "test-id"; + jest.spyOn(component, "refresh").mockResolvedValue(); + + const mockSend = new SendView(); + await component.deletedSend(mockSend); + + expect(component.refresh).toHaveBeenCalled(); + expect(component.action).toBe(""); + expect(component.sendId).toBeNull(); + }); + }); + + describe("savedSend", () => { + it("refreshes the list and selects the saved send", async () => { + jest.spyOn(component, "refresh").mockResolvedValue(); + jest.spyOn(component, "selectSend").mockResolvedValue(); + + const mockSend = new SendView(); + mockSend.id = "saved-send-id"; + + await component.savedSend(mockSend); + + expect(component.refresh).toHaveBeenCalled(); + expect(component.selectSend).toHaveBeenCalledWith("saved-send-id"); + }); + }); + + describe("selectSend", () => { + it("sets action to Edit and updates sendId", async () => { + await component.selectSend("new-send-id"); + + expect(component.action).toBe("edit"); + expect(component.sendId).toBe("new-send-id"); + }); + + it("updates addEditComponent when it exists", async () => { + const mockAddEdit = mock(); + component.addEditComponent = mockAddEdit; + + await component.selectSend("test-send-id"); + + expect(mockAddEdit.sendId).toBe("test-send-id"); + expect(mockAddEdit.refresh).toHaveBeenCalled(); + }); + + it("does not reload if same send is already selected in edit mode", async () => { + const mockAddEdit = mock(); + component.addEditComponent = mockAddEdit; + component.sendId = "same-id"; + component.action = "edit"; + + await component.selectSend("same-id"); + + expect(mockAddEdit.refresh).not.toHaveBeenCalled(); + }); + + it("reloads if selecting different send", async () => { + const mockAddEdit = mock(); + component.addEditComponent = mockAddEdit; + component.sendId = "old-id"; + component.action = "edit"; + + await component.selectSend("new-id"); + + expect(mockAddEdit.refresh).toHaveBeenCalled(); + }); + }); + + describe("selectedSendType", () => { + it("returns the type of the currently selected send", () => { + const mockSend1 = new SendView(); + mockSend1.id = "send-1"; + mockSend1.type = SendType.Text; + + const mockSend2 = new SendView(); + mockSend2.id = "send-2"; + mockSend2.type = SendType.File; + + component.sends = [mockSend1, mockSend2]; + component.sendId = "send-2"; + + expect(component.selectedSendType).toBe(SendType.File); + }); + + it("returns undefined when no send is selected", () => { + component.sends = []; + component.sendId = "non-existent"; + + expect(component.selectedSendType).toBeUndefined(); + }); + + it("returns undefined when sendId is null", () => { + const mockSend = new SendView(); + mockSend.id = "send-1"; + mockSend.type = SendType.Text; + + component.sends = [mockSend]; + component.sendId = null; + + expect(component.selectedSendType).toBeUndefined(); + }); + }); + + describe("viewSendMenu", () => { + let mockSend: SendView; + + beforeEach(() => { + mockSend = new SendView(); + mockSend.id = "test-send"; + mockSend.name = "Test Send"; + jest.clearAllMocks(); + }); + + it("creates menu with copy link option", () => { + jest.spyOn(component, "copy").mockResolvedValue(); + + component.viewSendMenu(mockSend); + + expect(utils.invokeMenu).toHaveBeenCalled(); + const menuItems = (utils.invokeMenu as jest.Mock).mock.calls[0][0]; + expect(menuItems.length).toBeGreaterThanOrEqual(2); // At minimum: copy link + delete + }); + + it("includes remove password option when send has password and is not disabled", () => { + mockSend.password = "test-password"; + mockSend.disabled = false; + jest.spyOn(component, "removePassword").mockResolvedValue(true); + + component.viewSendMenu(mockSend); + + expect(utils.invokeMenu).toHaveBeenCalled(); + const menuItems = (utils.invokeMenu as jest.Mock).mock.calls[0][0]; + expect(menuItems.length).toBe(3); // copy link + remove password + delete + }); + + it("excludes remove password option when send has no password", () => { + mockSend.password = null; + mockSend.disabled = false; + + component.viewSendMenu(mockSend); + + expect(utils.invokeMenu).toHaveBeenCalled(); + const menuItems = (utils.invokeMenu as jest.Mock).mock.calls[0][0]; + expect(menuItems.length).toBe(2); // copy link + delete (no remove password) + }); + + it("excludes remove password option when send is disabled", () => { + mockSend.password = "test-password"; + mockSend.disabled = true; + + component.viewSendMenu(mockSend); + + expect(utils.invokeMenu).toHaveBeenCalled(); + const menuItems = (utils.invokeMenu as jest.Mock).mock.calls[0][0]; + expect(menuItems.length).toBe(2); // copy link + delete (no remove password) + }); + + it("always includes delete option", () => { + jest.spyOn(component, "delete").mockResolvedValue(true); + jest.spyOn(component, "deletedSend").mockResolvedValue(); + + component.viewSendMenu(mockSend); + + expect(utils.invokeMenu).toHaveBeenCalled(); + const menuItems = (utils.invokeMenu as jest.Mock).mock.calls[0][0]; + // Delete is always the last item in the menu + expect(menuItems.length).toBeGreaterThan(0); + expect(menuItems[menuItems.length - 1]).toHaveProperty("label"); + expect(menuItems[menuItems.length - 1]).toHaveProperty("click"); + }); + }); + + describe("search bar subscription", () => { + it("updates searchText when search bar text changes", () => { + const searchSubject = new BehaviorSubject("initial"); + searchBarService.searchText$ = searchSubject; + + // Create new component to trigger constructor subscription + fixture = TestBed.createComponent(SendV2Component); + component = fixture.componentInstance; + + searchSubject.next("new search text"); + + expect(component.searchText).toBe("new search text"); + }); + }); + + describe("load", () => { + it("sets loading states correctly", async () => { + jest.spyOn(component, "search").mockResolvedValue(); + jest.spyOn(component, "selectAll"); + + expect(component.loaded).toBeFalsy(); + + await component.load(); + + expect(component.loading).toBe(false); + expect(component.loaded).toBe(true); + }); + + it("calls selectAll when onSuccessfulLoad is not set", async () => { + jest.spyOn(component, "search").mockResolvedValue(); + jest.spyOn(component, "selectAll"); + component.onSuccessfulLoad = null; + + await component.load(); + + expect(component.selectAll).toHaveBeenCalled(); + }); + + it("calls onSuccessfulLoad when it is set", async () => { + jest.spyOn(component, "search").mockResolvedValue(); + const mockCallback = jest.fn().mockResolvedValue(undefined); + component.onSuccessfulLoad = mockCallback; + + await component.load(); + + expect(mockCallback).toHaveBeenCalled(); + }); + }); +}); diff --git a/apps/desktop/src/app/tools/send-v2/send-v2.component.ts b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts new file mode 100644 index 00000000000..4afe02d9f98 --- /dev/null +++ b/apps/desktop/src/app/tools/send-v2/send-v2.component.ts @@ -0,0 +1,233 @@ +// FIXME: Update this file to be type safe and remove this and next line +// @ts-strict-ignore +import { CommonModule } from "@angular/common"; +import { Component, OnInit, OnDestroy, ViewChild, NgZone, ChangeDetectorRef } from "@angular/core"; +import { FormsModule } from "@angular/forms"; +import { mergeMap } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { SendComponent as BaseSendComponent } from "@bitwarden/angular/tools/send/send.component"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SendView } from "@bitwarden/common/tools/send/models/view/send.view"; +import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction"; +import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction"; +import { SearchService } from "@bitwarden/common/vault/abstractions/search.service"; +import { DialogService, ToastService } from "@bitwarden/components"; + +import { invokeMenu, RendererMenuItem } from "../../../utils"; +import { SearchBarService } from "../../layout/search/search-bar.service"; +import { AddEditComponent } from "../send/add-edit.component"; + +const Action = Object.freeze({ + /** No action is currently active. */ + None: "", + /** The user is adding a new Send. */ + Add: "add", + /** The user is editing an existing Send. */ + Edit: "edit", +} as const); + +type Action = (typeof Action)[keyof typeof Action]; + +const BroadcasterSubscriptionId = "SendV2Component"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-send-v2", + imports: [CommonModule, JslibModule, FormsModule, AddEditComponent], + templateUrl: "./send-v2.component.html", +}) +export class SendV2Component extends BaseSendComponent implements OnInit, OnDestroy { + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(AddEditComponent) addEditComponent: AddEditComponent; + + // The ID of the currently selected Send item being viewed or edited + sendId: string; + + // Tracks the current UI state: viewing list (None), adding new Send (Add), or editing existing Send (Edit) + action: Action = Action.None; + + constructor( + sendService: SendService, + i18nService: I18nService, + platformUtilsService: PlatformUtilsService, + environmentService: EnvironmentService, + private broadcasterService: BroadcasterService, + ngZone: NgZone, + searchService: SearchService, + policyService: PolicyService, + private searchBarService: SearchBarService, + logService: LogService, + sendApiService: SendApiService, + dialogService: DialogService, + toastService: ToastService, + accountService: AccountService, + private cdr: ChangeDetectorRef, + ) { + super( + sendService, + i18nService, + platformUtilsService, + environmentService, + ngZone, + searchService, + policyService, + logService, + sendApiService, + dialogService, + toastService, + accountService, + ); + + // Listen to search bar changes and update the Send list filter + // eslint-disable-next-line rxjs-angular/prefer-takeuntil + this.searchBarService.searchText$.subscribe((searchText) => { + this.searchText = searchText; + this.searchTextChanged(); + setTimeout(() => this.cdr.detectChanges(), 250); + }); + } + + // Initialize the component: enable search bar, subscribe to sync events, and load Send items + async ngOnInit() { + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t("searchSends")); + + await super.ngOnInit(); + + // Listen for sync completion events to refresh the Send list + this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.ngZone.run(async () => { + switch (message.command) { + case "syncCompleted": + await this.load(); + break; + } + }); + }); + await this.load(); + } + + // Clean up subscriptions and disable search bar when component is destroyed + ngOnDestroy() { + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.searchBarService.setEnabled(false); + } + + // Load Send items from the service and display them in the list. + // Subscribes to sendViews$ observable to get updates when Sends change. + // Manually triggers change detection to ensure UI updates immediately. + // Note: The filter parameter is ignored in this implementation for desktop-specific behavior. + async load(filter: (send: SendView) => boolean = null) { + this.loading = true; + this.sendService.sendViews$ + .pipe( + mergeMap(async (sends) => { + this.sends = sends; + await this.search(null); + // Trigger change detection after data updates + this.cdr.detectChanges(); + }), + ) + // eslint-disable-next-line rxjs-angular/prefer-takeuntil + .subscribe(); + if (this.onSuccessfulLoad != null) { + await this.onSuccessfulLoad(); + } else { + // Default action + this.selectAll(); + } + this.loading = false; + this.loaded = true; + } + + // Open the add Send form to create a new Send item + async addSend() { + this.action = Action.Add; + if (this.addEditComponent != null) { + await this.addEditComponent.resetAndLoad(); + } + } + + // Close the add/edit form and return to the list view + cancel(s: SendView) { + this.action = Action.None; + this.sendId = null; + } + + // Handle when a Send is deleted: refresh the list and close the edit form + async deletedSend(s: SendView) { + await this.refresh(); + this.action = Action.None; + this.sendId = null; + } + + // Handle when a Send is saved: refresh the list and re-select the saved Send + async savedSend(s: SendView) { + await this.refresh(); + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.selectSend(s.id); + } + + // Select a Send from the list and open it in the edit form. + // If the same Send is already selected and in edit mode, do nothing to avoid unnecessary reloads. + async selectSend(sendId: string) { + if (sendId === this.sendId && this.action === Action.Edit) { + return; + } + this.action = Action.Edit; + this.sendId = sendId; + if (this.addEditComponent != null) { + this.addEditComponent.sendId = sendId; + await this.addEditComponent.refresh(); + } + } + + // Get the type (text or file) of the currently selected Send for the edit form + get selectedSendType() { + return this.sends.find((s) => s.id === this.sendId)?.type; + } + + // Show the right-click context menu for a Send with options to copy link, remove password, or delete + viewSendMenu(send: SendView) { + const menu: RendererMenuItem[] = []; + menu.push({ + label: this.i18nService.t("copyLink"), + click: () => this.copy(send), + }); + if (send.password && !send.disabled) { + menu.push({ + label: this.i18nService.t("removePassword"), + click: async () => { + await this.removePassword(send); + if (this.sendId === send.id) { + this.sendId = null; + // FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling. + // eslint-disable-next-line @typescript-eslint/no-floating-promises + this.selectSend(send.id); + } + }, + }); + } + menu.push({ + label: this.i18nService.t("delete"), + click: async () => { + await this.delete(send); + await this.deletedSend(send); + }, + }); + + invokeMenu(menu); + } +} diff --git a/apps/desktop/src/billing/app/accounts/premium.component.html b/apps/desktop/src/billing/app/accounts/premium.component.html index d88602bed1e..c5f9722f133 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.html +++ b/apps/desktop/src/billing/app/accounts/premium.component.html @@ -13,7 +13,7 @@
    • - {{ "premiumSignUpStorage" | i18n }} + {{ "premiumSignUpStorageV2" | i18n: `${storageProvidedGb} GB` }}
    • diff --git a/apps/desktop/src/billing/app/accounts/premium.component.ts b/apps/desktop/src/billing/app/accounts/premium.component.ts index 637969c1a21..4aff0cc03e1 100644 --- a/apps/desktop/src/billing/app/accounts/premium.component.ts +++ b/apps/desktop/src/billing/app/accounts/premium.component.ts @@ -3,6 +3,7 @@ import { Component } from "@angular/core"; import { PremiumComponent as BasePremiumComponent } from "@bitwarden/angular/billing/components/premium.component"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -28,6 +29,7 @@ export class PremiumComponent extends BasePremiumComponent { billingAccountProfileStateService: BillingAccountProfileStateService, toastService: ToastService, accountService: AccountService, + billingApiService: BillingApiServiceAbstraction, ) { super( i18nService, @@ -39,6 +41,7 @@ export class PremiumComponent extends BasePremiumComponent { billingAccountProfileStateService, toastService, accountService, + billingApiService, ); } } diff --git a/apps/desktop/src/locales/en/messages.json b/apps/desktop/src/locales/en/messages.json index 6bef882d970..757059c4e41 100644 --- a/apps/desktop/src/locales/en/messages.json +++ b/apps/desktop/src/locales/en/messages.json @@ -1490,6 +1490,15 @@ "premiumSignUpStorage": { "message": "1 GB encrypted storage for file attachments." }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } + }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." }, @@ -2228,6 +2237,10 @@ "contactInfo": { "message": "Contact information" }, + "send": { + "message": "Send", + "description": "'Send' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." + }, "allSends": { "message": "All Sends", "description": "'Sends' is a noun and the name of a feature called 'Bitwarden Send'. It should not be translated." @@ -2991,7 +3004,8 @@ "message": "Are you sure you want to use the \"Never\" option? Setting your lock options to \"Never\" stores your vault's encryption key on your device. If you use this option you should ensure that you keep your device properly protected." }, "vault": { - "message": "Vault" + "message": "Vault", + "description": "'Vault' is a noun and refers to the Bitwarden Vault feature." }, "loginWithMasterPassword": { "message": "Log in with master password" diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.html b/apps/desktop/src/vault/app/vault-v3/vault.component.html new file mode 100644 index 00000000000..a9a25f57994 --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.html @@ -0,0 +1,70 @@ +
      + + +
      + +
      +
      +
      + + + + + + + +
      +
      +
      +
      + +
      + diff --git a/apps/desktop/src/vault/app/vault-v3/vault.component.ts b/apps/desktop/src/vault/app/vault-v3/vault.component.ts new file mode 100644 index 00000000000..21ba7547f8b --- /dev/null +++ b/apps/desktop/src/vault/app/vault-v3/vault.component.ts @@ -0,0 +1,1020 @@ +import { CommonModule } from "@angular/common"; +import { + ChangeDetectorRef, + Component, + NgZone, + OnDestroy, + OnInit, + ViewChild, + ViewContainerRef, +} from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { firstValueFrom, Subject, takeUntil, switchMap, lastValueFrom, Observable } from "rxjs"; +import { filter, map, take } from "rxjs/operators"; + +import { CollectionService, CollectionView } from "@bitwarden/admin-console/common"; +import { PremiumBadgeComponent } from "@bitwarden/angular/billing/components/premium-badge"; +import { VaultViewPasswordHistoryService } from "@bitwarden/angular/services/view-password-history.service"; +import { VaultFilter } from "@bitwarden/angular/vault/vault-filter/models/vault-filter.model"; +import { AuthRequestServiceAbstraction } from "@bitwarden/auth/common"; +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EventCollectionService } from "@bitwarden/common/abstractions/event/event-collection.service"; +import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; +import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { Account, AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { getUserId } from "@bitwarden/common/auth/services/account.service"; +import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; +import { EventType } from "@bitwarden/common/enums"; +import { BroadcasterService } from "@bitwarden/common/platform/abstractions/broadcaster.service"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { getByIds } from "@bitwarden/common/platform/misc"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; +import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; +import { PremiumUpgradePromptService } from "@bitwarden/common/vault/abstractions/premium-upgrade-prompt.service"; +import { TotpService } from "@bitwarden/common/vault/abstractions/totp.service"; +import { ViewPasswordHistoryService } from "@bitwarden/common/vault/abstractions/view-password-history.service"; +import { CipherType, toCipherType } from "@bitwarden/common/vault/enums"; +import { CipherRepromptType } from "@bitwarden/common/vault/enums/cipher-reprompt-type"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + CipherViewLike, + CipherViewLikeUtils, +} from "@bitwarden/common/vault/utils/cipher-view-like-utils"; +import { filterOutNullish } from "@bitwarden/common/vault/utils/observable-utilities"; +import { + BadgeModule, + ButtonModule, + DialogService, + ItemModule, + ToastService, + CopyClickListener, + COPY_CLICK_LISTENER, +} from "@bitwarden/components"; +import { I18nPipe } from "@bitwarden/ui-common"; +import { + AddEditFolderDialogComponent, + AddEditFolderDialogResult, + AttachmentDialogResult, + AttachmentsV2Component, + ChangeLoginPasswordService, + CipherFormConfig, + CipherFormConfigService, + CipherFormGenerationService, + CipherFormMode, + CipherFormModule, + CipherViewComponent, + CollectionAssignmentResult, + DecryptionFailureDialogComponent, + DefaultChangeLoginPasswordService, + DefaultCipherFormConfigService, + PasswordRepromptService, + CipherFormComponent, + ArchiveCipherUtilitiesService, +} from "@bitwarden/vault"; + +import { SearchBarService } from "../../../app/layout/search/search-bar.service"; +import { DesktopCredentialGenerationService } from "../../../services/desktop-cipher-form-generator.service"; +import { DesktopPremiumUpgradePromptService } from "../../../services/desktop-premium-upgrade-prompt.service"; +import { invokeMenu, RendererMenuItem } from "../../../utils"; +import { AssignCollectionsDesktopComponent } from "../vault/assign-collections"; +import { ItemFooterComponent } from "../vault/item-footer.component"; +import { VaultFilterComponent } from "../vault/vault-filter/vault-filter.component"; +import { VaultFilterModule } from "../vault/vault-filter/vault-filter.module"; +import { VaultItemsV2Component } from "../vault/vault-items-v2.component"; + +const BroadcasterSubscriptionId = "VaultComponent"; + +// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush +// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +@Component({ + selector: "app-vault-v3", + templateUrl: "vault.component.html", + imports: [ + BadgeModule, + CommonModule, + CipherFormModule, + CipherViewComponent, + ItemFooterComponent, + I18nPipe, + ItemModule, + ButtonModule, + PremiumBadgeComponent, + VaultFilterModule, + VaultItemsV2Component, + ], + providers: [ + { + provide: CipherFormConfigService, + useClass: DefaultCipherFormConfigService, + }, + { + provide: ChangeLoginPasswordService, + useClass: DefaultChangeLoginPasswordService, + }, + { + provide: ViewPasswordHistoryService, + useClass: VaultViewPasswordHistoryService, + }, + { + provide: PremiumUpgradePromptService, + useClass: DesktopPremiumUpgradePromptService, + }, + { provide: CipherFormGenerationService, useClass: DesktopCredentialGenerationService }, + { + provide: COPY_CLICK_LISTENER, + useExisting: VaultComponent, + }, + ], +}) +export class VaultComponent + implements OnInit, OnDestroy, CopyClickListener +{ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(VaultItemsV2Component, { static: true }) + vaultItemsComponent: VaultItemsV2Component | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(VaultFilterComponent, { static: true }) + vaultFilterComponent: VaultFilterComponent | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild("folderAddEdit", { read: ViewContainerRef, static: true }) + folderAddEditModalRef: ViewContainerRef | null = null; + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-signals + @ViewChild(CipherFormComponent) + cipherFormComponent: CipherFormComponent | null = null; + + action: CipherFormMode | "view" | null = null; + cipherId: string | null = null; + favorites = false; + type: CipherType | null = null; + folderId: string | null = null; + collectionId: string | null = null; + organizationId: string | null = null; + myVaultOnly = false; + addType: CipherType | undefined = undefined; + addOrganizationId: string | null = null; + addCollectionIds: string[] | null = null; + showingModal = false; + deleted = false; + userHasPremiumAccess = false; + activeFilter: VaultFilter = new VaultFilter(); + activeUserId: UserId | null = null; + cipherRepromptId: string | null = null; + cipher: CipherView | null = new CipherView(); + collections: CollectionView[] | null = null; + config: CipherFormConfig | null = null; + + /** Tracks the disabled status of the edit cipher form */ + protected formDisabled: boolean = false; + + private organizations$: Observable = this.accountService.activeAccount$.pipe( + map((a) => a?.id), + filterOutNullish(), + switchMap((id) => this.organizationService.organizations$(id)), + ); + + protected canAccessAttachments$ = this.accountService.activeAccount$.pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + ); + + private componentIsDestroyed$ = new Subject(); + private allOrganizations: Organization[] = []; + private allCollections: CollectionView[] = []; + + constructor( + private route: ActivatedRoute, + private router: Router, + private i18nService: I18nService, + private broadcasterService: BroadcasterService, + private changeDetectorRef: ChangeDetectorRef, + private ngZone: NgZone, + private syncService: SyncService, + private messagingService: MessagingService, + private platformUtilsService: PlatformUtilsService, + private eventCollectionService: EventCollectionService, + private totpService: TotpService, + private passwordRepromptService: PasswordRepromptService, + private searchBarService: SearchBarService, + private apiService: ApiService, + private dialogService: DialogService, + private billingAccountProfileStateService: BillingAccountProfileStateService, + private toastService: ToastService, + private accountService: AccountService, + private cipherService: CipherService, + private formConfigService: CipherFormConfigService, + private premiumUpgradePromptService: PremiumUpgradePromptService, + private collectionService: CollectionService, + private organizationService: OrganizationService, + private folderService: FolderService, + private configService: ConfigService, + private authRequestService: AuthRequestServiceAbstraction, + private cipherArchiveService: CipherArchiveService, + private policyService: PolicyService, + private archiveCipherUtilitiesService: ArchiveCipherUtilitiesService, + ) {} + + async ngOnInit() { + this.accountService.activeAccount$ + .pipe( + filter((account): account is Account => !!account), + switchMap((account) => + this.billingAccountProfileStateService.hasPremiumFromAnySource$(account.id), + ), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((canAccessPremium: boolean) => { + this.userHasPremiumAccess = canAccessPremium; + }); + + this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => { + this.ngZone + .run(async () => { + let detectChanges = true; + try { + switch (message.command) { + case "newLogin": + await this.addCipher(CipherType.Login).catch(() => {}); + break; + case "newCard": + await this.addCipher(CipherType.Card).catch(() => {}); + break; + case "newIdentity": + await this.addCipher(CipherType.Identity).catch(() => {}); + break; + case "newSecureNote": + await this.addCipher(CipherType.SecureNote).catch(() => {}); + break; + case "newSshKey": + await this.addCipher(CipherType.SshKey).catch(() => {}); + break; + case "focusSearch": + (document.querySelector("#search") as HTMLInputElement)?.select(); + detectChanges = false; + break; + case "syncCompleted": + if (this.vaultItemsComponent) { + await this.vaultItemsComponent + .reload(this.activeFilter.buildFilter()) + .catch(() => {}); + } + if (this.vaultFilterComponent) { + await this.vaultFilterComponent + .reloadCollectionsAndFolders(this.activeFilter) + .catch(() => {}); + await this.vaultFilterComponent.reloadOrganizations().catch(() => {}); + } + break; + case "modalShown": + this.showingModal = true; + break; + case "modalClosed": + this.showingModal = false; + break; + case "copyUsername": { + if (this.cipher?.login?.username) { + this.copyValue(this.cipher, this.cipher?.login?.username, "username", "Username"); + } + break; + } + case "copyPassword": { + if (this.cipher?.login?.password && this.cipher.viewPassword) { + this.copyValue(this.cipher, this.cipher.login.password, "password", "Password"); + await this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, this.cipher.id) + .catch(() => {}); + } + break; + } + case "copyTotp": { + if ( + this.cipher?.login?.hasTotp && + (this.cipher.organizationUseTotp || this.userHasPremiumAccess) + ) { + const value = await firstValueFrom( + this.totpService.getCode$(this.cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(this.cipher, value.code, "verificationCodeTotp", "TOTP"); + } + } + break; + } + default: + detectChanges = false; + break; + } + } catch { + // Ignore errors + } + if (detectChanges) { + this.changeDetectorRef.detectChanges(); + } + }) + .catch(() => {}); + }); + + if (!this.syncService.syncInProgress) { + await this.load().catch(() => {}); + } + + this.searchBarService.setEnabled(true); + this.searchBarService.setPlaceholderText(this.i18nService.t("searchVault")); + + const authRequests = await firstValueFrom( + this.authRequestService.getLatestPendingAuthRequest$()!, + ); + if (authRequests != null) { + this.messagingService.send("openLoginApproval", { + notificationId: authRequests.id, + }); + } + + this.activeUserId = await firstValueFrom( + this.accountService.activeAccount$.pipe(getUserId), + ).catch((): any => null); + + if (this.activeUserId) { + this.cipherService + .failedToDecryptCiphers$(this.activeUserId) + .pipe( + map((ciphers) => ciphers?.filter((c) => !c.isDeleted) ?? []), + filter((ciphers) => ciphers.length > 0), + take(1), + takeUntil(this.componentIsDestroyed$), + ) + .subscribe((ciphers) => { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: ciphers.map((c) => c.id as CipherId), + }); + }); + } + + this.organizations$.pipe(takeUntil(this.componentIsDestroyed$)).subscribe((orgs) => { + this.allOrganizations = orgs; + }); + + if (!this.activeUserId) { + throw new Error("No user found."); + } + + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(takeUntil(this.componentIsDestroyed$)) + .subscribe((collections) => { + this.allCollections = collections; + }); + } + + ngOnDestroy() { + this.searchBarService.setEnabled(false); + this.broadcasterService.unsubscribe(BroadcasterSubscriptionId); + this.componentIsDestroyed$.next(true); + this.componentIsDestroyed$.complete(); + } + + async load() { + const params = await firstValueFrom(this.route.queryParams).catch(); + const paramCipherAddType = toCipherType(params.addType); + if (params.cipherId) { + const cipherView = new CipherView(); + cipherView.id = params.cipherId; + if (params.action === "clone") { + await this.cloneCipher(cipherView).catch(() => {}); + } else if (params.action === "edit") { + await this.editCipher(cipherView).catch(() => {}); + } else { + await this.viewCipher(cipherView).catch(() => {}); + } + } else if (params.action === "add" && paramCipherAddType) { + this.addType = paramCipherAddType; + await this.addCipher(this.addType).catch(() => {}); + } + + const paramCipherType = toCipherType(params.type); + this.activeFilter = new VaultFilter({ + status: params.deleted ? "trash" : params.favorites ? "favorites" : "all", + cipherType: params.action === "add" || paramCipherType == null ? undefined : paramCipherType, + selectedFolderId: params.folderId, + selectedCollectionId: params.selectedCollectionId, + selectedOrganizationId: params.selectedOrganizationId, + myVaultOnly: params.myVaultOnly ?? false, + }); + if (this.vaultItemsComponent) { + await this.vaultItemsComponent.reload(this.activeFilter.buildFilter()).catch(() => {}); + } + } + + /** + * Handler for Vault level CopyClickDirectives to send the minimizeOnCopy message + */ + onCopy() { + this.messagingService.send("minimizeOnCopy"); + } + + async viewCipher(c: CipherViewLike) { + if (CipherViewLikeUtils.decryptionFailure(c)) { + DecryptionFailureDialogComponent.open(this.dialogService, { + cipherIds: [c.id as CipherId], + }); + return; + } + const cipher = await this.cipherService.getFullCipherView(c); + if (await this.shouldReprompt(cipher, "view")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + this.collections = + this.vaultFilterComponent?.collections?.fullList.filter((c) => + cipher.collectionIds.includes(c.id), + ) ?? null; + this.action = "view"; + + await this.go().catch(() => {}); + await this.eventCollectionService.collect( + EventType.Cipher_ClientViewed, + cipher.id, + false, + cipher.organizationId, + ); + } + + formStatusChanged(status: "disabled" | "enabled") { + this.formDisabled = status === "disabled"; + } + + async openAttachmentsDialog() { + if (!this.userHasPremiumAccess) { + return; + } + const dialogRef = AttachmentsV2Component.open(this.dialogService, { + cipherId: this.cipherId as CipherId, + }); + const result = await firstValueFrom(dialogRef.closed).catch((): any => null); + if ( + result?.action === AttachmentDialogResult.Removed || + result?.action === AttachmentDialogResult.Uploaded + ) { + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (this.cipherFormComponent == null) { + return; + } + + // The encrypted state of ciphers is updated when an attachment is added, + // but the cache is also cleared. Depending on timing, `cipherService.get` can return the + // old cipher. Retrieve the updated cipher from `cipherViews$`, + // which refreshes after the cached is cleared. + const updatedCipherView = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId)), + ), + ); + + // `find` can return undefined but that shouldn't happen as + // this would mean that the cipher was deleted. + // To make TypeScript happy, exit early if it isn't found. + if (!updatedCipherView) { + return; + } + + this.cipherFormComponent.patchCipher((currentCipher) => { + currentCipher.attachments = updatedCipherView.attachments; + currentCipher.revisionDate = updatedCipherView.revisionDate; + + return currentCipher; + }); + } + } + + async viewCipherMenu(c: CipherViewLike) { + const cipher = await this.cipherService.getFullCipherView(c); + const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)); + const userCanArchive = await firstValueFrom(this.cipherArchiveService.userCanArchive$(userId)); + const orgOwnershipPolicy = await firstValueFrom( + this.policyService.policyAppliesToUser$(PolicyType.OrganizationDataOwnership, userId), + ); + + const menu: RendererMenuItem[] = [ + { + label: this.i18nService.t("view"), + click: () => { + this.functionWithChangeDetection(() => { + this.viewCipher(cipher).catch(() => {}); + }); + }, + }, + ]; + + if (cipher.decryptionFailure) { + invokeMenu(menu); + } + + if (!cipher.isDeleted) { + menu.push({ + label: this.i18nService.t("edit"), + click: () => { + this.functionWithChangeDetection(() => { + this.editCipher(cipher).catch(() => {}); + }); + }, + }); + + const archivedWithOrgOwnership = cipher.isArchived && orgOwnershipPolicy; + const canCloneArchived = !cipher.isArchived || userCanArchive; + + if (!cipher.organizationId && !archivedWithOrgOwnership && canCloneArchived) { + menu.push({ + label: this.i18nService.t("clone"), + click: () => { + this.functionWithChangeDetection(() => { + this.cloneCipher(cipher).catch(() => {}); + }); + }, + }); + } + + const hasEditableCollections = this.allCollections.some((collection) => !collection.readOnly); + + if (cipher.canAssignToCollections && hasEditableCollections) { + menu.push({ + label: this.i18nService.t("assignToCollections"), + click: () => + this.functionWithChangeDetection(async () => { + await this.shareCipher(cipher); + }), + }); + } + } + + if (!cipher.organizationId && !cipher.isDeleted && !cipher.isArchived) { + menu.push({ + label: this.i18nService.t("archiveVerb"), + click: async () => { + if (!userCanArchive) { + await this.premiumUpgradePromptService.promptForPremium(); + return; + } + + await this.archiveCipherUtilitiesService.archiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + if (cipher.isArchived) { + menu.push({ + label: this.i18nService.t("unArchive"), + click: async () => { + await this.archiveCipherUtilitiesService.unarchiveCipher(cipher); + await this.refreshCurrentCipher(); + }, + }); + } + + switch (cipher.type) { + case CipherType.Login: + if ( + cipher.login.canLaunch || + cipher.login.username != null || + cipher.login.password != null + ) { + menu.push({ type: "separator" }); + } + if (cipher.login.canLaunch) { + menu.push({ + label: this.i18nService.t("launch"), + click: () => this.platformUtilsService.launchUri(cipher.login.launchUri), + }); + } + if (cipher.login.username != null) { + menu.push({ + label: this.i18nService.t("copyUsername"), + click: () => this.copyValue(cipher, cipher.login.username, "username", "Username"), + }); + } + if (cipher.login.password != null && cipher.viewPassword) { + menu.push({ + label: this.i18nService.t("copyPassword"), + click: () => { + this.copyValue(cipher, cipher.login.password, "password", "Password"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedPassword, cipher.id) + .catch(() => {}); + }, + }); + } + if (cipher.login.hasTotp && (cipher.organizationUseTotp || this.userHasPremiumAccess)) { + menu.push({ + label: this.i18nService.t("copyVerificationCodeTotp"), + click: async () => { + const value = await firstValueFrom( + this.totpService.getCode$(cipher.login.totp), + ).catch((): any => null); + if (value) { + this.copyValue(cipher, value.code, "verificationCodeTotp", "TOTP"); + } + }, + }); + } + break; + case CipherType.Card: + if (cipher.card.number != null || cipher.card.code != null) { + menu.push({ type: "separator" }); + } + if (cipher.card.number != null) { + menu.push({ + label: this.i18nService.t("copyNumber"), + click: () => this.copyValue(cipher, cipher.card.number, "number", "Card Number"), + }); + } + if (cipher.card.code != null) { + menu.push({ + label: this.i18nService.t("copySecurityCode"), + click: () => { + this.copyValue(cipher, cipher.card.code, "securityCode", "Security Code"); + this.eventCollectionService + .collect(EventType.Cipher_ClientCopiedCardCode, cipher.id) + .catch(() => {}); + }, + }); + } + break; + default: + break; + } + invokeMenu(menu); + } + + async shouldReprompt(cipher: CipherView, action: "edit" | "clone" | "view"): Promise { + return !(await this.canNavigateAway(action, cipher)) || !(await this.passwordReprompt(cipher)); + } + + async buildFormConfig(action: CipherFormMode) { + this.config = await this.formConfigService + .buildConfig(action, this.cipherId as CipherId, this.addType) + .catch((): any => null); + } + + async editCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "edit")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("edit"); + if (!cipher.edit && this.config) { + this.config.mode = "partial-edit"; + } + this.action = "edit"; + await this.go().catch(() => {}); + } + + async cloneCipher(cipher: CipherView) { + if (await this.shouldReprompt(cipher, "clone")) { + return; + } + this.cipherId = cipher.id; + this.cipher = cipher; + await this.buildFormConfig("clone"); + this.action = "clone"; + await this.go().catch(() => {}); + } + + async shareCipher(cipher: CipherView) { + if (!cipher) { + this.toastService.showToast({ + variant: "error", + title: this.i18nService.t("errorOccurred"), + message: this.i18nService.t("nothingSelected"), + }); + return; + } + + if (!(await this.passwordReprompt(cipher))) { + return; + } + + const availableCollections = this.getAvailableCollections(cipher); + + const dialog = AssignCollectionsDesktopComponent.open(this.dialogService, { + data: { + ciphers: [cipher], + organizationId: cipher.organizationId as OrganizationId, + availableCollections, + }, + }); + + const result = await lastValueFrom(dialog.closed); + if (result === CollectionAssignmentResult.Saved) { + const updatedCipher = await firstValueFrom( + // Fetch the updated cipher from the service + this.cipherService.cipherViews$(this.activeUserId as UserId).pipe( + filter((ciphers) => ciphers != null), + map((ciphers) => ciphers!.find((c) => c.id === cipher.id)), + filter((foundCipher) => foundCipher != null), + ), + ); + await this.savedCipher(updatedCipher); + } + } + + async addCipher(type: CipherType) { + if (this.action === "add") { + return; + } + this.addType = type || this.activeFilter.cipherType; + this.cipher = new CipherView(); + this.cipherId = null; + await this.buildFormConfig("add"); + this.action = "add"; + this.prefillCipherFromFilter(); + await this.go().catch(() => {}); + + if (type === CipherType.SshKey) { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("sshKeyGenerated"), + }); + } + } + + async savedCipher(cipher: CipherView) { + this.cipherId = null; + this.action = "view"; + await this.vaultItemsComponent?.refresh().catch(() => {}); + + if (!this.activeUserId) { + throw new Error("No userId provided."); + } + + this.collections = await firstValueFrom( + this.collectionService + .decryptedCollections$(this.activeUserId) + .pipe(getByIds(cipher.collectionIds)), + ); + + this.cipherId = cipher.id; + this.cipher = cipher; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async deleteCipher() { + this.cipherId = null; + this.cipher = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async restoreCipher() { + this.cipherId = null; + this.action = null; + await this.go().catch(() => {}); + await this.vaultItemsComponent?.refresh().catch(() => {}); + } + + async cancelCipher(cipher: CipherView) { + this.cipherId = cipher.id; + this.cipher = cipher; + this.action = this.cipherId ? "view" : null; + await this.go().catch(() => {}); + } + + async applyVaultFilter(vaultFilter: VaultFilter) { + this.searchBarService.setPlaceholderText( + this.i18nService.t(this.calculateSearchBarLocalizationString(vaultFilter)), + ); + this.activeFilter = vaultFilter; + await this.vaultItemsComponent + ?.reload( + this.activeFilter.buildFilter(), + vaultFilter.status === "trash", + vaultFilter.status === "archive", + ) + .catch(() => {}); + await this.go().catch(() => {}); + } + + private getAvailableCollections(cipher: CipherView): CollectionView[] { + const orgId = cipher.organizationId; + if (!orgId || orgId === "MyVault") { + return []; + } + + const organization = this.allOrganizations.find((o) => o.id === orgId); + return this.allCollections.filter((c) => c.organizationId === organization?.id && !c.readOnly); + } + + private calculateSearchBarLocalizationString(vaultFilter: VaultFilter): string { + if (vaultFilter.status === "favorites") { + return "searchFavorites"; + } + if (vaultFilter.status === "trash") { + return "searchTrash"; + } + if (vaultFilter.cipherType != null) { + return "searchType"; + } + if (vaultFilter.selectedFolderId != null && vaultFilter.selectedFolderId !== "none") { + return "searchFolder"; + } + if (vaultFilter.selectedCollectionId != null) { + return "searchCollection"; + } + if (vaultFilter.selectedOrganizationId != null) { + return "searchOrganization"; + } + if (vaultFilter.myVaultOnly) { + return "searchMyVault"; + } + return "searchVault"; + } + + async addFolder() { + this.messagingService.send("newFolder"); + } + + async editFolder(folderId: string) { + if (!this.activeUserId) { + return; + } + const folderView = await firstValueFrom( + this.folderService.getDecrypted$(folderId, this.activeUserId), + ); + + if (!folderView) { + return; + } + + const dialogRef = AddEditFolderDialogComponent.open(this.dialogService, { + editFolderConfig: { + folder: { + ...folderView, + }, + }, + }); + + const result = await lastValueFrom(dialogRef.closed); + + if ( + result === AddEditFolderDialogResult.Deleted || + result === AddEditFolderDialogResult.Created + ) { + await this.vaultFilterComponent?.reloadCollectionsAndFolders(this.activeFilter); + } + } + + /** Refresh the current cipher object */ + protected async refreshCurrentCipher() { + if (!this.cipher) { + return; + } + + this.cipher = await firstValueFrom( + this.cipherService.cipherViews$(this.activeUserId!).pipe( + filter((c) => !!c), + map((ciphers) => ciphers.find((c) => c.id === this.cipherId) ?? null), + ), + ); + } + + private dirtyInput(): boolean { + return ( + (this.action === "add" || this.action === "edit" || this.action === "clone") && + document.querySelectorAll("vault-cipher-form .ng-dirty").length > 0 + ); + } + + private async wantsToSaveChanges(): Promise { + const confirmed = await this.dialogService + .openSimpleDialog({ + title: { key: "unsavedChangesTitle" }, + content: { key: "unsavedChangesConfirmation" }, + type: "warning", + }) + .catch(() => false); + return !confirmed; + } + + private async go(queryParams: any = null) { + if (queryParams == null) { + queryParams = { + action: this.action, + cipherId: this.cipherId, + favorites: this.favorites ? true : null, + type: this.type, + folderId: this.folderId, + collectionId: this.collectionId, + deleted: this.deleted ? true : null, + organizationId: this.organizationId, + myVaultOnly: this.myVaultOnly, + }; + } + this.router + .navigate([], { + relativeTo: this.route, + queryParams: queryParams, + replaceUrl: true, + }) + .catch(() => {}); + } + + private copyValue(cipher: CipherView, value: string, labelI18nKey: string, aType: string) { + this.functionWithChangeDetection(() => { + (async () => { + if ( + cipher.reprompt !== CipherRepromptType.None && + this.passwordRepromptService.protectedFields().includes(aType) && + !(await this.passwordReprompt(cipher)) + ) { + return; + } + this.platformUtilsService.copyToClipboard(value); + this.toastService.showToast({ + variant: "info", + title: undefined, + message: this.i18nService.t("valueCopied", this.i18nService.t(labelI18nKey)), + }); + this.messagingService.send("minimizeOnCopy"); + })().catch(() => {}); + }); + } + + private functionWithChangeDetection(func: () => void) { + this.ngZone.run(() => { + func(); + this.changeDetectorRef.detectChanges(); + }); + } + + private prefillCipherFromFilter() { + if (this.activeFilter.selectedCollectionId != null && this.vaultFilterComponent != null) { + const collections = this.vaultFilterComponent.collections?.fullList.filter( + (c) => c.id === this.activeFilter.selectedCollectionId, + ); + if (collections.length > 0) { + this.addOrganizationId = collections[0].organizationId; + this.addCollectionIds = [this.activeFilter.selectedCollectionId]; + } + } else if (this.activeFilter.selectedOrganizationId) { + this.addOrganizationId = this.activeFilter.selectedOrganizationId; + } else { + // clear out organizationId when the user switches to a personal vault filter + this.addOrganizationId = null; + } + if (this.activeFilter.selectedFolderId && this.activeFilter.selectedFolder) { + this.folderId = this.activeFilter.selectedFolderId; + } + + if (this.config == null) { + return; + } + + this.config.initialValues = { + ...this.config.initialValues, + organizationId: this.addOrganizationId as OrganizationId, + }; + } + + private async canNavigateAway(action: string, cipher?: CipherView) { + if (this.action === action && (!cipher || this.cipherId === cipher.id)) { + return false; + } else if (this.dirtyInput() && (await this.wantsToSaveChanges())) { + return false; + } + return true; + } + + private async passwordReprompt(cipher: CipherView) { + if (cipher.reprompt === CipherRepromptType.None) { + this.cipherRepromptId = null; + return true; + } + if (this.cipherRepromptId === cipher.id) { + return true; + } + const repromptResult = await this.passwordRepromptService.showPasswordPrompt(); + if (repromptResult) { + this.cipherRepromptId = cipher.id; + } + return repromptResult; + } +} diff --git a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html index accb5f77fdc..e84f78458d6 100644 --- a/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html +++ b/apps/web/src/app/admin-console/organizations/layouts/organization-layout.component.html @@ -4,7 +4,7 @@ diff --git a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts index fb8bdef1d8c..9c39ca572fb 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/access-selector/storybook-utils.ts @@ -1,4 +1,4 @@ -import { action } from "@storybook/addon-actions"; +import { action } from "storybook/actions"; import { AccessItemType, AccessItemView } from "./access-selector.models"; diff --git a/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.html b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.html index 63c26bd61f1..33e89f21fc0 100644 --- a/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.html +++ b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.html @@ -24,7 +24,7 @@
      • - {{ "premiumSignUpStorage" | i18n }} + {{ "premiumSignUpStorageV2" | i18n: `${(providedStorageGb$ | async)} GB` }}
      • @@ -82,7 +82,10 @@ /> {{ "additionalStorageIntervalDesc" - | i18n: "1 GB" : (storagePrice$ | async | currency: "$") : ("year" | i18n) + | i18n + : `${(providedStorageGb$ | async)} GB` + : (storagePrice$ | async | currency: "$") + : ("year" | i18n) }}
diff --git a/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.ts b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.ts index fceeeedf170..86a508d2701 100644 --- a/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.ts +++ b/apps/web/src/app/billing/individual/premium/cloud-hosted-premium.component.ts @@ -22,8 +22,8 @@ import { debounceTime } from "rxjs/operators"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions"; +import { SubscriptionPricingServiceAbstraction } from "@bitwarden/common/billing/abstractions/subscription-pricing.service.abstraction"; import { PaymentMethodType } from "@bitwarden/common/billing/enums"; -import { DefaultSubscriptionPricingService } from "@bitwarden/common/billing/services/subscription-pricing.service"; import { PersonalSubscriptionPricingTierIds } from "@bitwarden/common/billing/types/subscription-pricing-tier"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -75,6 +75,7 @@ export class CloudHostedPremiumComponent { return { seat: premiumPlan.passwordManager.annualPrice, storage: premiumPlan.passwordManager.annualPricePerAdditionalStorageGB, + providedStorageGb: premiumPlan.passwordManager.providedStorageGB, }; }), shareReplay({ bufferSize: 1, refCount: true }), @@ -84,6 +85,8 @@ export class CloudHostedPremiumComponent { storagePrice$ = this.premiumPrices$.pipe(map((prices) => prices.storage)); + providedStorageGb$ = this.premiumPrices$.pipe(map((prices) => prices.providedStorageGb)); + protected isLoadingPrices$ = this.premiumPrices$.pipe( map(() => false), startWith(true), @@ -134,7 +137,7 @@ export class CloudHostedPremiumComponent { private accountService: AccountService, private subscriberBillingClient: SubscriberBillingClient, private taxClient: TaxClient, - private subscriptionPricingService: DefaultSubscriptionPricingService, + private subscriptionPricingService: SubscriptionPricingServiceAbstraction, ) { this.hasPremiumFromAnyOrganization$ = this.accountService.activeAccount$.pipe( switchMap((account) => diff --git a/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.spec.ts b/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.spec.ts index 7f698ae50d1..b28a7b8c4a2 100644 --- a/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.spec.ts +++ b/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.spec.ts @@ -1,4 +1,4 @@ -import { Component, input, output } from "@angular/core"; +import { ChangeDetectionStrategy, Component, input, output } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; import { NoopAnimationsModule } from "@angular/platform-browser/animations"; import { Router } from "@angular/router"; @@ -28,12 +28,11 @@ import { UnifiedUpgradeDialogStep, } from "./unified-upgrade-dialog.component"; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-upgrade-account", template: "", standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, }) class MockUpgradeAccountComponent { readonly dialogTitleMessageOverride = input(null); @@ -42,12 +41,11 @@ class MockUpgradeAccountComponent { closeClicked = output(); } -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-upgrade-payment", template: "", standalone: true, + changeDetection: ChangeDetectionStrategy.OnPush, }) class MockUpgradePaymentComponent { readonly selectedPlanId = input(null); @@ -77,10 +75,56 @@ describe("UnifiedUpgradeDialogComponent", () => { planSelectionStepTitleOverride: null, }; + /** + * Helper function to create and configure a fresh component instance with custom dialog data + */ + async function createComponentWithDialogData( + dialogData: UnifiedUpgradeDialogParams, + waitForStable = false, + ): Promise<{ + fixture: ComponentFixture; + component: UnifiedUpgradeDialogComponent; + }> { + TestBed.resetTestingModule(); + jest.clearAllMocks(); + + await TestBed.configureTestingModule({ + imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], + providers: [ + { provide: DialogRef, useValue: mockDialogRef }, + { provide: DIALOG_DATA, useValue: dialogData }, + { provide: Router, useValue: mockRouter }, + { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, + ], + }) + .overrideComponent(UnifiedUpgradeDialogComponent, { + remove: { + imports: [UpgradeAccountComponent, UpgradePaymentComponent], + }, + add: { + imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], + }, + }) + .compileComponents(); + + const newFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); + const newComponent = newFixture.componentInstance; + newFixture.detectChanges(); + + if (waitForStable) { + await newFixture.whenStable(); + } + + return { fixture: newFixture, component: newComponent }; + } + beforeEach(async () => { // Reset mocks jest.clearAllMocks(); + // Default mock: no premium interest + mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(false); + await TestBed.configureTestingModule({ imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], providers: [ @@ -117,49 +161,63 @@ describe("UnifiedUpgradeDialogComponent", () => { }); it("should initialize with custom initial step", async () => { - TestBed.resetTestingModule(); - const customDialogData: UnifiedUpgradeDialogParams = { account: mockAccount, initialStep: UnifiedUpgradeDialogStep.Payment, selectedPlan: PersonalSubscriptionPricingTierIds.Premium, }; - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], - providers: [ - { provide: DialogRef, useValue: mockDialogRef }, - { provide: DIALOG_DATA, useValue: customDialogData }, - { provide: Router, useValue: mockRouter }, - { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, - ], - }) - .overrideComponent(UnifiedUpgradeDialogComponent, { - remove: { - imports: [UpgradeAccountComponent, UpgradePaymentComponent], - }, - add: { - imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], - }, - }) - .compileComponents(); - - const customFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); - const customComponent = customFixture.componentInstance; - customFixture.detectChanges(); + const { component: customComponent } = await createComponentWithDialogData(customDialogData); expect(customComponent["step"]()).toBe(UnifiedUpgradeDialogStep.Payment); expect(customComponent["selectedPlan"]()).toBe(PersonalSubscriptionPricingTierIds.Premium); }); + describe("ngOnInit premium interest handling", () => { + it("should check premium interest on initialization", async () => { + // Component already initialized in beforeEach + expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledWith( + mockAccount.id, + ); + }); + + it("should set hasPremiumInterest signal and clear premium interest when it exists", async () => { + mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(true); + mockPremiumInterestStateService.clearPremiumInterest.mockResolvedValue(undefined); + + const { component: customComponent } = await createComponentWithDialogData( + defaultDialogData, + true, + ); + + expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledWith( + mockAccount.id, + ); + expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledWith( + mockAccount.id, + ); + expect(customComponent["hasPremiumInterest"]()).toBe(true); + }); + + it("should not set hasPremiumInterest signal or clear when premium interest does not exist", async () => { + mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(false); + + const { component: customComponent } = await createComponentWithDialogData(defaultDialogData); + + expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledWith( + mockAccount.id, + ); + expect(mockPremiumInterestStateService.clearPremiumInterest).not.toHaveBeenCalled(); + expect(customComponent["hasPremiumInterest"]()).toBe(false); + }); + }); + describe("custom dialog title", () => { it("should use null as default when no override is provided", () => { expect(component["planSelectionStepTitleOverride"]()).toBeNull(); }); it("should use custom title when provided in dialog config", async () => { - TestBed.resetTestingModule(); - const customDialogData: UnifiedUpgradeDialogParams = { account: mockAccount, initialStep: UnifiedUpgradeDialogStep.PlanSelection, @@ -167,28 +225,7 @@ describe("UnifiedUpgradeDialogComponent", () => { planSelectionStepTitleOverride: "upgradeYourPlan", }; - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], - providers: [ - { provide: DialogRef, useValue: mockDialogRef }, - { provide: DIALOG_DATA, useValue: customDialogData }, - { provide: Router, useValue: mockRouter }, - { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, - ], - }) - .overrideComponent(UnifiedUpgradeDialogComponent, { - remove: { - imports: [UpgradeAccountComponent, UpgradePaymentComponent], - }, - add: { - imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], - }, - }) - .compileComponents(); - - const customFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); - const customComponent = customFixture.componentInstance; - customFixture.detectChanges(); + const { component: customComponent } = await createComponentWithDialogData(customDialogData); expect(customComponent["planSelectionStepTitleOverride"]()).toBe("upgradeYourPlan"); }); @@ -221,8 +258,6 @@ describe("UnifiedUpgradeDialogComponent", () => { }); it("should be set to true when provided in dialog config", async () => { - TestBed.resetTestingModule(); - const customDialogData: UnifiedUpgradeDialogParams = { account: mockAccount, initialStep: null, @@ -230,108 +265,32 @@ describe("UnifiedUpgradeDialogComponent", () => { hideContinueWithoutUpgradingButton: true, }; - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], - providers: [ - { provide: DialogRef, useValue: mockDialogRef }, - { provide: DIALOG_DATA, useValue: customDialogData }, - { provide: Router, useValue: mockRouter }, - { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, - ], - }) - .overrideComponent(UnifiedUpgradeDialogComponent, { - remove: { - imports: [UpgradeAccountComponent, UpgradePaymentComponent], - }, - add: { - imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], - }, - }) - .compileComponents(); - - const customFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); - const customComponent = customFixture.componentInstance; - customFixture.detectChanges(); + const { component: customComponent } = await createComponentWithDialogData(customDialogData); expect(customComponent["hideContinueWithoutUpgradingButton"]()).toBe(true); }); }); - describe("onComplete with premium interest", () => { - it("should check premium interest, clear it, and route to /vault when premium interest exists", async () => { + describe("onComplete", () => { + it("should route to /vault when upgrading to premium with premium interest", async () => { + // Set up component with premium interest mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(true); - mockPremiumInterestStateService.clearPremiumInterest.mockResolvedValue(); + mockPremiumInterestStateService.clearPremiumInterest.mockResolvedValue(undefined); mockRouter.navigate.mockResolvedValue(true); - const result: UpgradePaymentResult = { - status: "upgradedToPremium", - organizationId: null, - }; - - await component["onComplete"](result); + const { component: customComponent } = await createComponentWithDialogData( + defaultDialogData, + true, + ); + // Premium interest should be set and cleared during ngOnInit expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledWith( mockAccount.id, ); expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledWith( mockAccount.id, ); - expect(mockRouter.navigate).toHaveBeenCalledWith(["/vault"]); - expect(mockDialogRef.close).toHaveBeenCalledWith({ - status: "upgradedToPremium", - organizationId: null, - }); - }); - - it("should not clear premium interest when upgrading to families", async () => { - const result: UpgradePaymentResult = { - status: "upgradedToFamilies", - organizationId: "org-123", - }; - - await component["onComplete"](result); - - expect(mockPremiumInterestStateService.getPremiumInterest).not.toHaveBeenCalled(); - expect(mockPremiumInterestStateService.clearPremiumInterest).not.toHaveBeenCalled(); - expect(mockDialogRef.close).toHaveBeenCalledWith({ - status: "upgradedToFamilies", - organizationId: "org-123", - }); - }); - - it("should use standard redirect when no premium interest exists", async () => { - TestBed.resetTestingModule(); - - const customDialogData: UnifiedUpgradeDialogParams = { - account: mockAccount, - redirectOnCompletion: true, - }; - - mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(false); - mockRouter.navigate.mockResolvedValue(true); - - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], - providers: [ - { provide: DialogRef, useValue: mockDialogRef }, - { provide: DIALOG_DATA, useValue: customDialogData }, - { provide: Router, useValue: mockRouter }, - { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, - ], - }) - .overrideComponent(UnifiedUpgradeDialogComponent, { - remove: { - imports: [UpgradeAccountComponent, UpgradePaymentComponent], - }, - add: { - imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], - }, - }) - .compileComponents(); - - const customFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); - const customComponent = customFixture.componentInstance; - customFixture.detectChanges(); + expect(customComponent["hasPremiumInterest"]()).toBe(true); const result: UpgradePaymentResult = { status: "upgradedToPremium", @@ -340,10 +299,55 @@ describe("UnifiedUpgradeDialogComponent", () => { await customComponent["onComplete"](result); - expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledWith( - mockAccount.id, - ); - expect(mockPremiumInterestStateService.clearPremiumInterest).not.toHaveBeenCalled(); + // Should route to /vault because hasPremiumInterest signal is true + // No additional service calls should be made in onComplete + expect(mockPremiumInterestStateService.getPremiumInterest).toHaveBeenCalledTimes(1); // Only from ngOnInit + expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledTimes(1); // Only from ngOnInit + expect(mockRouter.navigate).toHaveBeenCalledWith(["/vault"]); + expect(mockDialogRef.close).toHaveBeenCalledWith({ + status: "upgradedToPremium", + organizationId: null, + }); + }); + + it("should close dialog when upgrading to families (premium interest not relevant)", async () => { + const result: UpgradePaymentResult = { + status: "upgradedToFamilies", + organizationId: "org-123", + }; + + await component["onComplete"](result); + + // Premium interest logic only runs for premium upgrades, not families + expect(mockDialogRef.close).toHaveBeenCalledWith({ + status: "upgradedToFamilies", + organizationId: "org-123", + }); + }); + + it("should use standard redirect when upgrading to premium without premium interest", async () => { + const customDialogData: UnifiedUpgradeDialogParams = { + account: mockAccount, + redirectOnCompletion: true, + }; + + // No premium interest + mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(false); + mockRouter.navigate.mockResolvedValue(true); + + const { component: customComponent } = await createComponentWithDialogData(customDialogData); + + // Verify no premium interest was set during ngOnInit + expect(customComponent["hasPremiumInterest"]()).toBe(false); + + const result: UpgradePaymentResult = { + status: "upgradedToPremium", + organizationId: null, + }; + + await customComponent["onComplete"](result); + + // Should use standard redirect because hasPremiumInterest signal is false expect(mockRouter.navigate).toHaveBeenCalledWith([ "/settings/subscription/user-subscription", ]); @@ -354,70 +358,44 @@ describe("UnifiedUpgradeDialogComponent", () => { }); }); - describe("onCloseClicked with premium interest", () => { - it("should clear premium interest when modal is closed", async () => { - mockPremiumInterestStateService.clearPremiumInterest.mockResolvedValue(); - + describe("onCloseClicked", () => { + it("should close dialog without clearing premium interest (cleared in ngOnInit)", async () => { await component["onCloseClicked"](); - expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledWith( - mockAccount.id, - ); + // Premium interest should have been cleared only once during ngOnInit, not again here + expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledTimes(0); expect(mockDialogRef.close).toHaveBeenCalledWith({ status: "closed" }); }); }); - describe("previousStep with premium interest", () => { - it("should NOT clear premium interest when navigating between steps", async () => { + describe("previousStep", () => { + it("should go back to plan selection when on payment step", async () => { component["step"].set(UnifiedUpgradeDialogStep.Payment); component["selectedPlan"].set(PersonalSubscriptionPricingTierIds.Premium); await component["previousStep"](); - expect(mockPremiumInterestStateService.clearPremiumInterest).not.toHaveBeenCalled(); expect(component["step"]()).toBe(UnifiedUpgradeDialogStep.PlanSelection); expect(component["selectedPlan"]()).toBeNull(); + expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledTimes(0); }); - it("should clear premium interest when backing out of dialog completely", async () => { - TestBed.resetTestingModule(); - + it("should close dialog when backing out from plan selection step (no premium interest cleared)", async () => { const customDialogData: UnifiedUpgradeDialogParams = { account: mockAccount, initialStep: UnifiedUpgradeDialogStep.Payment, selectedPlan: PersonalSubscriptionPricingTierIds.Premium, }; - mockPremiumInterestStateService.clearPremiumInterest.mockResolvedValue(); + mockPremiumInterestStateService.getPremiumInterest.mockResolvedValue(false); - await TestBed.configureTestingModule({ - imports: [NoopAnimationsModule, UnifiedUpgradeDialogComponent], - providers: [ - { provide: DialogRef, useValue: mockDialogRef }, - { provide: DIALOG_DATA, useValue: customDialogData }, - { provide: Router, useValue: mockRouter }, - { provide: PremiumInterestStateService, useValue: mockPremiumInterestStateService }, - ], - }) - .overrideComponent(UnifiedUpgradeDialogComponent, { - remove: { - imports: [UpgradeAccountComponent, UpgradePaymentComponent], - }, - add: { - imports: [MockUpgradeAccountComponent, MockUpgradePaymentComponent], - }, - }) - .compileComponents(); - - const customFixture = TestBed.createComponent(UnifiedUpgradeDialogComponent); - const customComponent = customFixture.componentInstance; - customFixture.detectChanges(); + const { component: customComponent } = await createComponentWithDialogData(customDialogData); + // Start at payment step, go back once to reach plan selection, then go back again to close await customComponent["previousStep"](); - expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledWith( - mockAccount.id, - ); + // Premium interest cleared only in ngOnInit, not in previousStep + expect(mockPremiumInterestStateService.clearPremiumInterest).toHaveBeenCalledTimes(0); expect(mockDialogRef.close).toHaveBeenCalledWith({ status: "closed" }); }); }); diff --git a/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.ts b/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.ts index 02d48e8d8f4..222bf77715c 100644 --- a/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.ts +++ b/apps/web/src/app/billing/individual/upgrade/unified-upgrade-dialog/unified-upgrade-dialog.component.ts @@ -1,6 +1,6 @@ import { DIALOG_DATA } from "@angular/cdk/dialog"; import { CommonModule } from "@angular/common"; -import { Component, Inject, OnInit, signal } from "@angular/core"; +import { ChangeDetectionStrategy, Component, Inject, OnInit, signal } from "@angular/core"; import { Router } from "@angular/router"; import { PremiumInterestStateService } from "@bitwarden/angular/billing/services/premium-interest/premium-interest-state.service.abstraction"; @@ -63,10 +63,9 @@ export type UnifiedUpgradeDialogParams = { redirectOnCompletion?: boolean; }; -// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @Component({ selector: "app-unified-upgrade-dialog", + changeDetection: ChangeDetectionStrategy.OnPush, imports: [ CommonModule, DialogModule, @@ -87,6 +86,7 @@ export class UnifiedUpgradeDialogComponent implements OnInit { protected readonly account = signal(null); protected readonly planSelectionStepTitleOverride = signal(null); protected readonly hideContinueWithoutUpgradingButton = signal(false); + protected readonly hasPremiumInterest = signal(false); protected readonly PaymentStep = UnifiedUpgradeDialogStep.Payment; protected readonly PlanSelectionStep = UnifiedUpgradeDialogStep.PlanSelection; @@ -98,7 +98,7 @@ export class UnifiedUpgradeDialogComponent implements OnInit { private premiumInterestStateService: PremiumInterestStateService, ) {} - ngOnInit(): void { + async ngOnInit(): Promise { this.account.set(this.params.account); this.step.set(this.params.initialStep ?? UnifiedUpgradeDialogStep.PlanSelection); this.selectedPlan.set(this.params.selectedPlan ?? null); @@ -106,6 +106,19 @@ export class UnifiedUpgradeDialogComponent implements OnInit { this.hideContinueWithoutUpgradingButton.set( this.params.hideContinueWithoutUpgradingButton ?? false, ); + + /* + * Check if the user has premium interest at the point we open the dialog. + * If they do, record it on a component-level signal and clear the user's premium interest. + * This prevents us from having to clear it at every dialog conclusion point. + * */ + const hasPremiumInterest = await this.premiumInterestStateService.getPremiumInterest( + this.params.account.id, + ); + if (hasPremiumInterest) { + this.hasPremiumInterest.set(true); + await this.premiumInterestStateService.clearPremiumInterest(this.params.account.id); + } } protected onPlanSelected(planId: PersonalSubscriptionPricingTierId): void { @@ -113,8 +126,6 @@ export class UnifiedUpgradeDialogComponent implements OnInit { this.nextStep(); } protected async onCloseClicked(): Promise { - // Clear premium interest when user closes/abandons modal - await this.premiumInterestStateService.clearPremiumInterest(this.params.account.id); this.close({ status: UnifiedUpgradeDialogStatus.Closed }); } @@ -135,8 +146,6 @@ export class UnifiedUpgradeDialogComponent implements OnInit { this.step.set(UnifiedUpgradeDialogStep.PlanSelection); this.selectedPlan.set(null); } else { - // Clear premium interest when backing out of dialog completely - await this.premiumInterestStateService.clearPremiumInterest(this.params.account.id); this.close({ status: UnifiedUpgradeDialogStatus.Closed }); } } @@ -161,11 +170,7 @@ export class UnifiedUpgradeDialogComponent implements OnInit { // Check premium interest and route to vault for marketing-initiated premium upgrades if (status === UnifiedUpgradeDialogStatus.UpgradedToPremium) { - const hasPremiumInterest = await this.premiumInterestStateService.getPremiumInterest( - this.params.account.id, - ); - if (hasPremiumInterest) { - await this.premiumInterestStateService.clearPremiumInterest(this.params.account.id); + if (this.hasPremiumInterest()) { await this.router.navigate(["/vault"]); return; // Exit early, don't use redirectOnCompletion } diff --git a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts index 0fd7746fc9d..978bb35c5c7 100644 --- a/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts +++ b/apps/web/src/app/billing/organizations/change-plan-dialog.component.ts @@ -620,7 +620,10 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { } get storageGb() { - return this.sub?.maxStorageGb ? this.sub?.maxStorageGb - 1 : 0; + return Math.max( + 0, + (this.sub?.maxStorageGb ?? 0) - this.selectedPlan.PasswordManager.baseStorageGb, + ); } passwordManagerSeatTotal(plan: PlanResponse): number { @@ -644,12 +647,7 @@ export class ChangePlanDialogComponent implements OnInit, OnDestroy { return 0; } - return ( - plan.PasswordManager.additionalStoragePricePerGb * - // TODO: Eslint upgrade. Please resolve this since the null check does nothing - // eslint-disable-next-line no-constant-binary-expression - Math.abs(this.sub?.maxStorageGb ? this.sub?.maxStorageGb - 1 : 0 || 0) - ); + return plan.PasswordManager.additionalStoragePricePerGb * this.storageGb; } additionalStoragePriceMonthly(selectedPlan: PlanResponse) { diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.html b/apps/web/src/app/billing/organizations/organization-plans.component.html index 6234fc6e6e3..d06604ba29e 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.html +++ b/apps/web/src/app/billing/organizations/organization-plans.component.html @@ -104,7 +104,7 @@
  • {{ "gbEncryptedFileStorage" - | i18n: selectableProduct.PasswordManager.baseStorageGb + "GB" + | i18n: selectableProduct.PasswordManager.baseStorageGb + " GB" }}
  • @@ -239,7 +239,7 @@ {{ "additionalStorageIntervalDesc" | i18n - : "1 GB" + : `${selectedPlan.PasswordManager.baseStorageGb} GB` : (additionalStoragePriceMonthly(selectedPlan) | currency: "$") : ("month" | i18n) }} diff --git a/apps/web/src/app/billing/organizations/organization-plans.component.ts b/apps/web/src/app/billing/organizations/organization-plans.component.ts index 561a3e03deb..67f6f9b0a6b 100644 --- a/apps/web/src/app/billing/organizations/organization-plans.component.ts +++ b/apps/web/src/app/billing/organizations/organization-plans.component.ts @@ -654,6 +654,14 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { if (this.singleOrgPolicyBlock) { return; } + + // Validate billing form for paid plans during creation + if (this.createOrganization && this.selectedPlan.type !== PlanType.Free) { + this.billingFormGroup.markAllAsTouched(); + if (this.billingFormGroup.invalid) { + return; + } + } const doSubmit = async (): Promise => { let orgId: string; if (this.createOrganization) { @@ -703,11 +711,18 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { return orgId; }; - this.formPromise = doSubmit(); - const organizationId = await this.formPromise; - this.onSuccess.emit({ organizationId: organizationId }); - // TODO: No one actually listening to this message? - this.messagingService.send("organizationCreated", { organizationId }); + try { + this.formPromise = doSubmit(); + const organizationId = await this.formPromise; + this.onSuccess.emit({ organizationId: organizationId }); + // TODO: No one actually listening to this message? + this.messagingService.send("organizationCreated", { organizationId }); + } catch (error: unknown) { + if (error instanceof Error && error.message === "Payment method validation failed") { + return; + } + throw error; + } }; protected get showTaxIdField(): boolean { @@ -826,6 +841,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { return; } const paymentMethod = await this.enterPaymentMethodComponent.tokenize(); + if (!paymentMethod) { + throw new Error("Payment method validation failed"); + } await this.subscriberBillingClient.updatePaymentMethod( { type: "organization", data: this.organization }, paymentMethod, @@ -877,6 +895,9 @@ export class OrganizationPlansComponent implements OnInit, OnDestroy { } const paymentMethod = await this.enterPaymentMethodComponent.tokenize(); + if (!paymentMethod) { + throw new Error("Payment method validation failed"); + } const billingAddress = getBillingAddressFromForm( this.billingFormGroup.controls.billingAddress, diff --git a/apps/web/src/app/billing/services/pricing-summary.service.spec.ts b/apps/web/src/app/billing/services/pricing-summary.service.spec.ts new file mode 100644 index 00000000000..4e15d318a03 --- /dev/null +++ b/apps/web/src/app/billing/services/pricing-summary.service.spec.ts @@ -0,0 +1,232 @@ +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { PlanInterval, ProductTierType } from "@bitwarden/common/billing/enums"; +import { + BillingCustomerDiscount, + OrganizationSubscriptionResponse, +} from "@bitwarden/common/billing/models/response/organization-subscription.response"; +import { + PasswordManagerPlanFeaturesResponse, + PlanResponse, + SecretsManagerPlanFeaturesResponse, +} from "@bitwarden/common/billing/models/response/plan.response"; + +import { PricingSummaryData } from "../shared/pricing-summary/pricing-summary.component"; + +import { PricingSummaryService } from "./pricing-summary.service"; + +describe("PricingSummaryService", () => { + let service: PricingSummaryService; + + beforeEach(() => { + service = new PricingSummaryService(); + }); + + describe("getPricingSummaryData", () => { + let mockPlan: PlanResponse; + let mockSub: OrganizationSubscriptionResponse; + let mockOrganization: Organization; + + beforeEach(() => { + // Create mock plan with password manager features + mockPlan = { + productTier: ProductTierType.Teams, + PasswordManager: { + basePrice: 0, + seatPrice: 48, + baseSeats: 0, + hasAdditionalSeatsOption: true, + hasPremiumAccessOption: false, + premiumAccessOptionPrice: 0, + hasAdditionalStorageOption: true, + additionalStoragePricePerGb: 6, + baseStorageGb: 1, + } as PasswordManagerPlanFeaturesResponse, + SecretsManager: { + basePrice: 0, + seatPrice: 72, + baseSeats: 3, + hasAdditionalSeatsOption: true, + hasAdditionalServiceAccountOption: true, + additionalPricePerServiceAccount: 6, + baseServiceAccount: 50, + } as SecretsManagerPlanFeaturesResponse, + } as PlanResponse; + + // Create mock subscription + mockSub = { + seats: 5, + smSeats: 5, + smServiceAccounts: 5, + maxStorageGb: 2, + customerDiscount: null, + } as OrganizationSubscriptionResponse; + + // Create mock organization + mockOrganization = { + useSecretsManager: false, + } as Organization; + }); + + it("should calculate pricing data correctly for password manager only", async () => { + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + false, + 50, // estimatedTax + ); + + expect(result).toEqual({ + selectedPlanInterval: "month", + passwordManagerSeats: 5, + passwordManagerSeatTotal: 240, // 48 * 5 + secretsManagerSeatTotal: 360, // 72 * 5 + additionalStorageTotal: 6, // 6 * (2 - 1) + additionalStoragePriceMonthly: 6, + additionalServiceAccountTotal: 0, // No additional service accounts (50 base vs 5 used) + totalAppliedDiscount: 0, + secretsManagerSubtotal: 360, // 0 + 360 + 0 + passwordManagerSubtotal: 246, // 0 + 240 + 6 + total: 296, // 246 + 50 (tax) - organization doesn't use secrets manager + organization: mockOrganization, + sub: mockSub, + selectedPlan: mockPlan, + selectedInterval: PlanInterval.Monthly, + discountPercentageFromSub: 0, + discountPercentage: 20, + acceptingSponsorship: false, + additionalServiceAccount: 0, // 50 - 5 = 45, which is > 0, so return 0 + storageGb: 1, + isSecretsManagerTrial: false, + estimatedTax: 50, + }); + }); + + it("should calculate pricing data correctly with secrets manager enabled", async () => { + mockOrganization.useSecretsManager = true; + + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + false, + 50, + ); + + expect(result.total).toBe(656); // passwordManagerSubtotal (246) + secretsManagerSubtotal (360) + tax (50) + }); + + it("should handle secrets manager trial", async () => { + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + true, // isSecretsManagerTrial + 50, + ); + + expect(result.passwordManagerSeatTotal).toBe(0); // Should be 0 during trial + expect(result.discountPercentageFromSub).toBe(0); // Should be 0 during trial + }); + + it("should handle premium access option", async () => { + mockPlan.PasswordManager.hasPremiumAccessOption = true; + mockPlan.PasswordManager.premiumAccessOptionPrice = 25; + + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + false, + 50, + ); + + expect(result.passwordManagerSubtotal).toBe(271); // 0 + 240 + 6 + 25 + }); + + it("should handle customer discount", async () => { + mockSub.customerDiscount = { + id: "discount1", + active: true, + percentOff: 10, + appliesTo: ["subscription"], + } as BillingCustomerDiscount; + + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + false, + 50, + ); + + expect(result.discountPercentageFromSub).toBe(10); + }); + + it("should handle zero storage calculation", async () => { + mockSub.maxStorageGb = 1; // Same as base storage + + const result = await service.getPricingSummaryData( + mockPlan, + mockSub, + mockOrganization, + PlanInterval.Monthly, + false, + 50, + ); + + expect(result.additionalStorageTotal).toBe(0); + expect(result.storageGb).toBe(0); + }); + }); + + describe("getAdditionalServiceAccount", () => { + let mockPlan: PlanResponse; + let mockSub: OrganizationSubscriptionResponse; + + beforeEach(() => { + mockPlan = { + SecretsManager: { + baseServiceAccount: 50, + } as SecretsManagerPlanFeaturesResponse, + } as PlanResponse; + + mockSub = { + smServiceAccounts: 55, + } as OrganizationSubscriptionResponse; + }); + + it("should return additional service accounts when used exceeds base", () => { + const result = service.getAdditionalServiceAccount(mockPlan, mockSub); + expect(result).toBe(5); // Math.abs(50 - 55) = 5 + }); + + it("should return 0 when used is less than or equal to base", () => { + mockSub.smServiceAccounts = 40; + const result = service.getAdditionalServiceAccount(mockPlan, mockSub); + expect(result).toBe(0); + }); + + it("should return 0 when used equals base", () => { + mockSub.smServiceAccounts = 50; + const result = service.getAdditionalServiceAccount(mockPlan, mockSub); + expect(result).toBe(0); + }); + + it("should return 0 when plan is null", () => { + const result = service.getAdditionalServiceAccount(null, mockSub); + expect(result).toBe(0); + }); + + it("should return 0 when plan has no SecretsManager", () => { + mockPlan.SecretsManager = null; + const result = service.getAdditionalServiceAccount(mockPlan, mockSub); + expect(result).toBe(0); + }); + }); +}); diff --git a/apps/web/src/app/billing/services/pricing-summary.service.ts b/apps/web/src/app/billing/services/pricing-summary.service.ts index b3c071a8b88..da2fe0e8dbb 100644 --- a/apps/web/src/app/billing/services/pricing-summary.service.ts +++ b/apps/web/src/app/billing/services/pricing-summary.service.ts @@ -31,9 +31,10 @@ export class PricingSummaryService { const additionalServiceAccount = this.getAdditionalServiceAccount(plan, sub); + const storageGb = Math.max(0, (sub?.maxStorageGb ?? 0) - plan.PasswordManager.baseStorageGb); + const additionalStorageTotal = plan.PasswordManager?.hasAdditionalStorageOption - ? plan.PasswordManager.additionalStoragePricePerGb * - (sub?.maxStorageGb ? sub.maxStorageGb - 1 : 0) + ? plan.PasswordManager.additionalStoragePricePerGb * storageGb : 0; const additionalStoragePriceMonthly = plan.PasswordManager?.additionalStoragePricePerGb || 0; @@ -66,7 +67,6 @@ export class PricingSummaryService { : (sub?.customerDiscount?.percentOff ?? 0); const discountPercentage = 20; const acceptingSponsorship = false; - const storageGb = sub?.maxStorageGb ? sub?.maxStorageGb - 1 : 0; const total = organization?.useSecretsManager ? passwordManagerSubtotal + secretsManagerSubtotal + estimatedTax diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index 71e3578a7c6..929f1489a61 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -1,5 +1,4 @@ -import { DOCUMENT } from "@angular/common"; -import { Inject, Injectable } from "@angular/core"; +import { Inject, Injectable, DOCUMENT } from "@angular/core"; import { firstValueFrom } from "rxjs"; import { AbstractThemingService } from "@bitwarden/angular/platform/services/theming/theming.service.abstraction"; diff --git a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts index 505a0df5032..54d62b8414a 100644 --- a/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts +++ b/apps/web/src/app/vault/components/browser-extension-prompt/browser-extension-prompt.component.ts @@ -1,5 +1,12 @@ -import { CommonModule, DOCUMENT } from "@angular/common"; -import { Component, Inject, OnDestroy, OnInit, ChangeDetectionStrategy } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { + Component, + Inject, + OnDestroy, + OnInit, + DOCUMENT, + ChangeDetectionStrategy, +} from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { ActivatedRoute } from "@angular/router"; import { map, Observable, of, tap } from "rxjs"; diff --git a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts index 9a974a395f0..cdee9163480 100644 --- a/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/add-extension-videos.component.ts @@ -1,5 +1,5 @@ -import { CommonModule, DOCUMENT } from "@angular/common"; -import { Component, ViewChildren, QueryList, ElementRef, inject } from "@angular/core"; +import { CommonModule } from "@angular/common"; +import { Component, ViewChildren, QueryList, ElementRef, inject, DOCUMENT } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { debounceTime, fromEvent } from "rxjs"; diff --git a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts index 809e404f5f1..cfc1961c4d8 100644 --- a/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts +++ b/apps/web/src/app/vault/components/setup-extension/setup-extension.component.ts @@ -1,5 +1,5 @@ -import { DOCUMENT, NgIf } from "@angular/common"; -import { Component, DestroyRef, inject, OnDestroy, OnInit } from "@angular/core"; +import { NgIf } from "@angular/common"; +import { Component, DestroyRef, inject, OnDestroy, OnInit, DOCUMENT } from "@angular/core"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { Router, RouterModule } from "@angular/router"; import { firstValueFrom, pairwise, startWith } from "rxjs"; diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index 90468c61d5c..582efade7f4 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -3060,7 +3060,16 @@ "message": "Upgrade your account to a Premium membership and unlock some great additional features." }, "premiumSignUpStorage": { - "message": "1 GB encrypted storage for file attachments." + "message": "1 GB encrypted storage for file attachments." + }, + "premiumSignUpStorageV2": { + "message": "$SIZE$ encrypted storage for file attachments.", + "placeholders": { + "size": { + "content": "$1", + "example": "1 GB" + } + } }, "premiumSignUpTwoStepOptions": { "message": "Proprietary two-step login options such as YubiKey and Duo." diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/manage-clients.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/manage-clients.component.ts index eed3db87396..4e128a79369 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/clients/manage-clients.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/clients/manage-clients.component.ts @@ -21,8 +21,6 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { PlanResponse } from "@bitwarden/common/billing/models/response/plan.response"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; import { @@ -100,19 +98,11 @@ export class ManageClientsComponent implements OnInit, OnDestroy { ), ); - protected providerPortalTakeover$ = this.configService.getFeatureFlag$( - FeatureFlag.PM21821_ProviderPortalTakeover, - ); - protected suspensionActive$ = combineLatest([ this.isAdminOrServiceUser$, - this.providerPortalTakeover$, this.provider$.pipe(map((provider) => provider?.enabled ?? false)), ]).pipe( - map( - ([isAdminOrServiceUser, portalTakeoverEnabled, providerEnabled]) => - isAdminOrServiceUser && portalTakeoverEnabled && !providerEnabled, - ), + map(([isAdminOrServiceUser, providerEnabled]) => isAdminOrServiceUser && !providerEnabled), ); private destroy$ = new Subject(); @@ -127,7 +117,6 @@ export class ManageClientsComponent implements OnInit, OnDestroy { private validationService: ValidationService, private webProviderService: WebProviderService, private billingNotificationService: BillingNotificationService, - private configService: ConfigService, private accountService: AccountService, private providerApiService: ProviderApiServiceAbstraction, ) {} diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html index ab4aaa6bd69..15536b22ae6 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.html @@ -12,7 +12,7 @@ route="clients" > ; protected clientsTranslationKey$: Observable; - protected providerPortalTakeover$: Observable; protected subscriber$: Observable; protected getTaxIdWarning$: () => Observable; @@ -56,7 +53,6 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { constructor( private route: ActivatedRoute, private providerService: ProviderService, - private configService: ConfigService, private providerWarningsService: ProviderWarningsService, private accountService: AccountService, ) {} @@ -101,10 +97,6 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { ) .subscribe(); - this.providerPortalTakeover$ = this.configService.getFeatureFlag$( - FeatureFlag.PM21821_ProviderPortalTakeover, - ); - this.subscriber$ = this.provider$.pipe( map((provider) => ({ type: "provider", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.spec.ts b/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.spec.ts index 0eb25bff524..b3e4cd9bcc7 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.spec.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.spec.ts @@ -5,7 +5,6 @@ import { of } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { ProviderId } from "@bitwarden/common/types/guid"; @@ -21,7 +20,6 @@ describe("ProviderWarningsService", () => { let service: ProviderWarningsService; let activatedRoute: MockProxy; let apiService: MockProxy; - let configService: MockProxy; let dialogService: MockProxy; let i18nService: MockProxy; let router: MockProxy; @@ -42,7 +40,6 @@ describe("ProviderWarningsService", () => { beforeEach(() => { activatedRoute = mock(); apiService = mock(); - configService = mock(); dialogService = mock(); i18nService = mock(); router = mock(); @@ -72,7 +69,6 @@ describe("ProviderWarningsService", () => { ProviderWarningsService, { provide: ActivatedRoute, useValue: activatedRoute }, { provide: ApiService, useValue: apiService }, - { provide: ConfigService, useValue: configService }, { provide: DialogService, useValue: dialogService }, { provide: I18nService, useValue: i18nService }, { provide: Router, useValue: router }, @@ -211,22 +207,7 @@ describe("ProviderWarningsService", () => { }); describe("showProviderSuspendedDialog$", () => { - it("should not show dialog when feature flag is disabled", (done) => { - configService.getFeatureFlag$.mockReturnValue(of(false)); - apiService.send.mockResolvedValue({ - Suspension: { Resolution: "add_payment_method" }, - }); - - service.showProviderSuspendedDialog$(provider).subscribe({ - complete: () => { - expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); - done(); - }, - }); - }); - it("should not show dialog when no suspension warning exists", (done) => { - configService.getFeatureFlag$.mockReturnValue(of(true)); apiService.send.mockResolvedValue({}); service.showProviderSuspendedDialog$(provider).subscribe({ @@ -239,7 +220,6 @@ describe("ProviderWarningsService", () => { it("should show add payment method dialog with cancellation date", (done) => { const cancelsAt = new Date(2024, 11, 31); - configService.getFeatureFlag$.mockReturnValue(of(true)); apiService.send.mockResolvedValue({ Suspension: { Resolution: "add_payment_method", @@ -282,7 +262,6 @@ describe("ProviderWarningsService", () => { }); it("should show add payment method dialog without cancellation date", (done) => { - configService.getFeatureFlag$.mockReturnValue(of(true)); apiService.send.mockResolvedValue({ Suspension: { Resolution: "add_payment_method", @@ -319,7 +298,6 @@ describe("ProviderWarningsService", () => { }); it("should show contact administrator dialog for contact_administrator resolution", (done) => { - configService.getFeatureFlag$.mockReturnValue(of(true)); apiService.send.mockResolvedValue({ Suspension: { Resolution: "contact_administrator", @@ -343,7 +321,6 @@ describe("ProviderWarningsService", () => { }); it("should show contact support dialog with action for contact_support resolution", (done) => { - configService.getFeatureFlag$.mockReturnValue(of(true)); apiService.send.mockResolvedValue({ Suspension: { Resolution: "contact_support", diff --git a/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.ts b/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.ts index 89ddf4b4788..7ff36cc2db8 100644 --- a/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.ts +++ b/bitwarden_license/bit-web/src/app/billing/providers/warnings/services/provider-warnings.service.ts @@ -2,7 +2,6 @@ import { Injectable } from "@angular/core"; import { ActivatedRoute, Router } from "@angular/router"; import { BehaviorSubject, - combineLatest, from, lastValueFrom, map, @@ -16,8 +15,6 @@ import { import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; -import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; -import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SyncService } from "@bitwarden/common/platform/sync"; import { ProviderId } from "@bitwarden/common/types/guid"; @@ -39,7 +36,6 @@ export class ProviderWarningsService { constructor( private activatedRoute: ActivatedRoute, private apiService: ApiService, - private configService: ConfigService, private dialogService: DialogService, private i18nService: I18nService, private router: Router, @@ -61,12 +57,9 @@ export class ProviderWarningsService { refreshTaxIdWarning = () => this.refreshTaxIdWarningTrigger.next(); showProviderSuspendedDialog$ = (provider: Provider): Observable => - combineLatest([ - this.configService.getFeatureFlag$(FeatureFlag.PM21821_ProviderPortalTakeover), - this.getWarning$(provider, (response) => response.suspension), - ]).pipe( - switchMap(async ([providerPortalTakeover, warning]) => { - if (!providerPortalTakeover || !warning) { + this.getWarning$(provider, (response) => response.suspension).pipe( + switchMap(async (warning) => { + if (!warning) { return; } diff --git a/eslint.config.mjs b/eslint.config.mjs index 1e12e0e1e19..881cd5e1f4d 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -71,6 +71,7 @@ export default tseslint.config( "@angular-eslint/no-output-on-prefix": 0, "@angular-eslint/no-output-rename": "error", "@angular-eslint/no-outputs-metadata-property": "error", + "@angular-eslint/prefer-inject": 0, "@angular-eslint/prefer-on-push-component-change-detection": "error", "@angular-eslint/prefer-output-emitter-ref": "error", "@angular-eslint/prefer-signals": "error", diff --git a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.spec.ts b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.spec.ts index 107eb068e76..30a4d38b1df 100644 --- a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.spec.ts +++ b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.spec.ts @@ -40,6 +40,7 @@ describe("PremiumUpgradeDialogComponent", () => { type: "standalone", annualPrice: 10, annualPricePerAdditionalStorageGB: 4, + providedStorageGB: 1, features: [ { key: "feature1", value: "Feature 1" }, { key: "feature2", value: "Feature 2" }, @@ -58,6 +59,7 @@ describe("PremiumUpgradeDialogComponent", () => { users: 6, annualPrice: 40, annualPricePerAdditionalStorageGB: 4, + providedStorageGB: 1, features: [{ key: "featureA", value: "Feature A" }], }, }; diff --git a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.stories.ts b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.stories.ts index 7ba09192d3c..7fd66878cae 100644 --- a/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.stories.ts +++ b/libs/angular/src/billing/components/premium-upgrade-dialog/premium-upgrade-dialog.component.stories.ts @@ -31,6 +31,7 @@ const mockPremiumTier: PersonalSubscriptionPricingTier = { type: "standalone", annualPrice: 10, annualPricePerAdditionalStorageGB: 4, + providedStorageGB: 1, features: [ { key: "builtInAuthenticator", value: "Built-in authenticator" }, { key: "secureFileStorage", value: "Secure file storage" }, diff --git a/libs/angular/src/billing/components/premium.component.ts b/libs/angular/src/billing/components/premium.component.ts index 6d0b90385ba..3f53d62e561 100644 --- a/libs/angular/src/billing/components/premium.component.ts +++ b/libs/angular/src/billing/components/premium.component.ts @@ -5,6 +5,7 @@ import { firstValueFrom, Observable, switchMap } from "rxjs"; import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service"; import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -16,6 +17,7 @@ import { DialogService, SimpleDialogOptions, ToastService } from "@bitwarden/com export class PremiumComponent implements OnInit { isPremium$: Observable; price = 10; + storageProvidedGb = 0; refreshPromise: Promise; cloudWebVaultUrl: string; @@ -29,6 +31,7 @@ export class PremiumComponent implements OnInit { billingAccountProfileStateService: BillingAccountProfileStateService, private toastService: ToastService, accountService: AccountService, + private billingApiService: BillingApiServiceAbstraction, ) { this.isPremium$ = accountService.activeAccount$.pipe( switchMap((account) => @@ -39,6 +42,9 @@ export class PremiumComponent implements OnInit { async ngOnInit() { this.cloudWebVaultUrl = await firstValueFrom(this.environmentService.cloudWebVaultUrl$); + const premiumResponse = await this.billingApiService.getPremiumPlan(); + this.storageProvidedGb = premiumResponse.storage.provided; + this.price = premiumResponse.seat.price; } async refresh() { diff --git a/libs/angular/src/components/modal/modal-injector.ts b/libs/angular/src/components/modal/modal-injector.ts index d1f6ac8d8d7..b3eb4d023d0 100644 --- a/libs/angular/src/components/modal/modal-injector.ts +++ b/libs/angular/src/components/modal/modal-injector.ts @@ -1,4 +1,4 @@ -import { InjectFlags, InjectOptions, Injector, ProviderToken } from "@angular/core"; +import { InjectOptions, Injector, ProviderToken } from "@angular/core"; export class ModalInjector implements Injector { constructor( @@ -12,8 +12,8 @@ export class ModalInjector implements Injector { options: InjectOptions & { optional?: false }, ): T; get(token: ProviderToken, notFoundValue: null, options: InjectOptions): T; - get(token: ProviderToken, notFoundValue?: T, options?: InjectOptions | InjectFlags): T; - get(token: ProviderToken, notFoundValue?: T, flags?: InjectFlags): T; + get(token: ProviderToken, notFoundValue?: T, options?: InjectOptions | null): T; + get(token: ProviderToken, notFoundValue?: T, flags?: null): T; get(token: any, notFoundValue?: any): any; get(token: any, notFoundValue?: any, flags?: any): any { return this._additionalTokens.get(token) ?? this._parentInjector.get(token, notFoundValue); diff --git a/libs/auth/src/angular/input-password/input-password.mdx b/libs/auth/src/angular/input-password/input-password.mdx index e272044a215..e3cdcbf08b9 100644 --- a/libs/auth/src/angular/input-password/input-password.mdx +++ b/libs/auth/src/angular/input-password/input-password.mdx @@ -1,4 +1,4 @@ -import { Meta, Story } from "@storybook/addon-docs"; +import { Meta, Story } from "@storybook/addon-docs/blocks"; import * as stories from "./input-password.stories.ts"; diff --git a/libs/auth/src/angular/input-password/input-password.stories.ts b/libs/auth/src/angular/input-password/input-password.stories.ts index 4ffd0e202ee..285ce94b269 100644 --- a/libs/auth/src/angular/input-password/input-password.stories.ts +++ b/libs/auth/src/angular/input-password/input-password.stories.ts @@ -1,8 +1,8 @@ import { importProvidersFrom } from "@angular/core"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { action } from "@storybook/addon-actions"; import { Meta, StoryObj, applicationConfig } from "@storybook/angular"; import { of } from "rxjs"; +import { action } from "storybook/actions"; import { ZXCVBNResult } from "zxcvbn"; import { AuditService } from "@bitwarden/common/abstractions/audit.service"; diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.mdx b/libs/auth/src/angular/registration/registration-start/registration-start.mdx index a9654de22fc..e4645ca664e 100644 --- a/libs/auth/src/angular/registration/registration-start/registration-start.mdx +++ b/libs/auth/src/angular/registration/registration-start/registration-start.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from "@storybook/addon-docs"; +import { Meta, Story, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./registration-start.stories"; diff --git a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts index 975e065e21e..6fb355a8a1b 100644 --- a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts +++ b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.spec.ts @@ -71,7 +71,7 @@ describe("DefaultLoginSuccessHandlerService", () => { it("should log error and return early", async () => { await service.run(userId); - expect(logService.error).toHaveBeenCalledWith("SSO login email not found."); + expect(logService.debug).toHaveBeenCalledWith("SSO login email not found."); expect(ssoLoginService.updateSsoRequiredCache).not.toHaveBeenCalled(); }); }); diff --git a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts index 27d058c311a..2b9672f1c0b 100644 --- a/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts +++ b/libs/auth/src/common/services/login-success-handler/default-login-success-handler.service.ts @@ -25,7 +25,7 @@ export class DefaultLoginSuccessHandlerService implements LoginSuccessHandlerSer const ssoLoginEmail = await this.ssoLoginService.getSsoEmail(); if (!ssoLoginEmail) { - this.logService.error("SSO login email not found."); + this.logService.debug("SSO login email not found."); return; } diff --git a/libs/common/src/admin-console/models/data/organization.data.spec.ts b/libs/common/src/admin-console/models/data/organization.data.spec.ts index 53fc0d5ec3e..4b74e03db8d 100644 --- a/libs/common/src/admin-console/models/data/organization.data.spec.ts +++ b/libs/common/src/admin-console/models/data/organization.data.spec.ts @@ -64,6 +64,7 @@ describe("ORGANIZATIONS state", () => { isAdminInitiated: false, ssoEnabled: false, ssoMemberDecryptionType: undefined, + usePhishingBlocker: false, }, }; const result = sut.deserializer(JSON.parse(JSON.stringify(expectedResult))); diff --git a/libs/common/src/admin-console/models/data/organization.data.ts b/libs/common/src/admin-console/models/data/organization.data.ts index 6424947d004..de0d21fbf17 100644 --- a/libs/common/src/admin-console/models/data/organization.data.ts +++ b/libs/common/src/admin-console/models/data/organization.data.ts @@ -67,6 +67,7 @@ export class OrganizationData { isAdminInitiated: boolean; ssoEnabled: boolean; ssoMemberDecryptionType?: MemberDecryptionType; + usePhishingBlocker: boolean; constructor( response?: ProfileOrganizationResponse, @@ -135,6 +136,7 @@ export class OrganizationData { this.isAdminInitiated = response.isAdminInitiated; this.ssoEnabled = response.ssoEnabled; this.ssoMemberDecryptionType = response.ssoMemberDecryptionType; + this.usePhishingBlocker = response.usePhishingBlocker; this.isMember = options.isMember; this.isProviderUser = options.isProviderUser; diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index 458ae1e8f0c..b2153024ef8 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -98,6 +98,7 @@ export class Organization { isAdminInitiated: boolean; ssoEnabled: boolean; ssoMemberDecryptionType?: MemberDecryptionType; + usePhishingBlocker: boolean; constructor(obj?: OrganizationData) { if (obj == null) { @@ -162,6 +163,7 @@ export class Organization { this.isAdminInitiated = obj.isAdminInitiated; this.ssoEnabled = obj.ssoEnabled; this.ssoMemberDecryptionType = obj.ssoMemberDecryptionType; + this.usePhishingBlocker = obj.usePhishingBlocker; } get canAccess() { diff --git a/libs/common/src/admin-console/models/response/organization.response.ts b/libs/common/src/admin-console/models/response/organization.response.ts index 3f6443678bf..cf3ae6a90f7 100644 --- a/libs/common/src/admin-console/models/response/organization.response.ts +++ b/libs/common/src/admin-console/models/response/organization.response.ts @@ -39,6 +39,7 @@ export class OrganizationResponse extends BaseResponse { limitItemDeletion: boolean; allowAdminAccessToAllCollectionItems: boolean; useAccessIntelligence: boolean; + usePhishingBlocker: boolean; constructor(response: any) { super(response); @@ -82,5 +83,6 @@ export class OrganizationResponse extends BaseResponse { ); // Map from backend API property (UseRiskInsights) to domain model property (useAccessIntelligence) this.useAccessIntelligence = this.getResponseProperty("UseRiskInsights"); + this.usePhishingBlocker = this.getResponseProperty("UsePhishingBlocker") ?? false; } } diff --git a/libs/common/src/admin-console/models/response/profile-organization.response.ts b/libs/common/src/admin-console/models/response/profile-organization.response.ts index 268ec1e4b6e..263e8e7d6b9 100644 --- a/libs/common/src/admin-console/models/response/profile-organization.response.ts +++ b/libs/common/src/admin-console/models/response/profile-organization.response.ts @@ -62,6 +62,7 @@ export class ProfileOrganizationResponse extends BaseResponse { isAdminInitiated: boolean; ssoEnabled: boolean; ssoMemberDecryptionType?: MemberDecryptionType; + usePhishingBlocker: boolean; constructor(response: any) { super(response); @@ -135,5 +136,6 @@ export class ProfileOrganizationResponse extends BaseResponse { this.isAdminInitiated = this.getResponseProperty("IsAdminInitiated"); this.ssoEnabled = this.getResponseProperty("SsoEnabled") ?? false; this.ssoMemberDecryptionType = this.getResponseProperty("SsoMemberDecryptionType"); + this.usePhishingBlocker = this.getResponseProperty("UsePhishingBlocker") ?? false; } } diff --git a/libs/common/src/billing/models/response/premium-plan.response.ts b/libs/common/src/billing/models/response/premium-plan.response.ts index f5df560a601..73e4f834c6f 100644 --- a/libs/common/src/billing/models/response/premium-plan.response.ts +++ b/libs/common/src/billing/models/response/premium-plan.response.ts @@ -4,10 +4,12 @@ export class PremiumPlanResponse extends BaseResponse { seat: { stripePriceId: string; price: number; + provided: number; }; storage: { stripePriceId: string; price: number; + provided: number; }; constructor(response: any) { @@ -30,6 +32,7 @@ export class PremiumPlanResponse extends BaseResponse { class PurchasableResponse extends BaseResponse { stripePriceId: string; price: number; + provided: number; constructor(response: any) { super(response); @@ -43,5 +46,9 @@ class PurchasableResponse extends BaseResponse { if (typeof this.price !== "number" || isNaN(this.price)) { throw new Error("PurchasableResponse: Missing or invalid 'Price' property"); } + this.provided = this.getResponseProperty("Provided"); + if (typeof this.provided !== "number" || isNaN(this.provided)) { + throw new Error("PurchasableResponse: Missing or invalid 'Provided' property"); + } } } diff --git a/libs/common/src/billing/services/subscription-pricing.service.spec.ts b/libs/common/src/billing/services/subscription-pricing.service.spec.ts index 8f5e9c0a3ab..76e15c646af 100644 --- a/libs/common/src/billing/services/subscription-pricing.service.spec.ts +++ b/libs/common/src/billing/services/subscription-pricing.service.spec.ts @@ -55,6 +55,7 @@ describe("DefaultSubscriptionPricingService", () => { basePrice: 36, seatPrice: 0, additionalStoragePricePerGb: 4, + providedStorageGB: 1, allowSeatAutoscale: false, maxSeats: 6, maxCollections: null, @@ -94,6 +95,7 @@ describe("DefaultSubscriptionPricingService", () => { basePrice: 0, seatPrice: 36, additionalStoragePricePerGb: 4, + providedStorageGB: 1, allowSeatAutoscale: true, maxSeats: null, maxCollections: null, @@ -359,6 +361,7 @@ describe("DefaultSubscriptionPricingService", () => { type: "standalone", annualPrice: 10, annualPricePerAdditionalStorageGB: 4, + providedStorageGB: 1, features: [ { key: "builtInAuthenticator", value: "Built-in authenticator" }, { key: "secureFileStorage", value: "Secure file storage" }, @@ -383,6 +386,7 @@ describe("DefaultSubscriptionPricingService", () => { annualPrice: mockFamiliesPlan.PasswordManager.basePrice, annualPricePerAdditionalStorageGB: mockFamiliesPlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: mockFamiliesPlan.PasswordManager.baseStorageGb, features: [ { key: "premiumAccounts", value: "6 premium accounts" }, { key: "familiesUnlimitedSharing", value: "Unlimited sharing for families" }, @@ -456,6 +460,7 @@ describe("DefaultSubscriptionPricingService", () => { expect(premiumTier.passwordManager.annualPrice).toEqual(10); expect(premiumTier.passwordManager.annualPricePerAdditionalStorageGB).toEqual(4); + expect(premiumTier.passwordManager.providedStorageGB).toEqual(1); expect(familiesTier.passwordManager.annualPrice).toEqual( mockFamiliesPlan.PasswordManager.basePrice, @@ -463,6 +468,9 @@ describe("DefaultSubscriptionPricingService", () => { expect(familiesTier.passwordManager.annualPricePerAdditionalStorageGB).toEqual( mockFamiliesPlan.PasswordManager.additionalStoragePricePerGb, ); + expect(familiesTier.passwordManager.providedStorageGB).toEqual( + mockFamiliesPlan.PasswordManager.baseStorageGb, + ); done(); }); @@ -487,6 +495,7 @@ describe("DefaultSubscriptionPricingService", () => { annualPricePerUser: mockTeamsPlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: mockTeamsPlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: mockTeamsPlan.PasswordManager.baseStorageGb, features: [ { key: "secureItemSharing", value: "Secure item sharing" }, { key: "eventLogMonitoring", value: "Event log monitoring" }, @@ -522,6 +531,7 @@ describe("DefaultSubscriptionPricingService", () => { annualPricePerUser: mockEnterprisePlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: mockEnterprisePlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: mockEnterprisePlan.PasswordManager.baseStorageGb, features: [ { key: "enterpriseSecurityPolicies", value: "Enterprise security policies" }, { key: "passwordLessSso", value: "Passwordless SSO" }, @@ -648,6 +658,9 @@ describe("DefaultSubscriptionPricingService", () => { expect(teamsSecretsManager.annualPricePerAdditionalServiceAccount).toEqual( mockTeamsPlan.SecretsManager.additionalPricePerServiceAccount, ); + expect(teamsPasswordManager.providedStorageGB).toEqual( + mockTeamsPlan.PasswordManager.baseStorageGb, + ); const enterprisePasswordManager = enterpriseTier.passwordManager as any; const enterpriseSecretsManager = enterpriseTier.secretsManager as any; @@ -657,6 +670,9 @@ describe("DefaultSubscriptionPricingService", () => { expect(enterprisePasswordManager.annualPricePerAdditionalStorageGB).toEqual( mockEnterprisePlan.PasswordManager.additionalStoragePricePerGb, ); + expect(enterprisePasswordManager.providedStorageGB).toEqual( + mockEnterprisePlan.PasswordManager.baseStorageGb, + ); expect(enterpriseSecretsManager.annualPricePerUser).toEqual( mockEnterprisePlan.SecretsManager.seatPrice, ); @@ -729,6 +745,7 @@ describe("DefaultSubscriptionPricingService", () => { annualPricePerUser: mockTeamsPlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: mockTeamsPlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: mockTeamsPlan.PasswordManager.baseStorageGb, features: [ { key: "secureItemSharing", value: "Secure item sharing" }, { key: "eventLogMonitoring", value: "Event log monitoring" }, @@ -764,6 +781,7 @@ describe("DefaultSubscriptionPricingService", () => { annualPricePerUser: mockEnterprisePlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: mockEnterprisePlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: mockEnterprisePlan.PasswordManager.baseStorageGb, features: [ { key: "enterpriseSecurityPolicies", value: "Enterprise security policies" }, { key: "passwordLessSso", value: "Passwordless SSO" }, diff --git a/libs/common/src/billing/services/subscription-pricing.service.ts b/libs/common/src/billing/services/subscription-pricing.service.ts index f1502eb26e8..a3f048fee78 100644 --- a/libs/common/src/billing/services/subscription-pricing.service.ts +++ b/libs/common/src/billing/services/subscription-pricing.service.ts @@ -40,6 +40,7 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer */ private static readonly FALLBACK_PREMIUM_SEAT_PRICE = 10; private static readonly FALLBACK_PREMIUM_STORAGE_PRICE = 4; + private static readonly FALLBACK_PREMIUM_PROVIDED_STORAGE_GB = 1; constructor( private billingApiService: BillingApiServiceAbstraction, @@ -114,11 +115,13 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer map((premiumPlan) => ({ seat: premiumPlan.seat.price, storage: premiumPlan.storage.price, + provided: premiumPlan.storage.provided, })), ) : of({ seat: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_SEAT_PRICE, storage: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_STORAGE_PRICE, + provided: DefaultSubscriptionPricingService.FALLBACK_PREMIUM_PROVIDED_STORAGE_GB, }), ), map((premiumPrices) => ({ @@ -130,6 +133,7 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer type: "standalone", annualPrice: premiumPrices.seat, annualPricePerAdditionalStorageGB: premiumPrices.storage, + providedStorageGB: premiumPrices.provided, features: [ this.featureTranslations.builtInAuthenticator(), this.featureTranslations.secureFileStorage(), @@ -161,6 +165,7 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer annualPrice: familiesPlan.PasswordManager.basePrice, annualPricePerAdditionalStorageGB: familiesPlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: familiesPlan.PasswordManager.baseStorageGb, features: [ this.featureTranslations.premiumAccounts(), this.featureTranslations.familiesUnlimitedSharing(), @@ -214,6 +219,7 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer annualPricePerUser: annualTeamsPlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: annualTeamsPlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: annualTeamsPlan.PasswordManager.baseStorageGb, features: [ this.featureTranslations.secureItemSharing(), this.featureTranslations.eventLogMonitoring(), @@ -253,6 +259,7 @@ export class DefaultSubscriptionPricingService implements SubscriptionPricingSer annualPricePerUser: annualEnterprisePlan.PasswordManager.seatPrice, annualPricePerAdditionalStorageGB: annualEnterprisePlan.PasswordManager.additionalStoragePricePerGb, + providedStorageGB: annualEnterprisePlan.PasswordManager.baseStorageGb, features: [ this.featureTranslations.enterpriseSecurityPolicies(), this.featureTranslations.passwordLessSso(), diff --git a/libs/common/src/billing/types/subscription-pricing-tier.ts b/libs/common/src/billing/types/subscription-pricing-tier.ts index 8febc4b86db..3f5c076ba4f 100644 --- a/libs/common/src/billing/types/subscription-pricing-tier.ts +++ b/libs/common/src/billing/types/subscription-pricing-tier.ts @@ -30,13 +30,19 @@ type HasAdditionalStorage = { annualPricePerAdditionalStorageGB: number; }; +type HasProvidedStorage = { + providedStorageGB: number; +}; + type StandalonePasswordManager = HasFeatures & - HasAdditionalStorage & { + HasAdditionalStorage & + HasProvidedStorage & { type: "standalone"; annualPrice: number; }; type PackagedPasswordManager = HasFeatures & + HasProvidedStorage & HasAdditionalStorage & { type: "packaged"; users: number; @@ -52,6 +58,7 @@ type CustomPasswordManager = HasFeatures & { }; type ScalablePasswordManager = HasFeatures & + HasProvidedStorage & HasAdditionalStorage & { type: "scalable"; annualPricePerUser: number; diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index be2ea75203c..01ffdafcef9 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -24,7 +24,6 @@ export enum FeatureFlag { /* Billing */ TrialPaymentOptional = "PM-8163-trial-payment", - PM21821_ProviderPortalTakeover = "pm-21821-provider-portal-takeover", PM22415_TaxIDWarnings = "pm-22415-tax-id-warnings", PM24032_NewNavigationPremiumUpgradeButton = "pm-24032-new-navigation-premium-upgrade-button", PM25379_UseNewOrganizationMetadataStructure = "pm-25379-use-new-organization-metadata-structure", @@ -72,6 +71,9 @@ export enum FeatureFlag { /* Innovation */ PM19148_InnovationArchive = "pm-19148-innovation-archive", + /* Desktop */ + DesktopUiMigrationMilestone1 = "desktop-ui-migration-milestone-1", + /* UIF */ RouterFocusManagement = "router-focus-management", } @@ -123,7 +125,6 @@ export const DefaultFeatureFlagValue = { /* Billing */ [FeatureFlag.TrialPaymentOptional]: FALSE, - [FeatureFlag.PM21821_ProviderPortalTakeover]: FALSE, [FeatureFlag.PM22415_TaxIDWarnings]: FALSE, [FeatureFlag.PM24032_NewNavigationPremiumUpgradeButton]: FALSE, [FeatureFlag.PM25379_UseNewOrganizationMetadataStructure]: FALSE, @@ -152,6 +153,9 @@ export const DefaultFeatureFlagValue = { /* Innovation */ [FeatureFlag.PM19148_InnovationArchive]: FALSE, + /* Desktop */ + [FeatureFlag.DesktopUiMigrationMilestone1]: FALSE, + /* UIF */ [FeatureFlag.RouterFocusManagement]: FALSE, } satisfies Record; diff --git a/libs/common/src/key-management/crypto/abstractions/crypto-function.service.ts b/libs/common/src/key-management/crypto/abstractions/crypto-function.service.ts index 705a1c1a24e..b16371198b3 100644 --- a/libs/common/src/key-management/crypto/abstractions/crypto-function.service.ts +++ b/libs/common/src/key-management/crypto/abstractions/crypto-function.service.ts @@ -91,7 +91,7 @@ export abstract class CryptoFunctionService { abstract rsaEncrypt( data: Uint8Array, publicKey: Uint8Array, - algorithm: "sha1" | "sha256", + algorithm: "sha1", ): Promise; /** * @deprecated HAZMAT WARNING: DO NOT USE THIS FOR NEW CODE. Implement low-level crypto operations @@ -100,10 +100,10 @@ export abstract class CryptoFunctionService { abstract rsaDecrypt( data: Uint8Array, privateKey: Uint8Array, - algorithm: "sha1" | "sha256", + algorithm: "sha1", ): Promise; abstract rsaExtractPublicKey(privateKey: Uint8Array): Promise; - abstract rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[Uint8Array, Uint8Array]>; + abstract rsaGenerateKeyPair(length: 2048): Promise<[Uint8Array, Uint8Array]>; /** * Generates a key of the given length suitable for use in AES encryption */ diff --git a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts index 132bbc306cb..a5da0c82382 100644 --- a/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts +++ b/libs/common/src/key-management/crypto/services/encrypt.service.implementation.ts @@ -252,15 +252,9 @@ export class EncryptServiceImplementation implements EncryptService { throw new Error("[Encrypt service] rsaDecrypt: No data provided for decryption."); } - let algorithm: "sha1" | "sha256"; switch (data.encryptionType) { case EncryptionType.Rsa2048_OaepSha1_B64: case EncryptionType.Rsa2048_OaepSha1_HmacSha256_B64: - algorithm = "sha1"; - break; - case EncryptionType.Rsa2048_OaepSha256_B64: - case EncryptionType.Rsa2048_OaepSha256_HmacSha256_B64: - algorithm = "sha256"; break; default: throw new Error("Invalid encryption type."); @@ -270,6 +264,6 @@ export class EncryptServiceImplementation implements EncryptService { throw new Error("[Encrypt service] rsaDecrypt: No private key provided for decryption."); } - return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, algorithm); + return this.cryptoFunctionService.rsaDecrypt(data.dataBytes, privateKey, "sha1"); } } diff --git a/libs/common/src/key-management/crypto/services/web-crypto-function.service.spec.ts b/libs/common/src/key-management/crypto/services/web-crypto-function.service.spec.ts index c0b5150a720..c64926e0e5b 100644 --- a/libs/common/src/key-management/crypto/services/web-crypto-function.service.spec.ts +++ b/libs/common/src/key-management/crypto/services/web-crypto-function.service.spec.ts @@ -1,12 +1,20 @@ import { mock } from "jest-mock-extended"; import { PlatformUtilsService } from "../../../platform/abstractions/platform-utils.service"; +import { SdkLoadService } from "../../../platform/abstractions/sdk/sdk-load.service"; import { Utils } from "../../../platform/misc/utils"; import { EcbDecryptParameters } from "../../../platform/models/domain/decrypt-parameters"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { WebCryptoFunctionService } from "./web-crypto-function.service"; +class TestSdkLoadService extends SdkLoadService { + protected override load(): Promise { + // Simulate successful WASM load + return Promise.resolve(); + } +} + const RsaPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + @@ -40,6 +48,10 @@ const Sha512Mac = "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; describe("WebCrypto Function Service", () => { + beforeAll(async () => { + await new TestSdkLoadService().loadAndInit(); + }); + describe("pbkdf2", () => { const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; @@ -287,7 +299,6 @@ describe("WebCrypto Function Service", () => { }); describe("rsaGenerateKeyPair", () => { - testRsaGenerateKeyPair(1024); testRsaGenerateKeyPair(2048); // Generating 4096 bit keys can be slow. Commenting it out to save CI. @@ -483,7 +494,7 @@ function testHmac(algorithm: "sha1" | "sha256" | "sha512", mac: string) { }); } -function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { +function testRsaGenerateKeyPair(length: 2048) { it( "should successfully generate a " + length + " bit key pair", async () => { diff --git a/libs/common/src/key-management/crypto/services/web-crypto-function.service.ts b/libs/common/src/key-management/crypto/services/web-crypto-function.service.ts index 11c186bc393..ee0b5cab902 100644 --- a/libs/common/src/key-management/crypto/services/web-crypto-function.service.ts +++ b/libs/common/src/key-management/crypto/services/web-crypto-function.service.ts @@ -1,5 +1,8 @@ import * as forge from "node-forge"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; +import { PureCrypto } from "@bitwarden/sdk-internal"; + import { EncryptionType } from "../../../platform/enums"; import { Utils } from "../../../platform/misc/utils"; import { @@ -260,57 +263,31 @@ export class WebCryptoFunctionService implements CryptoFunctionService { async rsaEncrypt( data: Uint8Array, publicKey: Uint8Array, - algorithm: "sha1" | "sha256", + _algorithm: "sha1", ): Promise { - // Note: Edge browser requires that we specify name and hash for both key import and decrypt. - // We cannot use the proper types here. - const rsaParams = { - name: "RSA-OAEP", - hash: { name: this.toWebCryptoAlgorithm(algorithm) }, - }; - const impKey = await this.subtle.importKey("spki", publicKey, rsaParams, false, ["encrypt"]); - const buffer = await this.subtle.encrypt(rsaParams, impKey, data); - return new Uint8Array(buffer); + await SdkLoadService.Ready; + return PureCrypto.rsa_encrypt_data(data, publicKey); } async rsaDecrypt( data: Uint8Array, privateKey: Uint8Array, - algorithm: "sha1" | "sha256", + _algorithm: "sha1", ): Promise { - // Note: Edge browser requires that we specify name and hash for both key import and decrypt. - // We cannot use the proper types here. - const rsaParams = { - name: "RSA-OAEP", - hash: { name: this.toWebCryptoAlgorithm(algorithm) }, - }; - const impKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, false, ["decrypt"]); - const buffer = await this.subtle.decrypt(rsaParams, impKey, data); - return new Uint8Array(buffer); + await SdkLoadService.Ready; + return PureCrypto.rsa_decrypt_data(data, privateKey); } - async rsaExtractPublicKey(privateKey: Uint8Array): Promise { - const rsaParams = { - name: "RSA-OAEP", - // Have to specify some algorithm - hash: { name: this.toWebCryptoAlgorithm("sha1") }, - }; - const impPrivateKey = await this.subtle.importKey("pkcs8", privateKey, rsaParams, true, [ - "decrypt", - ]); - const jwkPrivateKey = await this.subtle.exportKey("jwk", impPrivateKey); - const jwkPublicKeyParams = { - kty: "RSA", - e: jwkPrivateKey.e, - n: jwkPrivateKey.n, - alg: "RSA-OAEP", - ext: true, - }; - const impPublicKey = await this.subtle.importKey("jwk", jwkPublicKeyParams, rsaParams, true, [ - "encrypt", - ]); - const buffer = await this.subtle.exportKey("spki", impPublicKey); - return new Uint8Array(buffer) as UnsignedPublicKey; + async rsaExtractPublicKey(privateKey: Uint8Array): Promise { + await SdkLoadService.Ready; + return PureCrypto.rsa_extract_public_key(privateKey) as UnsignedPublicKey; + } + + async rsaGenerateKeyPair(_length: 2048): Promise<[UnsignedPublicKey, Uint8Array]> { + await SdkLoadService.Ready; + const privateKey = PureCrypto.rsa_generate_keypair(); + const publicKey = await this.rsaExtractPublicKey(privateKey); + return [publicKey, privateKey]; } async aesGenerateKey(bitLength = 128 | 192 | 256 | 512): Promise { @@ -330,20 +307,6 @@ export class WebCryptoFunctionService implements CryptoFunctionService { return new Uint8Array(rawKey) as CsprngArray; } - async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[Uint8Array, Uint8Array]> { - const rsaParams = { - name: "RSA-OAEP", - modulusLength: length, - publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 65537 - // Have to specify some algorithm - hash: { name: this.toWebCryptoAlgorithm("sha1") }, - }; - const keyPair = await this.subtle.generateKey(rsaParams, true, ["encrypt", "decrypt"]); - const publicKey = await this.subtle.exportKey("spki", keyPair.publicKey); - const privateKey = await this.subtle.exportKey("pkcs8", keyPair.privateKey); - return [new Uint8Array(publicKey), new Uint8Array(privateKey)]; - } - randomBytes(length: number): Promise { const arr = new Uint8Array(length); this.crypto.getRandomValues(arr); diff --git a/libs/common/src/models/domain/domain-service.ts b/libs/common/src/models/domain/domain-service.ts index a6b5ecfdaac..bfbe45450d0 100644 --- a/libs/common/src/models/domain/domain-service.ts +++ b/libs/common/src/models/domain/domain-service.ts @@ -1,3 +1,5 @@ +import { UriMatchType } from "@bitwarden/sdk-internal"; + /* See full documentation at: https://bitwarden.com/help/uri-match-detection/#match-detection-options @@ -23,3 +25,28 @@ export type UriMatchStrategySetting = (typeof UriMatchStrategy)[keyof typeof Uri // using uniqueness properties of object shape over Set for ease of state storability export type NeverDomains = { [id: string]: null | { bannerIsDismissed?: boolean } }; export type EquivalentDomains = string[][]; + +/** + * Normalizes UriMatchStrategySetting for SDK mapping. + * @param value - The URI match strategy from user data + * @returns Valid UriMatchType or undefined if invalid + */ +export function normalizeUriMatchStrategyForSdk( + value: UriMatchStrategySetting | undefined, +): UriMatchType | undefined { + if (value == null) { + return undefined; + } + + switch (value) { + case 0: // Domain + case 1: // Host + case 2: // StartsWith + case 3: // Exact + case 4: // RegularExpression + case 5: // Never + return value; + default: + return undefined; + } +} diff --git a/libs/common/src/vault/abstractions/cipher-encryption.service.ts b/libs/common/src/vault/abstractions/cipher-encryption.service.ts index 6057a91bae5..fdd42c0acf2 100644 --- a/libs/common/src/vault/abstractions/cipher-encryption.service.ts +++ b/libs/common/src/vault/abstractions/cipher-encryption.service.ts @@ -67,7 +67,10 @@ export abstract class CipherEncryptionService { * * @returns A promise that resolves to an array of decrypted cipher views */ - abstract decryptManyLegacy(ciphers: Cipher[], userId: UserId): Promise; + abstract decryptManyLegacy( + ciphers: Cipher[], + userId: UserId, + ): Promise<[CipherView[], CipherView[]]>; /** * Decrypts many ciphers using the SDK for the given userId, and returns a list of * failures. diff --git a/libs/common/src/vault/enums/cipher-reprompt-type.ts b/libs/common/src/vault/enums/cipher-reprompt-type.ts index 91b05399d32..30d99e98005 100644 --- a/libs/common/src/vault/enums/cipher-reprompt-type.ts +++ b/libs/common/src/vault/enums/cipher-reprompt-type.ts @@ -1,3 +1,5 @@ +import { CipherRepromptType as SdkCipherRepromptType } from "@bitwarden/sdk-internal"; + import { UnionOfValues } from "../types/union-of-values"; export const CipherRepromptType = { @@ -6,3 +8,20 @@ export const CipherRepromptType = { } as const; export type CipherRepromptType = UnionOfValues; + +/** + * Normalizes a CipherRepromptType value to ensure compatibility with the SDK. + * @param value - The cipher reprompt type from user data + * @returns Valid CipherRepromptType, defaults to CipherRepromptType.None if unrecognized + */ +export function normalizeCipherRepromptTypeForSdk( + value: CipherRepromptType, +): SdkCipherRepromptType { + switch (value) { + case CipherRepromptType.None: + case CipherRepromptType.Password: + return value; + default: + return CipherRepromptType.None; + } +} diff --git a/libs/common/src/vault/enums/field-type.enum.ts b/libs/common/src/vault/enums/field-type.enum.ts index 0e8e2aaca3d..3d819adbba6 100644 --- a/libs/common/src/vault/enums/field-type.enum.ts +++ b/libs/common/src/vault/enums/field-type.enum.ts @@ -1,3 +1,5 @@ +import { FieldType as SdkFieldType } from "@bitwarden/sdk-internal"; + const _FieldType = Object.freeze({ Text: 0, Hidden: 1, @@ -10,3 +12,20 @@ type _FieldType = typeof _FieldType; export type FieldType = _FieldType[keyof _FieldType]; export const FieldType: Record = _FieldType; + +/** + * Normalizes a FieldType value to ensure compatibility with the SDK. + * @param value - The field type from user data + * @returns Valid FieldType, defaults to FieldType.Text if unrecognized + */ +export function normalizeFieldTypeForSdk(value: FieldType): SdkFieldType { + switch (value) { + case FieldType.Text: + case FieldType.Hidden: + case FieldType.Boolean: + case FieldType.Linked: + return value; + default: + return FieldType.Text; + } +} diff --git a/libs/common/src/vault/enums/linked-id-type.enum.ts b/libs/common/src/vault/enums/linked-id-type.enum.ts index 20ef15e6207..38b852f94a7 100644 --- a/libs/common/src/vault/enums/linked-id-type.enum.ts +++ b/libs/common/src/vault/enums/linked-id-type.enum.ts @@ -1,3 +1,5 @@ +import { LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal"; + import { UnionOfValues } from "../types/union-of-values"; export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId; @@ -46,3 +48,25 @@ export const IdentityLinkedId = { } as const; export type IdentityLinkedId = UnionOfValues; + +/** + * Normalizes a LinkedIdType value to ensure compatibility with the SDK. + * @param value - The linked ID type from user data + * @returns Valid LinkedIdType or undefined if unrecognized + */ +export function normalizeLinkedIdTypeForSdk( + value: LinkedIdType | undefined, +): SdkLinkedIdType | undefined { + if (value == null) { + return undefined; + } + + // Check all valid LinkedId numeric values (100-418) + const allValidValues = [ + ...Object.values(LoginLinkedId), + ...Object.values(CardLinkedId), + ...Object.values(IdentityLinkedId), + ]; + + return allValidValues.includes(value) ? value : undefined; +} diff --git a/libs/common/src/vault/enums/secure-note-type.enum.ts b/libs/common/src/vault/enums/secure-note-type.enum.ts index bb5838d028c..348841c7dfc 100644 --- a/libs/common/src/vault/enums/secure-note-type.enum.ts +++ b/libs/common/src/vault/enums/secure-note-type.enum.ts @@ -1,3 +1,5 @@ +import { SecureNoteType as SdkSecureNoteType } from "@bitwarden/sdk-internal"; + import { UnionOfValues } from "../types/union-of-values"; export const SecureNoteType = { @@ -5,3 +7,12 @@ export const SecureNoteType = { } as const; export type SecureNoteType = UnionOfValues; + +/** + * Normalizes a SecureNoteType value to ensure compatibility with the SDK. + * @param value - The secure note type from user data + * @returns Valid SecureNoteType, defaults to SecureNoteType.Generic if unrecognized + */ +export function normalizeSecureNoteTypeForSdk(value: SecureNoteType): SdkSecureNoteType { + return SecureNoteType.Generic; +} diff --git a/libs/common/src/vault/models/domain/cipher.ts b/libs/common/src/vault/models/domain/cipher.ts index bbbf6a6a054..abddb73422b 100644 --- a/libs/common/src/vault/models/domain/cipher.ts +++ b/libs/common/src/vault/models/domain/cipher.ts @@ -9,7 +9,10 @@ import { Utils } from "../../../platform/misc/utils"; import Domain from "../../../platform/models/domain/domain-base"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; -import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; +import { + CipherRepromptType, + normalizeCipherRepromptTypeForSdk, +} from "../../enums/cipher-reprompt-type"; import { CipherType } from "../../enums/cipher-type"; import { conditionalEncString, encStringFrom } from "../../utils/domain-utils"; import { CipherPermissionsApi } from "../api/cipher-permissions.api"; @@ -414,10 +417,7 @@ export class Cipher extends Domain implements Decryptable { creationDate: this.creationDate.toISOString(), deletedDate: this.deletedDate?.toISOString(), archivedDate: this.archivedDate?.toISOString(), - reprompt: - this.reprompt === CipherRepromptType.Password - ? CipherRepromptType.Password - : CipherRepromptType.None, + reprompt: normalizeCipherRepromptTypeForSdk(this.reprompt), // Initialize all cipher-type-specific properties as undefined login: undefined, identity: undefined, diff --git a/libs/common/src/vault/models/domain/field.ts b/libs/common/src/vault/models/domain/field.ts index 2ee3a9af8a5..adec9263515 100644 --- a/libs/common/src/vault/models/domain/field.ts +++ b/libs/common/src/vault/models/domain/field.ts @@ -1,11 +1,16 @@ import { Jsonify } from "type-fest"; -import { Field as SdkField, LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal"; +import { Field as SdkField } from "@bitwarden/sdk-internal"; import { EncString } from "../../../key-management/crypto/models/enc-string"; import Domain from "../../../platform/models/domain/domain-base"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; -import { FieldType, LinkedIdType } from "../../enums"; +import { + FieldType, + LinkedIdType, + normalizeFieldTypeForSdk, + normalizeLinkedIdTypeForSdk, +} from "../../enums"; import { conditionalEncString, encStringFrom } from "../../utils/domain-utils"; import { FieldData } from "../data/field.data"; import { FieldView } from "../view/field.view"; @@ -77,9 +82,8 @@ export class Field extends Domain { return { name: this.name?.toSdk(), value: this.value?.toSdk(), - type: this.type, - // Safe type cast: client and SDK LinkedIdType enums have identical values - linkedId: this.linkedId as unknown as SdkLinkedIdType, + type: normalizeFieldTypeForSdk(this.type), + linkedId: normalizeLinkedIdTypeForSdk(this.linkedId), }; } diff --git a/libs/common/src/vault/models/domain/login-uri.ts b/libs/common/src/vault/models/domain/login-uri.ts index cac487747f8..42acca25d6f 100644 --- a/libs/common/src/vault/models/domain/login-uri.ts +++ b/libs/common/src/vault/models/domain/login-uri.ts @@ -3,7 +3,10 @@ import { Jsonify } from "type-fest"; import { LoginUri as SdkLoginUri } from "@bitwarden/sdk-internal"; import { EncString } from "../../../key-management/crypto/models/enc-string"; -import { UriMatchStrategySetting } from "../../../models/domain/domain-service"; +import { + normalizeUriMatchStrategyForSdk, + UriMatchStrategySetting, +} from "../../../models/domain/domain-service"; import { Utils } from "../../../platform/misc/utils"; import Domain from "../../../platform/models/domain/domain-base"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; @@ -91,7 +94,7 @@ export class LoginUri extends Domain { return { uri: this.uri?.toSdk(), uriChecksum: this.uriChecksum?.toSdk(), - match: this.match, + match: normalizeUriMatchStrategyForSdk(this.match), }; } diff --git a/libs/common/src/vault/models/domain/secure-note.ts b/libs/common/src/vault/models/domain/secure-note.ts index fb568f482b0..688d34830db 100644 --- a/libs/common/src/vault/models/domain/secure-note.ts +++ b/libs/common/src/vault/models/domain/secure-note.ts @@ -3,7 +3,7 @@ import { Jsonify } from "type-fest"; import { SecureNote as SdkSecureNote } from "@bitwarden/sdk-internal"; import Domain from "../../../platform/models/domain/domain-base"; -import { SecureNoteType } from "../../enums"; +import { normalizeSecureNoteTypeForSdk, SecureNoteType } from "../../enums"; import { SecureNoteData } from "../data/secure-note.data"; import { SecureNoteView } from "../view/secure-note.view"; @@ -46,7 +46,7 @@ export class SecureNote extends Domain { */ toSdkSecureNote(): SdkSecureNote { return { - type: this.type, + type: normalizeSecureNoteTypeForSdk(this.type), }; } diff --git a/libs/common/src/vault/models/view/cipher.view.ts b/libs/common/src/vault/models/view/cipher.view.ts index 0d4ab8e5207..db360f7f991 100644 --- a/libs/common/src/vault/models/view/cipher.view.ts +++ b/libs/common/src/vault/models/view/cipher.view.ts @@ -166,10 +166,6 @@ export class CipherView implements View, InitializerMetadata { } get canAssignToCollections(): boolean { - if (this.isArchived) { - return false; - } - if (this.organizationId == null) { return true; } diff --git a/libs/common/src/vault/services/cipher.service.spec.ts b/libs/common/src/vault/services/cipher.service.spec.ts index 85ce8bd0423..fe2926144b8 100644 --- a/libs/common/src/vault/services/cipher.service.spec.ts +++ b/libs/common/src/vault/services/cipher.service.spec.ts @@ -807,7 +807,7 @@ describe("Cipher Service", () => { // Set up expected results const expectedSuccessCipherViews = [ - { id: mockCiphers[0].id, name: "Success 1" } as unknown as CipherListView, + { id: mockCiphers[0].id, name: "Success 1", decryptionFailure: false } as CipherView, ]; const expectedFailedCipher = new CipherView(mockCiphers[1]); @@ -815,6 +815,11 @@ describe("Cipher Service", () => { expectedFailedCipher.decryptionFailure = true; const expectedFailedCipherViews = [expectedFailedCipher]; + cipherEncryptionService.decryptManyLegacy.mockResolvedValue([ + expectedSuccessCipherViews, + expectedFailedCipherViews, + ]); + // Execute const [successes, failures] = await (cipherService as any).decryptCiphers( mockCiphers, @@ -822,10 +827,7 @@ describe("Cipher Service", () => { ); // Verify the SDK was used for decryption - expect(cipherEncryptionService.decryptManyWithFailures).toHaveBeenCalledWith( - mockCiphers, - userId, - ); + expect(cipherEncryptionService.decryptManyLegacy).toHaveBeenCalledWith(mockCiphers, userId); expect(successes).toEqual(expectedSuccessCipherViews); expect(failures).toEqual(expectedFailedCipherViews); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 72c1ca40913..b2c5ac8943c 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -2143,15 +2143,19 @@ export class CipherService implements CipherServiceAbstraction { userId: UserId, fullDecryption: boolean = true, ): Promise<[CipherViewLike[], CipherView[]]> { + if (fullDecryption) { + const [decryptedViews, failedViews] = await this.cipherEncryptionService.decryptManyLegacy( + ciphers, + userId, + ); + return [decryptedViews.sort(this.getLocaleSortingFunction()), failedViews]; + } + const [decrypted, failures] = await this.cipherEncryptionService.decryptManyWithFailures( ciphers, userId, ); - const decryptedViews = fullDecryption - ? await Promise.all(decrypted.map((c) => this.getFullCipherView(c))) - : decrypted; - const failedViews = failures.map((c) => { const cipher_view = new CipherView(c); cipher_view.name = "[error: cannot decrypt]"; @@ -2159,7 +2163,7 @@ export class CipherService implements CipherServiceAbstraction { return cipher_view; }); - return [decryptedViews.sort(this.getLocaleSortingFunction()), failedViews]; + return [decrypted.sort(this.getLocaleSortingFunction()), failedViews]; } /** Fetches the full `CipherView` when a `CipherListView` is passed. */ diff --git a/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts b/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts index 6d6341bd1fa..f54dfa17a38 100644 --- a/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts +++ b/libs/common/src/vault/services/default-cipher-encryption.service.spec.ts @@ -496,9 +496,11 @@ describe("DefaultCipherEncryptionService", () => { .mockReturnValueOnce(expectedViews[0]) .mockReturnValueOnce(expectedViews[1]); - const result = await cipherEncryptionService.decryptManyLegacy(ciphers, userId); + const [successfulDecryptions, failedDecryptions] = + await cipherEncryptionService.decryptManyLegacy(ciphers, userId); - expect(result).toEqual(expectedViews); + expect(successfulDecryptions).toEqual(expectedViews); + expect(failedDecryptions).toEqual([]); expect(mockSdkClient.vault().ciphers().decrypt).toHaveBeenCalledTimes(2); expect(CipherView.fromSdkCipherView).toHaveBeenCalledTimes(2); }); diff --git a/libs/common/src/vault/services/default-cipher-encryption.service.ts b/libs/common/src/vault/services/default-cipher-encryption.service.ts index 3f03e0f5e9e..f1b737ed50f 100644 --- a/libs/common/src/vault/services/default-cipher-encryption.service.ts +++ b/libs/common/src/vault/services/default-cipher-encryption.service.ts @@ -168,7 +168,7 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService { ); } - decryptManyLegacy(ciphers: Cipher[], userId: UserId): Promise { + decryptManyLegacy(ciphers: Cipher[], userId: UserId): Promise<[CipherView[], CipherView[]]> { return firstValueFrom( this.sdkService.userClient$(userId).pipe( map((sdk) => { @@ -178,38 +178,49 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService { using ref = sdk.take(); - return ciphers.map((cipher) => { - const sdkCipherView = ref.value.vault().ciphers().decrypt(cipher.toSdkCipher()); - const clientCipherView = CipherView.fromSdkCipherView(sdkCipherView)!; + const successful: CipherView[] = []; + const failed: CipherView[] = []; - // Handle FIDO2 credentials if present - if ( - clientCipherView.type === CipherType.Login && - sdkCipherView.login?.fido2Credentials?.length - ) { - const fido2CredentialViews = ref.value - .vault() - .ciphers() - .decrypt_fido2_credentials(sdkCipherView); + ciphers.forEach((cipher) => { + try { + const sdkCipherView = ref.value.vault().ciphers().decrypt(cipher.toSdkCipher()); + const clientCipherView = CipherView.fromSdkCipherView(sdkCipherView)!; - // TODO (PM-21259): Remove manual keyValue decryption for FIDO2 credentials. - // This is a temporary workaround until we can use the SDK for FIDO2 authentication. - const decryptedKeyValue = ref.value - .vault() - .ciphers() - .decrypt_fido2_private_key(sdkCipherView); + // Handle FIDO2 credentials if present + if ( + clientCipherView.type === CipherType.Login && + sdkCipherView.login?.fido2Credentials?.length + ) { + const fido2CredentialViews = ref.value + .vault() + .ciphers() + .decrypt_fido2_credentials(sdkCipherView); - clientCipherView.login.fido2Credentials = fido2CredentialViews - .map((f) => { - const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!; - view.keyValue = decryptedKeyValue; - return view; - }) - .filter((view): view is Fido2CredentialView => view !== undefined); + const decryptedKeyValue = ref.value + .vault() + .ciphers() + .decrypt_fido2_private_key(sdkCipherView); + + clientCipherView.login.fido2Credentials = fido2CredentialViews + .map((f) => { + const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!; + view.keyValue = decryptedKeyValue; + return view; + }) + .filter((view): view is Fido2CredentialView => view !== undefined); + } + + successful.push(clientCipherView); + } catch (error) { + this.logService.error(`Failed to decrypt cipher ${cipher.id}: ${error}`); + const failedView = new CipherView(cipher); + failedView.name = "[error: cannot decrypt]"; + failedView.decryptionFailure = true; + failed.push(failedView); } - - return clientCipherView; }); + + return [successful, failed] as [CipherView[], CipherView[]]; }), catchError((error: unknown) => { this.logService.error(`Failed to decrypt ciphers: ${error}`); diff --git a/libs/components/src/a11y/aria-disabled-click-capture.service.ts b/libs/components/src/a11y/aria-disabled-click-capture.service.ts index d828d15c873..78f45acc80a 100644 --- a/libs/components/src/a11y/aria-disabled-click-capture.service.ts +++ b/libs/components/src/a11y/aria-disabled-click-capture.service.ts @@ -1,5 +1,4 @@ -import { DOCUMENT } from "@angular/common"; -import { Injectable, Inject, NgZone, OnDestroy } from "@angular/core"; +import { Injectable, Inject, NgZone, OnDestroy, DOCUMENT } from "@angular/core"; @Injectable({ providedIn: "root" }) export class AriaDisabledClickCaptureService implements OnDestroy { diff --git a/libs/components/src/anon-layout/anon-layout-wrapper.mdx b/libs/components/src/anon-layout/anon-layout-wrapper.mdx index 8fe332b8caf..683c41e491e 100644 --- a/libs/components/src/anon-layout/anon-layout-wrapper.mdx +++ b/libs/components/src/anon-layout/anon-layout-wrapper.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from "@storybook/addon-docs"; +import { Meta, Story, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./anon-layout-wrapper.stories"; diff --git a/libs/components/src/anon-layout/anon-layout.mdx b/libs/components/src/anon-layout/anon-layout.mdx index 3c6faeccfb0..fef3e9b0761 100644 --- a/libs/components/src/anon-layout/anon-layout.mdx +++ b/libs/components/src/anon-layout/anon-layout.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from "@storybook/addon-docs"; +import { Meta, Story, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./anon-layout.stories"; diff --git a/libs/components/src/async-actions/in-forms.mdx b/libs/components/src/async-actions/in-forms.mdx index 111a0df06af..27806ec92db 100644 --- a/libs/components/src/async-actions/in-forms.mdx +++ b/libs/components/src/async-actions/in-forms.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/async-actions/in-forms.stories.ts b/libs/components/src/async-actions/in-forms.stories.ts index 7a19908cbbf..623549ca202 100644 --- a/libs/components/src/async-actions/in-forms.stories.ts +++ b/libs/components/src/async-actions/in-forms.stories.ts @@ -1,8 +1,8 @@ import { Component } from "@angular/core"; import { FormsModule, ReactiveFormsModule, Validators, FormBuilder } from "@angular/forms"; -import { action } from "@storybook/addon-actions"; import { Meta, moduleMetadata, StoryObj } from "@storybook/angular"; import { delay, of } from "rxjs"; +import { action } from "storybook/actions"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service"; diff --git a/libs/components/src/async-actions/overview.mdx b/libs/components/src/async-actions/overview.mdx index 7cffd379b84..9f1638fc15c 100644 --- a/libs/components/src/async-actions/overview.mdx +++ b/libs/components/src/async-actions/overview.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/async-actions/standalone.mdx b/libs/components/src/async-actions/standalone.mdx index a781f40d852..a7699da9977 100644 --- a/libs/components/src/async-actions/standalone.mdx +++ b/libs/components/src/async-actions/standalone.mdx @@ -1,4 +1,4 @@ -import { Meta, Story } from "@storybook/addon-docs"; +import { Meta, Story } from "@storybook/addon-docs/blocks"; import * as stories from "./standalone.stories.ts"; diff --git a/libs/components/src/async-actions/standalone.stories.ts b/libs/components/src/async-actions/standalone.stories.ts index 52c1e990c23..690f746f812 100644 --- a/libs/components/src/async-actions/standalone.stories.ts +++ b/libs/components/src/async-actions/standalone.stories.ts @@ -1,7 +1,7 @@ import { Component } from "@angular/core"; -import { action } from "@storybook/addon-actions"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; import { delay, of } from "rxjs"; +import { action } from "storybook/actions"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; diff --git a/libs/components/src/avatar/avatar.mdx b/libs/components/src/avatar/avatar.mdx index bbf356f96fa..96db771889f 100644 --- a/libs/components/src/avatar/avatar.mdx +++ b/libs/components/src/avatar/avatar.mdx @@ -1,4 +1,4 @@ -import { Description, Meta, Canvas, Primary, Controls, Title } from "@storybook/addon-docs"; +import { Meta, Description, Canvas, Primary, Controls, Title } from "@storybook/addon-docs/blocks"; import * as stories from "./avatar.stories"; diff --git a/libs/components/src/badge/badge.mdx b/libs/components/src/badge/badge.mdx index 957a3256cbb..c8deaeb7643 100644 --- a/libs/components/src/badge/badge.mdx +++ b/libs/components/src/badge/badge.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./badge.stories"; diff --git a/libs/components/src/banner/banner.mdx b/libs/components/src/banner/banner.mdx index f37fe90e117..fe43106f2b9 100644 --- a/libs/components/src/banner/banner.mdx +++ b/libs/components/src/banner/banner.mdx @@ -1,4 +1,4 @@ -import { Canvas, Controls, Description, Meta, Primary, Title } from "@storybook/addon-docs"; +import { Canvas, Controls, Description, Meta, Primary, Title } from "@storybook/addon-docs/blocks"; import * as stories from "./banner.stories"; diff --git a/libs/components/src/breadcrumbs/breadcrumbs.mdx b/libs/components/src/breadcrumbs/breadcrumbs.mdx index cd1d0226387..6713795e854 100644 --- a/libs/components/src/breadcrumbs/breadcrumbs.mdx +++ b/libs/components/src/breadcrumbs/breadcrumbs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./breadcrumbs.stories"; diff --git a/libs/components/src/button/button.mdx b/libs/components/src/button/button.mdx index b0f347ba337..3080f6ffe4a 100644 --- a/libs/components/src/button/button.mdx +++ b/libs/components/src/button/button.mdx @@ -6,7 +6,7 @@ import { Controls, Title, Description, -} from "@storybook/addon-docs"; +} from "@storybook/addon-docs/blocks"; import * as stories from "./button.stories"; diff --git a/libs/components/src/callout/callout.mdx b/libs/components/src/callout/callout.mdx index 297a2ffd0a3..fa631c18549 100644 --- a/libs/components/src/callout/callout.mdx +++ b/libs/components/src/callout/callout.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./callout.stories"; diff --git a/libs/components/src/card/base-card/base-card.mdx b/libs/components/src/card/base-card/base-card.mdx index df326462906..11753c90f1d 100644 --- a/libs/components/src/card/base-card/base-card.mdx +++ b/libs/components/src/card/base-card/base-card.mdx @@ -1,4 +1,4 @@ -import { Meta, Primary, Controls, Canvas, Title, Description } from "@storybook/addon-docs"; +import { Meta, Primary, Controls, Canvas, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./base-card.stories"; diff --git a/libs/components/src/checkbox/checkbox.mdx b/libs/components/src/checkbox/checkbox.mdx index ba5de4d234a..079f7993ea6 100644 --- a/libs/components/src/checkbox/checkbox.mdx +++ b/libs/components/src/checkbox/checkbox.mdx @@ -1,4 +1,12 @@ -import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Canvas, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./checkbox.stories"; diff --git a/libs/components/src/chip-select/chip-select.mdx b/libs/components/src/chip-select/chip-select.mdx index b09b9664f8e..75ab1ad8a06 100644 --- a/libs/components/src/chip-select/chip-select.mdx +++ b/libs/components/src/chip-select/chip-select.mdx @@ -1,4 +1,4 @@ -import { Meta, Primary, Controls, Canvas, Title, Description } from "@storybook/addon-docs"; +import { Meta, Primary, Controls, Canvas, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./chip-select.stories"; diff --git a/libs/components/src/chip-select/chip-select.stories.ts b/libs/components/src/chip-select/chip-select.stories.ts index e9b78235ccb..fbdbd22b5ec 100644 --- a/libs/components/src/chip-select/chip-select.stories.ts +++ b/libs/components/src/chip-select/chip-select.stories.ts @@ -1,6 +1,6 @@ import { FormsModule } from "@angular/forms"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { getAllByRole, userEvent } from "@storybook/test"; +import { getAllByRole, userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/color-password/color-password.mdx b/libs/components/src/color-password/color-password.mdx index 4deeace9b9e..5397be4f599 100644 --- a/libs/components/src/color-password/color-password.mdx +++ b/libs/components/src/color-password/color-password.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./color-password.stories"; diff --git a/libs/components/src/container/container.mdx b/libs/components/src/container/container.mdx index 35e0c69587f..e64b7969cac 100644 --- a/libs/components/src/container/container.mdx +++ b/libs/components/src/container/container.mdx @@ -1,4 +1,4 @@ -import { Meta, Primary, Title, Description } from "@storybook/addon-docs"; +import { Meta, Primary, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./container.stories"; diff --git a/libs/components/src/dialog/dialog.service.stories.ts b/libs/components/src/dialog/dialog.service.stories.ts index 0921e9a9def..3b5bdc4d4e9 100644 --- a/libs/components/src/dialog/dialog.service.stories.ts +++ b/libs/components/src/dialog/dialog.service.stories.ts @@ -1,9 +1,9 @@ import { DIALOG_DATA, DialogRef } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; +import { Component, inject } from "@angular/core"; import { NoopAnimationsModule, provideAnimations } from "@angular/platform-browser/animations"; import { RouterTestingModule } from "@angular/router/testing"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; -import { getAllByRole, userEvent } from "@storybook/test"; +import { getAllByRole, userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -36,7 +36,7 @@ interface Animal { imports: [ButtonModule, LayoutComponent], }) class StoryDialogComponent { - constructor(public dialogService: DialogService) {} + dialogService = inject(DialogService); openDialog() { this.dialogService.open(StoryDialogContentComponent, { @@ -85,10 +85,8 @@ class StoryDialogComponent { imports: [DialogModule, ButtonModule], }) class StoryDialogContentComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) private data: Animal, - ) {} + dialogRef = inject(DialogRef); + private data = inject(DIALOG_DATA); get animal() { return this.data?.animal; @@ -118,10 +116,8 @@ class StoryDialogContentComponent { imports: [DialogModule, ButtonModule], }) class NonDismissableContentComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) private data: Animal, - ) {} + dialogRef = inject(DialogRef); + private data = inject(DIALOG_DATA); get animal() { return this.data?.animal; diff --git a/libs/components/src/dialog/dialog/dialog.mdx b/libs/components/src/dialog/dialog/dialog.mdx index 1806958210e..056e4ac79bc 100644 --- a/libs/components/src/dialog/dialog/dialog.mdx +++ b/libs/components/src/dialog/dialog/dialog.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./dialog.stories"; diff --git a/libs/components/src/dialog/dialogs.mdx b/libs/components/src/dialog/dialogs.mdx index 3f44f31a5eb..ee03dda6f57 100644 --- a/libs/components/src/dialog/dialogs.mdx +++ b/libs/components/src/dialog/dialogs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source } from "@storybook/addon-docs"; +import { Meta, Canvas, Source } from "@storybook/addon-docs/blocks"; import * as stories from "./dialog.service.stories"; diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.mdx b/libs/components/src/dialog/simple-dialog/simple-dialog.mdx index e3ed2fcf698..1d7a3668719 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.mdx +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./simple-dialog.stories"; diff --git a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts index 7e03ad60ca2..3b7fc3deeff 100644 --- a/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts +++ b/libs/components/src/dialog/simple-dialog/simple-dialog.service.stories.ts @@ -1,8 +1,8 @@ import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; -import { Component, Inject } from "@angular/core"; +import { Component, inject } from "@angular/core"; import { provideAnimations } from "@angular/platform-browser/animations"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { getAllByRole, userEvent } from "@storybook/test"; +import { getAllByRole, userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -30,7 +30,7 @@ interface Animal { imports: [ButtonModule], }) class StoryDialogComponent { - constructor(public dialogService: DialogService) {} + dialogService = inject(DialogService); openSimpleDialog() { this.dialogService.open(SimpleDialogContentComponent, { @@ -84,10 +84,8 @@ class StoryDialogComponent { imports: [ButtonModule, DialogModule], }) class SimpleDialogContentComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) private data: Animal, - ) {} + dialogRef = inject(DialogRef); + private data = inject(DIALOG_DATA); get animal() { return this.data?.animal; @@ -115,10 +113,8 @@ class SimpleDialogContentComponent { imports: [ButtonModule, DialogModule], }) class NonDismissableWithPrimaryButtonContentComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) private data: Animal, - ) {} + dialogRef = inject(DialogRef); + private data = inject(DIALOG_DATA); get animal() { return this.data?.animal; @@ -141,10 +137,8 @@ class NonDismissableWithPrimaryButtonContentComponent { imports: [ButtonModule, DialogModule], }) class NonDismissableWithNoButtonsContentComponent { - constructor( - public dialogRef: DialogRef, - @Inject(DIALOG_DATA) private data: Animal, - ) {} + dialogRef = inject(DialogRef); + private data = inject(DIALOG_DATA); get animal() { return this.data?.animal; diff --git a/libs/components/src/disclosure/disclosure.mdx b/libs/components/src/disclosure/disclosure.mdx index 9f01df8d3d9..69930261f93 100644 --- a/libs/components/src/disclosure/disclosure.mdx +++ b/libs/components/src/disclosure/disclosure.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./disclosure.stories"; diff --git a/libs/components/src/drawer/drawer.mdx b/libs/components/src/drawer/drawer.mdx index bc99fa290d6..1050ab476f7 100644 --- a/libs/components/src/drawer/drawer.mdx +++ b/libs/components/src/drawer/drawer.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./drawer.stories"; diff --git a/libs/components/src/form-field/form-field.mdx b/libs/components/src/form-field/form-field.mdx index d84535481ac..7d8feaea1d2 100644 --- a/libs/components/src/form-field/form-field.mdx +++ b/libs/components/src/form-field/form-field.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Source, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./form-field.stories"; diff --git a/libs/components/src/form-field/multi-select.stories.ts b/libs/components/src/form-field/multi-select.stories.ts index db7507f31a8..85dc09e7f83 100644 --- a/libs/components/src/form-field/multi-select.stories.ts +++ b/libs/components/src/form-field/multi-select.stories.ts @@ -6,8 +6,8 @@ import { FormGroup, } from "@angular/forms"; import { NgSelectModule } from "@ng-select/ng-select"; -import { action } from "@storybook/addon-actions"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; +import { action } from "storybook/actions"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/form/form.stories.ts b/libs/components/src/form/form.stories.ts index 1fc9bbef751..2f1dc652c40 100644 --- a/libs/components/src/form/form.stories.ts +++ b/libs/components/src/form/form.stories.ts @@ -1,6 +1,6 @@ import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from "@angular/forms"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { userEvent, getByText } from "@storybook/test"; +import { userEvent, getByText } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/form/forms.mdx b/libs/components/src/form/forms.mdx index 498eb8a3ed2..9efeaadfe6b 100644 --- a/libs/components/src/form/forms.mdx +++ b/libs/components/src/form/forms.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source } from "@storybook/addon-docs"; +import { Meta, Canvas, Source } from "@storybook/addon-docs/blocks"; import * as formStories from "./form.stories"; import * as fieldStories from "../form-field/form-field.stories"; diff --git a/libs/components/src/icon-button/icon-button.component.ts b/libs/components/src/icon-button/icon-button.component.ts index ca0e3fb1de5..d69bae98dff 100644 --- a/libs/components/src/icon-button/icon-button.component.ts +++ b/libs/components/src/icon-button/icon-button.component.ts @@ -120,7 +120,7 @@ export class BitIconButtonComponent implements ButtonLikeAbstraction, FocusableE * label input will be used to set the `aria-label` attributes on the button. * This is for accessibility purposes, as it provides a text alternative for the icon button. * - * NOTE: It will also be used to set the `title` attribute on the button if no `title` is provided. + * NOTE: It will also be used to set the content of the tooltip on the button if no `title` is provided. */ readonly label = input(); diff --git a/libs/components/src/icon-button/icon-button.mdx b/libs/components/src/icon-button/icon-button.mdx index 3fcd4a23583..062e3068685 100644 --- a/libs/components/src/icon-button/icon-button.mdx +++ b/libs/components/src/icon-button/icon-button.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./icon-button.stories"; @@ -81,10 +81,5 @@ with less padding around the icon, such as in the navigation component. Follow guidelines outlined in the [Button docs](?path=/docs/component-library-button--doc) -Always use the `appA11yTitle` directive set to a string that describes the action of the -icon-button. This will auto assign the same string to the `title` and `aria-label` attributes. - -`aria-label` allows assistive technology to announce the action the button takes to the users. - -`title` attribute provides a user with the browser tool tip if they do not understand what the icon -is indicating. +label input will be used to set the `aria-label` attributes on the button. This is for accessibility +purposes, as it provides a text alternative for the icon button. diff --git a/libs/components/src/icon/icon.mdx b/libs/components/src/icon/icon.mdx index 800a4711ff8..4f6f13c895e 100644 --- a/libs/components/src/icon/icon.mdx +++ b/libs/components/src/icon/icon.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Controls } from "@storybook/addon-docs"; +import { Meta, Story, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./icon.stories"; diff --git a/libs/components/src/input/autofocus.mdx b/libs/components/src/input/autofocus.mdx index b65aaff0c0a..fba2a7e9841 100644 --- a/libs/components/src/input/autofocus.mdx +++ b/libs/components/src/input/autofocus.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./autofocus.stories"; diff --git a/libs/components/src/input/input.mdx b/libs/components/src/input/input.mdx index 1cb3d16bc7b..252738bf03a 100644 --- a/libs/components/src/input/input.mdx +++ b/libs/components/src/input/input.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/item/item.mdx b/libs/components/src/item/item.mdx index a5b20dbcb62..8be3c0a8efa 100644 --- a/libs/components/src/item/item.mdx +++ b/libs/components/src/item/item.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./item.stories"; diff --git a/libs/components/src/layout/layout.stories.ts b/libs/components/src/layout/layout.stories.ts index 7ab0ffc7ce9..a059fd61b92 100644 --- a/libs/components/src/layout/layout.stories.ts +++ b/libs/components/src/layout/layout.stories.ts @@ -1,6 +1,6 @@ import { RouterTestingModule } from "@angular/router/testing"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { userEvent } from "@storybook/test"; +import { userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/link/link.mdx b/libs/components/src/link/link.mdx index 8fb5f693f10..072e0dd84d8 100644 --- a/libs/components/src/link/link.mdx +++ b/libs/components/src/link/link.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Story, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./link.stories"; diff --git a/libs/components/src/menu/menu.mdx b/libs/components/src/menu/menu.mdx index d77dc0a7d7b..61e7b40ce00 100644 --- a/libs/components/src/menu/menu.mdx +++ b/libs/components/src/menu/menu.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./menu.stories"; diff --git a/libs/components/src/multi-select/multi-select.component.ts b/libs/components/src/multi-select/multi-select.component.ts index 4e755d1deda..8faf629c9a0 100644 --- a/libs/components/src/multi-select/multi-select.component.ts +++ b/libs/components/src/multi-select/multi-select.component.ts @@ -108,11 +108,11 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro /** Needs to be arrow function to retain `this` scope. */ keyDown = (event: KeyboardEvent) => { const select = this.select(); - if (!select.isOpen && event.key === "Enter" && !hasModifierKey(event)) { + if (!select.isOpen() && event.key === "Enter" && !hasModifierKey(event)) { return false; } - if (select.isOpen && event.key === "Escape" && !hasModifierKey(event)) { + if (select.isOpen() && event.key === "Escape" && !hasModifierKey(event)) { this.selectedItems = []; select.close(); event.stopPropagation(); @@ -198,7 +198,9 @@ export class MultiSelectComponent implements OnInit, BitFormFieldControl, Contro } set ariaDescribedBy(value: string | undefined) { this._ariaDescribedBy = value; - this.select()?.searchInput.nativeElement.setAttribute("aria-describedby", value ?? ""); + this.select() + ?.searchInput() + .nativeElement.setAttribute("aria-describedby", value ?? ""); } private _ariaDescribedBy?: string; diff --git a/libs/components/src/popover/popover.mdx b/libs/components/src/popover/popover.mdx index 2d18303ea78..d56f00b10ae 100644 --- a/libs/components/src/popover/popover.mdx +++ b/libs/components/src/popover/popover.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./popover.stories"; diff --git a/libs/components/src/popover/popover.stories.ts b/libs/components/src/popover/popover.stories.ts index 596381d0777..a3d374c3136 100644 --- a/libs/components/src/popover/popover.stories.ts +++ b/libs/components/src/popover/popover.stories.ts @@ -1,5 +1,5 @@ import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { getByRole, userEvent } from "@storybook/test"; +import { getByRole, userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/progress/progress.mdx b/libs/components/src/progress/progress.mdx index def2f239129..bb121ca7b47 100644 --- a/libs/components/src/progress/progress.mdx +++ b/libs/components/src/progress/progress.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls, Title, Description } from "@storybook/addon-docs/blocks"; import * as stories from "./progress.stories"; diff --git a/libs/components/src/search/search.mdx b/libs/components/src/search/search.mdx index 98e91162c94..54144f1591f 100644 --- a/libs/components/src/search/search.mdx +++ b/libs/components/src/search/search.mdx @@ -1,4 +1,12 @@ -import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Canvas, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./search.stories"; diff --git a/libs/components/src/section/section.mdx b/libs/components/src/section/section.mdx index 81d98101c06..cc46bd6da37 100644 --- a/libs/components/src/section/section.mdx +++ b/libs/components/src/section/section.mdx @@ -1,4 +1,4 @@ -import { Meta, Primary, Controls, Canvas } from "@storybook/addon-docs"; +import { Meta, Primary, Controls, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./section.stories"; diff --git a/libs/components/src/select/select.component.ts b/libs/components/src/select/select.component.ts index f6358ccf6c6..0aa18d68702 100644 --- a/libs/components/src/select/select.component.ts +++ b/libs/components/src/select/select.component.ts @@ -158,7 +158,9 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce } set ariaDescribedBy(value: string | undefined) { this._ariaDescribedBy = value; - this.select()?.searchInput.nativeElement.setAttribute("aria-describedby", value ?? ""); + this.select() + ?.searchInput() + .nativeElement.setAttribute("aria-describedby", value ?? ""); } private _ariaDescribedBy?: string; @@ -218,7 +220,7 @@ export class SelectComponent implements BitFormFieldControl, ControlValueAcce * Needs to be arrow function to retain `this` scope. */ protected onKeyDown = (event: KeyboardEvent) => { - if (this.select().isOpen && event.key === "Escape" && !hasModifierKey(event)) { + if (this.select().isOpen() && event.key === "Escape" && !hasModifierKey(event)) { event.stopPropagation(); } diff --git a/libs/components/src/select/select.mdx b/libs/components/src/select/select.mdx index 8dfbe4aa526..95f2a28c235 100644 --- a/libs/components/src/select/select.mdx +++ b/libs/components/src/select/select.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Source, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./select.stories"; diff --git a/libs/components/src/skeleton/skeleton.mdx b/libs/components/src/skeleton/skeleton.mdx index ab0ba22f43e..161ac7f662e 100644 --- a/libs/components/src/skeleton/skeleton.mdx +++ b/libs/components/src/skeleton/skeleton.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Source } from "@storybook/addon-docs"; +import { Meta, Canvas, Source } from "@storybook/addon-docs/blocks"; import * as skeletonStories from "./skeleton.stories"; import * as skeletonTextStories from "./skeleton-text.stories"; diff --git a/libs/components/src/stepper/stepper.mdx b/libs/components/src/stepper/stepper.mdx index ca4efd97aef..6c340027c69 100644 --- a/libs/components/src/stepper/stepper.mdx +++ b/libs/components/src/stepper/stepper.mdx @@ -1,4 +1,12 @@ -import { Meta, Story, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Story, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./stepper.stories"; diff --git a/libs/components/src/stories/colors.mdx b/libs/components/src/stories/colors.mdx index 87ca673797b..ca9a97b9071 100644 --- a/libs/components/src/stories/colors.mdx +++ b/libs/components/src/stories/colors.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/stories/compact-mode.mdx b/libs/components/src/stories/compact-mode.mdx index 3cffd45847a..898acc39f71 100644 --- a/libs/components/src/stories/compact-mode.mdx +++ b/libs/components/src/stories/compact-mode.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as itemStories from "../item/item.stories"; import * as popupLayoutStories from "../../../../apps/browser/src/platform/popup/layout/popup-layout.stories"; diff --git a/libs/components/src/stories/icons/icons.mdx b/libs/components/src/stories/icons/icons.mdx index 300a424b87d..8adfe6fadee 100644 --- a/libs/components/src/stories/icons/icons.mdx +++ b/libs/components/src/stories/icons/icons.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas } from "@storybook/addon-docs"; +import { Meta, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./icons.stories"; diff --git a/libs/components/src/stories/introduction.mdx b/libs/components/src/stories/introduction.mdx index 4401c47551f..5f766f130f3 100644 --- a/libs/components/src/stories/introduction.mdx +++ b/libs/components/src/stories/introduction.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.mdx b/libs/components/src/stories/kitchen-sink/kitchen-sink.mdx index 34e80081887..6eb02880e97 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.mdx +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.mdx @@ -1,4 +1,4 @@ -import { Meta, Story } from "@storybook/addon-docs"; +import { Meta, Story } from "@storybook/addon-docs/blocks"; import * as stories from "./kitchen-sink.stories"; diff --git a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts index c25eb778d11..fc6be00b0e0 100644 --- a/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts +++ b/libs/components/src/stories/kitchen-sink/kitchen-sink.stories.ts @@ -9,7 +9,7 @@ import { fireEvent, getByText, getAllByLabelText, -} from "@storybook/test"; +} from "storybook/test"; import { PasswordManagerLogo } from "@bitwarden/assets/svg"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/stories/migration.mdx b/libs/components/src/stories/migration.mdx index 1160c602894..b6d7925534d 100644 --- a/libs/components/src/stories/migration.mdx +++ b/libs/components/src/stories/migration.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/stories/responsive-design.mdx b/libs/components/src/stories/responsive-design.mdx index a54baf6a231..a69497c063e 100644 --- a/libs/components/src/stories/responsive-design.mdx +++ b/libs/components/src/stories/responsive-design.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/stories/virtual-scrolling.mdx b/libs/components/src/stories/virtual-scrolling.mdx index ab51d9865db..532d9a31d3f 100644 --- a/libs/components/src/stories/virtual-scrolling.mdx +++ b/libs/components/src/stories/virtual-scrolling.mdx @@ -1,4 +1,4 @@ -import { Meta } from "@storybook/addon-docs"; +import { Meta } from "@storybook/addon-docs/blocks"; diff --git a/libs/components/src/switch/switch.mdx b/libs/components/src/switch/switch.mdx index 71421d6a808..9df97cfba48 100644 --- a/libs/components/src/switch/switch.mdx +++ b/libs/components/src/switch/switch.mdx @@ -1,4 +1,12 @@ -import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Canvas, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./switch.stories"; diff --git a/libs/components/src/table/table.mdx b/libs/components/src/table/table.mdx index 59bf5b773a3..fe84842e10c 100644 --- a/libs/components/src/table/table.mdx +++ b/libs/components/src/table/table.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Source, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Story, Source, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./table.stories"; diff --git a/libs/components/src/tabs/tabs.mdx b/libs/components/src/tabs/tabs.mdx index bab5fbb52c4..6d80c759f87 100644 --- a/libs/components/src/tabs/tabs.mdx +++ b/libs/components/src/tabs/tabs.mdx @@ -1,4 +1,4 @@ -import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs"; +import { Meta, Canvas, Primary, Controls } from "@storybook/addon-docs/blocks"; import * as stories from "./tabs.stories"; import * as dialogStories from "../dialog/dialog/dialog.stories"; diff --git a/libs/components/src/toast/toast.mdx b/libs/components/src/toast/toast.mdx index 6d9d80c6ae5..a6aa1ef23fe 100644 --- a/libs/components/src/toast/toast.mdx +++ b/libs/components/src/toast/toast.mdx @@ -1,4 +1,12 @@ -import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Canvas, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./toast.stories"; diff --git a/libs/components/src/toast/toast.stories.ts b/libs/components/src/toast/toast.stories.ts index f7eeadce971..58451aafbb6 100644 --- a/libs/components/src/toast/toast.stories.ts +++ b/libs/components/src/toast/toast.stories.ts @@ -1,8 +1,8 @@ import { CommonModule } from "@angular/common"; import { Component, Input } from "@angular/core"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; -import { action } from "@storybook/addon-actions"; import { Meta, StoryObj, applicationConfig, moduleMetadata } from "@storybook/angular"; +import { action } from "storybook/actions"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/toggle-group/toggle-group.mdx b/libs/components/src/toggle-group/toggle-group.mdx index d553ae11298..d6fad84a4c4 100644 --- a/libs/components/src/toggle-group/toggle-group.mdx +++ b/libs/components/src/toggle-group/toggle-group.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Primary, Controls, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Primary, Controls, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./toggle-group.stories"; diff --git a/libs/components/src/tooltip/tooltip.directive.ts b/libs/components/src/tooltip/tooltip.directive.ts index bcf9fc5e174..cca52526c7d 100644 --- a/libs/components/src/tooltip/tooltip.directive.ts +++ b/libs/components/src/tooltip/tooltip.directive.ts @@ -16,6 +16,8 @@ import { import { TooltipPositionIdentifier, tooltipPositions } from "./tooltip-positions"; import { TooltipComponent, TOOLTIP_DATA } from "./tooltip.component"; +export const TOOLTIP_DELAY_MS = 800; + /** * Directive to add a tooltip to any element. The tooltip content is provided via the `bitTooltip` input. * The position of the tooltip can be set via the `tooltipPosition` input. Default position is "above-center". @@ -85,7 +87,7 @@ export class TooltipDirective implements OnInit { this.isVisible.set(false); }; - private showTooltip = () => { + protected showTooltip = () => { if (!this.overlayRef) { this.overlayRef = this.overlay.create({ ...this.defaultPopoverConfig, @@ -94,14 +96,17 @@ export class TooltipDirective implements OnInit { this.overlayRef.attach(this.tooltipPortal); } - this.isVisible.set(true); + + setTimeout(() => { + this.isVisible.set(true); + }, TOOLTIP_DELAY_MS); }; - private hideTooltip = () => { + protected hideTooltip = () => { this.destroyTooltip(); }; - private readonly resolvedDescribedByIds = computed(() => { + protected readonly resolvedDescribedByIds = computed(() => { if (this.addTooltipToDescribedby()) { if (this.currentDescribedByIds) { return `${this.currentDescribedByIds || ""} ${this.tooltipId}`; diff --git a/libs/components/src/tooltip/tooltip.mdx b/libs/components/src/tooltip/tooltip.mdx index 13e159c98eb..34f40784444 100644 --- a/libs/components/src/tooltip/tooltip.mdx +++ b/libs/components/src/tooltip/tooltip.mdx @@ -1,4 +1,12 @@ -import { Meta, Canvas, Source, Primary, Controls, Title, Description } from "@storybook/addon-docs"; +import { + Meta, + Canvas, + Source, + Primary, + Controls, + Title, + Description, +} from "@storybook/addon-docs/blocks"; import * as stories from "./tooltip.stories"; diff --git a/libs/components/src/tooltip/tooltip.spec.ts b/libs/components/src/tooltip/tooltip.spec.ts index a88424de3bb..ff73911d29d 100644 --- a/libs/components/src/tooltip/tooltip.spec.ts +++ b/libs/components/src/tooltip/tooltip.spec.ts @@ -6,11 +6,11 @@ import { } from "@angular/cdk/overlay"; import { ComponentPortal } from "@angular/cdk/portal"; import { Component } from "@angular/core"; -import { ComponentFixture, TestBed } from "@angular/core/testing"; +import { ComponentFixture, TestBed, fakeAsync, tick } from "@angular/core/testing"; import { By } from "@angular/platform-browser"; import { Observable, Subject } from "rxjs"; -import { TooltipDirective } from "./tooltip.directive"; +import { TooltipDirective, TOOLTIP_DELAY_MS } from "./tooltip.directive"; // FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection @@ -90,23 +90,25 @@ describe("TooltipDirective (visibility only)", () => { return hostDE.injector.get(TooltipDirective); } - it("sets isVisible to true on mouseenter", () => { + it("sets isVisible to true on mouseenter", fakeAsync(() => { const button: HTMLButtonElement = fixture.debugElement.query(By.css("button")).nativeElement; const directive = getDirective(); const isVisible = (directive as unknown as { isVisible: () => boolean }).isVisible; button.dispatchEvent(new Event("mouseenter")); + tick(TOOLTIP_DELAY_MS); expect(isVisible()).toBe(true); - }); + })); - it("sets isVisible to true on focus", () => { + it("sets isVisible to true on focus", fakeAsync(() => { const button: HTMLButtonElement = fixture.debugElement.query(By.css("button")).nativeElement; const directive = getDirective(); const isVisible = (directive as unknown as { isVisible: () => boolean }).isVisible; button.dispatchEvent(new Event("focus")); + tick(TOOLTIP_DELAY_MS); expect(isVisible()).toBe(true); - }); + })); }); diff --git a/libs/components/src/tooltip/tooltip.stories.ts b/libs/components/src/tooltip/tooltip.stories.ts index 73df08e25fa..2b4998d12f8 100644 --- a/libs/components/src/tooltip/tooltip.stories.ts +++ b/libs/components/src/tooltip/tooltip.stories.ts @@ -1,6 +1,6 @@ import { signal } from "@angular/core"; import { Meta, StoryObj, moduleMetadata } from "@storybook/angular"; -import { getByRole, userEvent } from "@storybook/test"; +import { getByRole, userEvent } from "storybook/test"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; diff --git a/libs/components/src/typography/typography.mdx b/libs/components/src/typography/typography.mdx index d8439481dbc..a200ae68f98 100644 --- a/libs/components/src/typography/typography.mdx +++ b/libs/components/src/typography/typography.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as stories from "./typography.stories"; diff --git a/libs/node/src/services/node-crypto-function.service.spec.ts b/libs/node/src/services/node-crypto-function.service.spec.ts index 3256d85110f..28a6c127d44 100644 --- a/libs/node/src/services/node-crypto-function.service.spec.ts +++ b/libs/node/src/services/node-crypto-function.service.spec.ts @@ -1,9 +1,17 @@ +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { EcbDecryptParameters } from "@bitwarden/common/platform/models/domain/decrypt-parameters"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { NodeCryptoFunctionService } from "./node-crypto-function.service"; +class TestSdkLoadService extends SdkLoadService { + protected override load(): Promise { + // Simulate successful WASM load + return Promise.resolve(); + } +} + const RsaPublicKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAl0Vawl/toXzkEvB82FEtqHP" + "4xlU2ab/v0crqIfXfIoWF/XXdHGIdrZeilnRXPPJT1B9dTsasttEZNnua/0Rek/cjNDHtzT52irfoZYS7X6HNIfOi54Q+egP" + @@ -37,6 +45,10 @@ const Sha512Mac = "5ea7817a0b7c5d4d9b00364ccd214669131fc17fe4aca"; describe("NodeCrypto Function Service", () => { + beforeAll(async () => { + await new TestSdkLoadService().loadAndInit(); + }); + describe("pbkdf2", () => { const regular256Key = "pj9prw/OHPleXI6bRdmlaD+saJS4awrMiQsQiDjeu2I="; const utf8256Key = "yqvoFXgMRmHR3QPYr5pyR4uVuoHkltv9aHUP63p8n7I="; @@ -279,7 +291,6 @@ describe("NodeCrypto Function Service", () => { }); describe("rsaGenerateKeyPair", () => { - testRsaGenerateKeyPair(1024); testRsaGenerateKeyPair(2048); // Generating 4096 bit keys is really slow with Forge lib. @@ -514,7 +525,7 @@ function testCompare(fast = false) { }); } -function testRsaGenerateKeyPair(length: 1024 | 2048 | 4096) { +function testRsaGenerateKeyPair(length: 2048) { it( "should successfully generate a " + length + " bit key pair", async () => { diff --git a/libs/node/src/services/node-crypto-function.service.ts b/libs/node/src/services/node-crypto-function.service.ts index 22cc5756f30..49dbc65ca84 100644 --- a/libs/node/src/services/node-crypto-function.service.ts +++ b/libs/node/src/services/node-crypto-function.service.ts @@ -4,6 +4,7 @@ import * as forge from "node-forge"; import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; import { UnsignedPublicKey } from "@bitwarden/common/key-management/types"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { EncryptionType } from "@bitwarden/common/platform/enums"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { @@ -12,6 +13,7 @@ import { } from "@bitwarden/common/platform/models/domain/decrypt-parameters"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; import { CsprngArray } from "@bitwarden/common/types/csprng"; +import { PureCrypto } from "@bitwarden/sdk-internal"; export class NodeCryptoFunctionService implements CryptoFunctionService { pbkdf2( @@ -205,72 +207,34 @@ export class NodeCryptoFunctionService implements CryptoFunctionService { return Promise.resolve(this.toUint8Buffer(decBuf)); } - rsaEncrypt( + async rsaEncrypt( data: Uint8Array, publicKey: Uint8Array, - algorithm: "sha1" | "sha256", + _algorithm: "sha1", ): Promise { - if (algorithm === "sha256") { - throw new Error("Node crypto does not support RSA-OAEP SHA-256"); - } - - const pem = this.toPemPublicKey(publicKey); - const decipher = crypto.publicEncrypt(pem, this.toNodeBuffer(data)); - return Promise.resolve(this.toUint8Buffer(decipher)); + await SdkLoadService.Ready; + return PureCrypto.rsa_encrypt_data(data, publicKey); } - rsaDecrypt( + async rsaDecrypt( data: Uint8Array, privateKey: Uint8Array, - algorithm: "sha1" | "sha256", + _algorithm: "sha1", ): Promise { - if (algorithm === "sha256") { - throw new Error("Node crypto does not support RSA-OAEP SHA-256"); - } - - const pem = this.toPemPrivateKey(privateKey); - const decipher = crypto.privateDecrypt(pem, this.toNodeBuffer(data)); - return Promise.resolve(this.toUint8Buffer(decipher)); + await SdkLoadService.Ready; + return PureCrypto.rsa_decrypt_data(data, privateKey); } async rsaExtractPublicKey(privateKey: Uint8Array): Promise { - const privateKeyByteString = Utils.fromBufferToByteString(privateKey); - const privateKeyAsn1 = forge.asn1.fromDer(privateKeyByteString); - const forgePrivateKey: any = forge.pki.privateKeyFromAsn1(privateKeyAsn1); - const forgePublicKey = (forge.pki as any).setRsaPublicKey(forgePrivateKey.n, forgePrivateKey.e); - const publicKeyAsn1 = forge.pki.publicKeyToAsn1(forgePublicKey); - const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).data; - const publicKeyArray = Utils.fromByteStringToArray(publicKeyByteString); - return publicKeyArray as UnsignedPublicKey; + await SdkLoadService.Ready; + return PureCrypto.rsa_extract_public_key(privateKey) as UnsignedPublicKey; } - async rsaGenerateKeyPair(length: 1024 | 2048 | 4096): Promise<[UnsignedPublicKey, Uint8Array]> { - return new Promise<[UnsignedPublicKey, Uint8Array]>((resolve, reject) => { - forge.pki.rsa.generateKeyPair( - { - bits: length, - workers: -1, - e: 0x10001, // 65537 - }, - (error, keyPair) => { - if (error != null) { - reject(error); - return; - } - - const publicKeyAsn1 = forge.pki.publicKeyToAsn1(keyPair.publicKey); - const publicKeyByteString = forge.asn1.toDer(publicKeyAsn1).getBytes(); - const publicKey = Utils.fromByteStringToArray(publicKeyByteString); - - const privateKeyAsn1 = forge.pki.privateKeyToAsn1(keyPair.privateKey); - const privateKeyPkcs8 = forge.pki.wrapRsaPrivateKey(privateKeyAsn1); - const privateKeyByteString = forge.asn1.toDer(privateKeyPkcs8).getBytes(); - const privateKey = Utils.fromByteStringToArray(privateKeyByteString); - - resolve([publicKey as UnsignedPublicKey, privateKey]); - }, - ); - }); + async rsaGenerateKeyPair(_length: 2048): Promise<[UnsignedPublicKey, Uint8Array]> { + await SdkLoadService.Ready; + const privateKey = PureCrypto.rsa_generate_keypair(); + const publicKey = await this.rsaExtractPublicKey(privateKey); + return [publicKey, privateKey]; } aesGenerateKey(bitLength: 128 | 192 | 256 | 512): Promise { diff --git a/libs/pricing/src/components/cart-summary/cart-summary.component.mdx b/libs/pricing/src/components/cart-summary/cart-summary.component.mdx index 79f36bb2523..a8b599fc66b 100644 --- a/libs/pricing/src/components/cart-summary/cart-summary.component.mdx +++ b/libs/pricing/src/components/cart-summary/cart-summary.component.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as CartSummaryStories from "./cart-summary.component.stories"; diff --git a/libs/pricing/src/components/discount-badge/discount-badge.component.mdx b/libs/pricing/src/components/discount-badge/discount-badge.component.mdx index d3df2dcf0f6..deef8fecb73 100644 --- a/libs/pricing/src/components/discount-badge/discount-badge.component.mdx +++ b/libs/pricing/src/components/discount-badge/discount-badge.component.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as DiscountBadgeStories from "./discount-badge.component.stories"; diff --git a/libs/pricing/src/components/pricing-card/pricing-card.component.mdx b/libs/pricing/src/components/pricing-card/pricing-card.component.mdx index 355ca71eb80..39e45b3fd25 100644 --- a/libs/pricing/src/components/pricing-card/pricing-card.component.mdx +++ b/libs/pricing/src/components/pricing-card/pricing-card.component.mdx @@ -1,4 +1,4 @@ -import { Meta, Story, Canvas } from "@storybook/addon-docs"; +import { Meta, Story, Canvas } from "@storybook/addon-docs/blocks"; import * as PricingCardStories from "./pricing-card.component.stories"; diff --git a/libs/vault/src/cipher-form/cipher-form.mdx b/libs/vault/src/cipher-form/cipher-form.mdx index 658fcd38d11..da453a655d9 100644 --- a/libs/vault/src/cipher-form/cipher-form.mdx +++ b/libs/vault/src/cipher-form/cipher-form.mdx @@ -1,4 +1,4 @@ -import { Controls, Meta, Primary } from "@storybook/addon-docs"; +import { Controls, Meta, Primary } from "@storybook/addon-docs/blocks"; import * as stories from "./cipher-form.stories"; diff --git a/libs/vault/src/cipher-form/cipher-form.stories.ts b/libs/vault/src/cipher-form/cipher-form.stories.ts index cf52dbc557f..e732513913d 100644 --- a/libs/vault/src/cipher-form/cipher-form.stories.ts +++ b/libs/vault/src/cipher-form/cipher-form.stories.ts @@ -2,7 +2,6 @@ // @ts-strict-ignore import { importProvidersFrom, signal } from "@angular/core"; import { ActivatedRoute } from "@angular/router"; -import { action } from "@storybook/addon-actions"; import { applicationConfig, componentWrapperDecorator, @@ -11,6 +10,7 @@ import { StoryObj, } from "@storybook/angular"; import { BehaviorSubject, of } from "rxjs"; +import { action } from "storybook/actions"; // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports diff --git a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts index 56c3414a12e..a5306606199 100644 --- a/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts +++ b/libs/vault/src/cipher-form/components/attachments/cipher-attachments.component.ts @@ -108,11 +108,21 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit { // eslint-disable-next-line @angular-eslint/prefer-signals @Input() submitBtn?: ButtonComponent; + /** Emits when a file upload is started */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref + @Output() onUploadStarted = new EventEmitter(); + /** Emits after a file has been successfully uploaded */ // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @Output() onUploadSuccess = new EventEmitter(); + /** Emits when a file upload fails */ + // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals + // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref + @Output() onUploadFailed = new EventEmitter(); + /** Emits after a file has been successfully removed */ // FIXME(https://bitwarden.atlassian.net/browse/CL-903): Migrate to Signals // eslint-disable-next-line @angular-eslint/prefer-output-emitter-ref @@ -196,6 +206,8 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit { /** Save the attachments to the cipher */ submit = async () => { + this.onUploadStarted.emit(); + const file = this.attachmentForm.value.file; if (file === null) { this.toastService.showToast({ @@ -253,6 +265,7 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit { variant: "error", message: errorMessage, }); + this.onUploadFailed.emit(); } }; diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts index 1e60ad91fb1..9f3102239ae 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.spec.ts @@ -42,9 +42,18 @@ describe("CipherFormComponent", () => { { provide: CipherFormService, useValue: mockAddEditFormService }, { provide: CipherFormCacheService, - useValue: { init: jest.fn(), getCachedCipherView: jest.fn() }, + useValue: { init: jest.fn(), getCachedCipherView: jest.fn(), clearCache: jest.fn() }, + }, + { + provide: ViewCacheService, + useValue: { + signal: jest.fn(() => { + const signalFn = (): any => null; + signalFn.set = jest.fn(); + return signalFn; + }), + }, }, - { provide: ViewCacheService, useValue: { signal: jest.fn(() => (): any => null) } }, { provide: ConfigService, useValue: mock() }, { provide: AccountService, useValue: mockAccountService }, { provide: CipherArchiveService, useValue: mockCipherArchiveService }, diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.ts b/libs/vault/src/cipher-form/components/cipher-form.component.ts index f94af25e90a..c9e867f8d3a 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.ts +++ b/libs/vault/src/cipher-form/components/cipher-form.component.ts @@ -304,13 +304,30 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci * Updates `updatedCipherView` based on the value from the cache. */ setInitialCipherFromCache() { + // If we are coming from the overlay/popup flow clear the cache to avoid old cached data + const hasOverlayData = + this.config.initialValues && + (this.config.initialValues.username !== undefined || + this.config.initialValues.password !== undefined); + + if (hasOverlayData) { + this.cipherFormCacheService.clearCache(); + return; + } + const cachedCipher = this.cipherFormCacheService.getCachedCipherView(); if (cachedCipher === null) { return; } - // Use the cached cipher when it matches the cipher being edited - if (this.updatedCipherView.id === cachedCipher.id) { + const isEditingExistingCipher = + this.updatedCipherView.id && this.updatedCipherView.id === cachedCipher.id; + const isCreatingNewCipher = + !this.updatedCipherView.id && + !cachedCipher.id && + this.updatedCipherView.type === cachedCipher.type; + + if (isEditingExistingCipher || isCreatingNewCipher) { this.updatedCipherView = cachedCipher; } } @@ -382,6 +399,9 @@ export class CipherFormComponent implements AfterViewInit, OnInit, OnChanges, Ci this.config, ); + // Clear the cache after successful save + this.cipherFormCacheService.clearCache(); + this.toastService.showToast({ variant: "success", title: null, diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts index 6fd74d86525..58959a957a8 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.ts @@ -192,11 +192,6 @@ export class ItemDetailsSectionComponent implements OnInit { } get showOwnership() { - // Don't show ownership field for archived ciphers - if (this.originalCipherView?.isArchived) { - return false; - } - // Show ownership field when editing with available orgs const isEditingWithOrgs = this.organizations.length > 0 && this.config.mode === "edit"; diff --git a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts index 25581ae5ea1..d525dcd9afa 100644 --- a/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts +++ b/libs/vault/src/cipher-form/services/default-cipher-form-cache.service.ts @@ -22,7 +22,6 @@ export class CipherFormCacheService { key: CIPHER_FORM_CACHE_KEY, initialValue: null, deserializer: CipherView.fromJSON, - clearOnTabChange: true, }); constructor() { @@ -45,4 +44,11 @@ export class CipherFormCacheService { getCachedCipherView(): CipherView | null { return this.cipherCache(); } + + /** + * Clear the cached CipherView. + */ + clearCache(): void { + this.cipherCache.set(null); + } } diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.html b/libs/vault/src/cipher-view/attachments/attachments-v2.component.html index 3b096634069..a8dc22c75ac 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.html +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.html @@ -9,7 +9,9 @@ [organizationId]="organizationId" [admin]="admin" [submitBtn]="submitBtn" + (onUploadStarted)="uploadStarted()" (onUploadSuccess)="uploadSuccessful()" + (onUploadFailed)="uploadFailed()" (onRemoveSuccess)="removalSuccessful()" > diff --git a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts index 2796cae08d0..218f5b2c6d3 100644 --- a/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts +++ b/libs/vault/src/cipher-view/attachments/attachments-v2.component.ts @@ -1,7 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CommonModule } from "@angular/common"; -import { Component, Inject } from "@angular/core"; +import { Component, HostListener, Inject } from "@angular/core"; import { CipherId, OrganizationId } from "@bitwarden/common/types/guid"; import { UnionOfValues } from "@bitwarden/common/vault/types/union-of-values"; @@ -52,6 +52,7 @@ export class AttachmentsV2Component { admin: boolean = false; organizationId?: OrganizationId; attachmentFormId = CipherAttachmentsComponent.attachmentFormID; + private isUploading = false; /** * Constructor for AttachmentsV2Component. @@ -82,16 +83,54 @@ export class AttachmentsV2Component { }); } + /** + * Prevent browser tab from closing/refreshing during upload. + * Shows a confirmation dialog if user tries to leave during an active upload. + * This provides additional protection beyond dialogRef.disableClose. + * Using arrow function to preserve 'this' context when used as event listener. + */ + @HostListener("window:beforeunload", ["$event"]) + private handleBeforeUnloadEvent = (event: BeforeUnloadEvent): string | undefined => { + if (this.isUploading) { + event.preventDefault(); + // The custom message is not displayed in modern browsers, but MDN docs still recommend setting it for legacy support. + const message = "Upload in progress. Are you sure you want to leave?"; + event.returnValue = message; + return message; + } + return undefined; + }; + + /** + * Called when an attachment upload is started. + * Disables closing the dialog to prevent accidental interruption. + */ + uploadStarted() { + this.isUploading = true; + this.dialogRef.disableClose = true; + } + /** * Called when an attachment is successfully uploaded. - * Closes the dialog with an 'uploaded' result. + * Re-enables dialog closing and closes the dialog with an 'uploaded' result. */ uploadSuccessful() { + this.isUploading = false; + this.dialogRef.disableClose = false; this.dialogRef.close({ action: AttachmentDialogResult.Uploaded, }); } + /** + * Called when an attachment upload fails. + * Re-enables closing the dialog. + */ + uploadFailed() { + this.isUploading = false; + this.dialogRef.disableClose = false; + } + /** * Called when an attachment is successfully removed. * Closes the dialog with a 'removed' result. diff --git a/libs/vault/src/components/carousel/carousel.component.html b/libs/vault/src/components/carousel/carousel.component.html index 778b70e15e2..999d7ef289a 100644 --- a/libs/vault/src/components/carousel/carousel.component.html +++ b/libs/vault/src/components/carousel/carousel.component.html @@ -12,10 +12,9 @@ bitIconButton="bwi-angle-left" class="tw-size-6 tw-p-0 tw-flex tw-items-center tw-justify-center" size="small" - [attr.label]="'back' | i18n" (click)="prevSlide()" [disabled]="selectedIndex <= 0" - appA11yTitle="{{ 'back' | i18n }}" + label="{{ 'back' | i18n }}" >
    diff --git a/package-lock.json b/package-lock.json index c7186eab53f..ea662c62b6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,24 +14,24 @@ "libs/**/*" ], "dependencies": { - "@angular/animations": "19.2.14", - "@angular/cdk": "19.2.18", - "@angular/common": "19.2.14", - "@angular/compiler": "19.2.14", - "@angular/core": "19.2.14", - "@angular/forms": "19.2.14", - "@angular/platform-browser": "19.2.14", - "@angular/platform-browser-dynamic": "19.2.14", - "@angular/router": "19.2.14", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.395", - "@bitwarden/sdk-internal": "0.2.0-main.395", + "@angular/animations": "20.3.15", + "@angular/cdk": "20.2.14", + "@angular/common": "20.3.15", + "@angular/compiler": "20.3.15", + "@angular/core": "20.3.15", + "@angular/forms": "20.3.15", + "@angular/platform-browser": "20.3.15", + "@angular/platform-browser-dynamic": "20.3.15", + "@angular/router": "20.3.15", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.403", + "@bitwarden/sdk-internal": "0.2.0-main.403", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", "@koa/router": "14.0.0", "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", - "@ng-select/ng-select": "14.9.0", + "@ng-select/ng-select": "20.7.0", "@nx/devkit": "21.6.9", "@nx/eslint": "21.6.9", "@nx/jest": "21.6.9", @@ -52,7 +52,7 @@ "koa": "2.16.3", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", - "lit": "3.3.0", + "lit": "3.3.1", "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "2.0.2", @@ -68,17 +68,18 @@ "rxjs": "7.8.1", "semver": "7.7.3", "tabbable": "6.3.0", - "tldts": "7.0.18", + "tldts": "7.0.19", "ts-node": "10.9.2", "utf-8-validate": "6.0.5", + "vite-tsconfig-paths": "5.1.4", "zone.js": "0.15.1", "zxcvbn": "4.4.2" }, "devDependencies": { - "@angular-devkit/build-angular": "19.2.14", - "@angular-eslint/schematics": "19.6.0", - "@angular/cli": "19.2.14", - "@angular/compiler-cli": "19.2.14", + "@angular-devkit/build-angular": "20.3.12", + "@angular-eslint/schematics": "20.7.0", + "@angular/cli": "20.3.12", + "@angular/compiler-cli": "20.3.15", "@babel/core": "7.28.5", "@babel/preset-env": "7.28.5", "@compodoc/compodoc": "1.1.26", @@ -86,19 +87,15 @@ "@electron/rebuild": "4.0.1", "@eslint/compat": "2.0.0", "@lit-labs/signals": "0.1.2", - "@ngtools/webpack": "19.2.14", - "@storybook/addon-a11y": "8.6.12", - "@storybook/addon-actions": "8.6.12", - "@storybook/addon-designs": "8.2.1", - "@storybook/addon-essentials": "8.6.12", - "@storybook/addon-interactions": "8.6.12", - "@storybook/addon-links": "8.6.12", - "@storybook/addon-themes": "8.6.12", - "@storybook/angular": "8.6.12", - "@storybook/manager-api": "8.6.12", + "@ngtools/webpack": "20.3.11", + "@storybook/addon-a11y": "9.1.16", + "@storybook/addon-designs": "9.0.0-next.3", + "@storybook/addon-docs": "9.1.16", + "@storybook/addon-links": "9.1.16", + "@storybook/addon-themes": "9.1.16", + "@storybook/angular": "9.1.16", "@storybook/test-runner": "0.22.0", - "@storybook/theming": "8.6.12", - "@storybook/web-components-webpack5": "8.6.12", + "@storybook/web-components-vite": "9.1.16", "@tailwindcss/container-queries": "0.1.1", "@types/chrome": "0.1.28", "@types/firefox-webext-browser": "143.0.0", @@ -123,7 +120,7 @@ "@typescript-eslint/utils": "8.31.0", "@webcomponents/custom-elements": "1.6.0", "@yao-pkg/pkg": "6.5.1", - "angular-eslint": "19.6.0", + "angular-eslint": "20.7.0", "autoprefixer": "10.4.21", "axe-playwright": "2.2.2", "babel-loader": "9.2.1", @@ -146,7 +143,7 @@ "eslint-plugin-import": "2.31.0", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", - "eslint-plugin-storybook": "0.12.0", + "eslint-plugin-storybook": "9.1.16", "eslint-plugin-tailwindcss": "3.18.0", "html-loader": "5.1.0", "html-webpack-injector": "1.1.4", @@ -160,16 +157,17 @@ "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.4", "nx": "21.6.9", + "path-browserify": "1.0.1", "postcss": "8.5.6", "postcss-loader": "8.2.0", "prettier": "3.6.2", - "prettier-plugin-tailwindcss": "0.6.11", + "prettier-plugin-tailwindcss": "0.7.1", "process": "0.11.10", "remark-gfm": "4.0.1", "rimraf": "6.0.1", "sass": "1.94.2", "sass-loader": "16.0.6", - "storybook": "8.6.12", + "storybook": "9.1.16", "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.4.5", @@ -188,7 +186,7 @@ "webpack-node-externals": "3.0.0" }, "engines": { - "node": "~22", + "node": ">=22.12.0", "npm": "~10" } }, @@ -226,7 +224,7 @@ "proper-lockfile": "4.1.2", "rxjs": "7.8.1", "semver": "7.7.3", - "tldts": "7.0.18", + "tldts": "7.0.19", "zxcvbn": "4.4.2" }, "bin": { @@ -487,9 +485,9 @@ "license": "GPL-3.0" }, "node_modules/@adobe/css-tools": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", - "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", "dev": true, "license": "MIT" }, @@ -500,6 +498,215 @@ "dev": true, "license": "MIT" }, + "node_modules/@algolia/abtesting": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@algolia/abtesting/-/abtesting-1.1.0.tgz", + "integrity": "sha512-sEyWjw28a/9iluA37KLGu8vjxEIlb60uxznfTUmXImy7H5NvbpSO6yYgmgH5KiD7j+zTUUihiST0jEP12IoXow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-abtesting": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-abtesting/-/client-abtesting-5.35.0.tgz", + "integrity": "sha512-uUdHxbfHdoppDVflCHMxRlj49/IllPwwQ2cQ8DLC4LXr3kY96AHBpW0dMyi6ygkn2MtFCc6BxXCzr668ZRhLBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-analytics": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-analytics/-/client-analytics-5.35.0.tgz", + "integrity": "sha512-SunAgwa9CamLcRCPnPHx1V2uxdQwJGqb1crYrRWktWUdld0+B2KyakNEeVn5lln4VyeNtW17Ia7V7qBWyM/Skw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-common": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-common/-/client-common-5.35.0.tgz", + "integrity": "sha512-ipE0IuvHu/bg7TjT2s+187kz/E3h5ssfTtjpg1LbWMgxlgiaZIgTTbyynM7NfpSJSKsgQvCQxWjGUO51WSCu7w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-insights": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-insights/-/client-insights-5.35.0.tgz", + "integrity": "sha512-UNbCXcBpqtzUucxExwTSfAe8gknAJ485NfPN6o1ziHm6nnxx97piIbcBQ3edw823Tej2Wxu1C0xBY06KgeZ7gA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-personalization": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-personalization/-/client-personalization-5.35.0.tgz", + "integrity": "sha512-/KWjttZ6UCStt4QnWoDAJ12cKlQ+fkpMtyPmBgSS2WThJQdSV/4UWcqCUqGH7YLbwlj3JjNirCu3Y7uRTClxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-query-suggestions": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-query-suggestions/-/client-query-suggestions-5.35.0.tgz", + "integrity": "sha512-8oCuJCFf/71IYyvQQC+iu4kgViTODbXDk3m7yMctEncRSRV+u2RtDVlpGGfPlJQOrAY7OONwJlSHkmbbm2Kp/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/client-search": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/client-search/-/client-search-5.35.0.tgz", + "integrity": "sha512-FfmdHTrXhIduWyyuko1YTcGLuicVbhUyRjO3HbXE4aP655yKZgdTIfMhZ/V5VY9bHuxv/fGEh3Od1Lvv2ODNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/ingestion": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@algolia/ingestion/-/ingestion-1.35.0.tgz", + "integrity": "sha512-gPzACem9IL1Co8mM1LKMhzn1aSJmp+Vp434An4C0OBY4uEJRcqsLN3uLBlY+bYvFg8C8ImwM9YRiKczJXRk0XA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/monitoring": { + "version": "1.35.0", + "resolved": "https://registry.npmjs.org/@algolia/monitoring/-/monitoring-1.35.0.tgz", + "integrity": "sha512-w9MGFLB6ashI8BGcQoVt7iLgDIJNCn4OIu0Q0giE3M2ItNrssvb8C0xuwJQyTy1OFZnemG0EB1OvXhIHOvQwWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/recommend": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/recommend/-/recommend-5.35.0.tgz", + "integrity": "sha512-AhrVgaaXAb8Ue0u2nuRWwugt0dL5UmRgS9LXe0Hhz493a8KFeZVUE56RGIV3hAa6tHzmAV7eIoqcWTQvxzlJeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-browser-xhr": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-browser-xhr/-/requester-browser-xhr-5.35.0.tgz", + "integrity": "sha512-diY415KLJZ6x1Kbwl9u96Jsz0OstE3asjXtJ9pmk1d+5gPuQ5jQyEsgC+WmEXzlec3iuVszm8AzNYYaqw6B+Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-fetch": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-fetch/-/requester-fetch-5.35.0.tgz", + "integrity": "sha512-uydqnSmpAjrgo8bqhE9N1wgcB98psTRRQXcjc4izwMB7yRl9C8uuAQ/5YqRj04U0mMQ+fdu2fcNF6m9+Z1BzDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@algolia/requester-node-http": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/@algolia/requester-node-http/-/requester-node-http-5.35.0.tgz", + "integrity": "sha512-RgLX78ojYOrThJHrIiPzT4HW3yfQa0D7K+MQ81rhxqaNyNBu4F1r+72LNHYH/Z+y9I1Mrjrd/c/Ue5zfDgAEjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@algolia/client-common": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -528,112 +735,129 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1902.15", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.15.tgz", - "integrity": "sha512-RbqhStc6ZoRv57ZqLB36VOkBkAdU3nNezCvIs0AJV5V4+vLPMrb0hpIB0sF+9yMlMjWsolnRsj0/Fil+zQG3bw==", + "version": "0.2003.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2003.12.tgz", + "integrity": "sha512-5H40lAFF4CKY32C4HOp6bTlOF1f4WsGCwe7FjFQp9A+T7yoCBiHpIWt2JKTwV4sBoTKVDZOnuf0GG+UVKjQT4A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.15", - "rxjs": "7.8.1" + "@angular-devkit/core": "20.3.12", + "rxjs": "7.8.2" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, + "node_modules/@angular-devkit/architect/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@angular-devkit/build-angular": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-19.2.14.tgz", - "integrity": "sha512-0K8vZxXdkME31fd6/+WACug8j4eLlU7mxR2/XJvS+VQ+a7bqdEsVddZDkwdWE+Y3ccZXvD/aNLZSEuSKmVFsnA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-20.3.12.tgz", + "integrity": "sha512-HPepPbJA5vprYTWJaSCfpk0s1bPT6Ui6VjFOSb9oY+p9iq+MGkuB1I+swNcRcMLttyMD+FpbMd27F8jSeX5XVw==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.14", - "@angular-devkit/build-webpack": "0.1902.14", - "@angular-devkit/core": "19.2.14", - "@angular/build": "19.2.14", - "@babel/core": "7.26.10", - "@babel/generator": "7.26.10", - "@babel/helper-annotate-as-pure": "7.25.9", + "@angular-devkit/architect": "0.2003.12", + "@angular-devkit/build-webpack": "0.2003.12", + "@angular-devkit/core": "20.3.12", + "@angular/build": "20.3.12", + "@babel/core": "7.28.3", + "@babel/generator": "7.28.3", + "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-transform-async-generator-functions": "7.26.8", - "@babel/plugin-transform-async-to-generator": "7.25.9", - "@babel/plugin-transform-runtime": "7.26.10", - "@babel/preset-env": "7.26.9", - "@babel/runtime": "7.26.10", + "@babel/plugin-transform-async-generator-functions": "7.28.0", + "@babel/plugin-transform-async-to-generator": "7.27.1", + "@babel/plugin-transform-runtime": "7.28.3", + "@babel/preset-env": "7.28.3", + "@babel/runtime": "7.28.3", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "19.2.14", - "@vitejs/plugin-basic-ssl": "1.2.0", + "@ngtools/webpack": "20.3.12", "ansi-colors": "4.1.3", - "autoprefixer": "10.4.20", - "babel-loader": "9.2.1", + "autoprefixer": "10.4.21", + "babel-loader": "10.0.0", "browserslist": "^4.21.5", - "copy-webpack-plugin": "12.0.2", + "copy-webpack-plugin": "13.0.1", "css-loader": "7.1.2", - "esbuild-wasm": "0.25.4", + "esbuild-wasm": "0.25.9", "fast-glob": "3.3.3", "http-proxy-middleware": "3.0.5", "istanbul-lib-instrument": "6.0.3", "jsonc-parser": "3.3.1", "karma-source-map-support": "1.4.0", - "less": "4.2.2", - "less-loader": "12.2.0", + "less": "4.4.0", + "less-loader": "12.3.0", "license-webpack-plugin": "4.0.2", "loader-utils": "3.3.1", - "mini-css-extract-plugin": "2.9.2", - "open": "10.1.0", - "ora": "5.4.1", - "picomatch": "4.0.2", - "piscina": "4.8.0", - "postcss": "8.5.2", + "mini-css-extract-plugin": "2.9.4", + "open": "10.2.0", + "ora": "8.2.0", + "picomatch": "4.0.3", + "piscina": "5.1.3", + "postcss": "8.5.6", "postcss-loader": "8.1.1", "resolve-url-loader": "5.0.0", - "rxjs": "7.8.1", - "sass": "1.85.0", + "rxjs": "7.8.2", + "sass": "1.90.0", "sass-loader": "16.0.5", - "semver": "7.7.1", + "semver": "7.7.2", "source-map-loader": "5.0.0", "source-map-support": "0.5.21", - "terser": "5.39.0", + "terser": "5.43.1", "tree-kill": "1.2.2", "tslib": "2.8.1", - "webpack": "5.98.0", + "webpack": "5.101.2", "webpack-dev-middleware": "7.4.2", - "webpack-dev-server": "5.2.0", + "webpack-dev-server": "5.2.2", "webpack-merge": "6.0.1", "webpack-subresource-integrity": "5.1.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "esbuild": "0.25.4" + "esbuild": "0.25.9" }, "peerDependencies": { - "@angular/compiler-cli": "^19.0.0 || ^19.2.0-next.0", - "@angular/localize": "^19.0.0 || ^19.2.0-next.0", - "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", - "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.14", + "@angular/compiler-cli": "^20.0.0", + "@angular/core": "^20.0.0", + "@angular/localize": "^20.0.0", + "@angular/platform-browser": "^20.0.0", + "@angular/platform-server": "^20.0.0", + "@angular/service-worker": "^20.0.0", + "@angular/ssr": "^20.3.12", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", - "jest": "^29.5.0", - "jest-environment-jsdom": "^29.5.0", + "jest": "^29.5.0 || ^30.2.0", + "jest-environment-jsdom": "^29.5.0 || ^30.2.0", "karma": "^6.3.0", - "ng-packagr": "^19.0.0 || ^19.2.0-next.0", + "ng-packagr": "^20.0.0", "protractor": "^7.0.0", "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "typescript": ">=5.5 <5.9" + "typescript": ">=5.8 <6.0" }, "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, "@angular/localize": { "optional": true }, + "@angular/platform-browser": { + "optional": true + }, "@angular/platform-server": { "optional": true }, @@ -669,67 +893,23 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1902.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.14.tgz", - "integrity": "sha512-rgMkqOrxedzqLZ8w59T/0YrpWt7LDmGwt+ZhNHE7cn27jZ876yGC2Bhcn58YZh2+R03WEJ9q0ePblaBYz03SMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.14", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.14.tgz", - "integrity": "sha512-aaPEnRNIBoYT4XrrYcZlHadX8vFDTUR+4wUgcmr0cNDLeWzWtoPFeVq8TQD6kFDeqovSx/UVEblGgg/28WvHyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -755,80 +935,81 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.28.3.tgz", + "integrity": "sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/compat-data": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.28.3", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.28.0", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.28.0", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.28.3", + "@babel/plugin-transform-classes": "^7.28.3", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.28.0", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-explicit-resource-management": "^7.28.0", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.28.0", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.7", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.28.3", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.0", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", + "core-js-compat": "^3.43.0", "semver": "^6.3.1" }, "engines": { @@ -848,137 +1029,93 @@ "semver": "bin/semver.js" } }, - "node_modules/@angular-devkit/build-angular/node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", + "node_modules/@angular-devkit/build-angular/node_modules/@ngtools/webpack": { + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-20.3.12.tgz", + "integrity": "sha512-ePuofHOtbgvEq2t+hcmL30s4q9HQ/nv9ABwpLiELdVIObcWUnrnizAvM7hujve/9CQL6gRCeEkxPLPS4ZrK9AQ==", "dev": true, "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" }, "peerDependencies": { - "postcss": "^8.1.0" + "@angular/compiler-cli": "^20.0.0", + "typescript": ">=5.8 <6.0", + "webpack": "^5.54.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "node_modules/@angular-devkit/build-angular/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/babel-loader": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-10.0.0.tgz", + "integrity": "sha512-z8jt+EdS61AMw22nSfoNJAZ0vrtmhPRVi6ghL3rCeRZI8cdNYFiV5xeV3HbE7rlZZNmGH8BVccwWt8/ED0QOHA==", "dev": true, "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" + "find-up": "^5.0.0" }, "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" + "node": "^18.20.0 || ^20.10.0 || >=22.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0", + "webpack": ">=5.61.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@angular-devkit/build-angular/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/build-angular/node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" + "restore-cursor": "^5.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@angular-devkit/build-angular/node_modules/convert-source-map": { @@ -988,48 +1125,13 @@ "dev": true, "license": "MIT" }, - "node_modules/@angular-devkit/build-angular/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "node_modules/@angular-devkit/build-angular/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, - "node_modules/@angular-devkit/build-angular/node_modules/copy-webpack-plugin": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-12.0.2.tgz", - "integrity": "sha512-SNwdBeHyII+rWvee/bTnAYyO8vfVdcSTud4EIb6jcZ8inLeWucJE0DnxXQBjlQ5zlteuuvooGQy3LIyGxhvlOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.1", - "globby": "^14.0.0", - "normalize-path": "^3.0.0", - "schema-utils": "^4.2.0", - "serialize-javascript": "^6.0.2" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - } - }, "node_modules/@angular-devkit/build-angular/node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -1054,147 +1156,27 @@ "node": ">=4.0" } }, - "node_modules/@angular-devkit/build-angular/node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "node_modules/@angular-devkit/build-angular/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "dev": true, "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, "engines": { - "node": ">= 0.10.0" + "node": ">=12" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/build-angular/node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/build-angular/node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "node_modules/@angular-devkit/build-angular/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "dev": true, "license": "MIT", "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -1207,37 +1189,34 @@ "dev": true, "license": "MIT" }, - "node_modules/@angular-devkit/build-angular/node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "node_modules/@angular-devkit/build-angular/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", "dev": true, "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", + "node": ">=18" + }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "node_modules/@angular-devkit/build-angular/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", "dev": true, "license": "MIT", - "bin": { - "mime": "cli.js" - }, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@angular-devkit/build-angular/node_modules/mime-db": { @@ -1263,48 +1242,14 @@ "node": ">= 0.6" } }, - "node_modules/@angular-devkit/build-angular/node_modules/mini-css-extract-plugin": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", - "integrity": "sha512-GJuACcS//jtq4kCtd5ii/M0SZf7OZRH+BxdqXZHaJfb8TJiVl+NgQRPwiYt2EuqeSkNydn/7vP+bcE27C5mb9w==", + "node_modules/@angular-devkit/build-angular/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "dev": true, "license": "MIT", "dependencies": { - "schema-utils": "^4.0.0", - "tapable": "^2.2.1" - }, - "engines": { - "node": ">= 12.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/open": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.0.tgz", - "integrity": "sha512-mnkeQ1qP5Ue2wd+aivTD3NHd/lZ96Lu0jgf0pwktLPtx6cTZiH7tyeGRRHs0zX0rbrahXPnXlUnbeXyaBBuIaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" + "mimic-function": "^5.0.0" }, "engines": { "node": ">=18" @@ -1313,40 +1258,60 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "node_modules/@angular-devkit/build-angular/node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/build-angular/node_modules/postcss": { - "version": "8.5.2", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.2.tgz", - "integrity": "sha512-MjOadfU3Ys9KYoX0AdkBlFEF1Vx37uCCeN4ZHnmwm9FfpbsGWMZeBLMmmpY+6Ocqod7mkdZ0DT31OlbsFrLlkA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" }, "engines": { - "node": "^10 || ^12 || >=14" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/@angular-devkit/build-angular/node_modules/postcss-loader": { @@ -1381,68 +1346,37 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "node_modules/@angular-devkit/build-angular/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "side-channel": "^1.0.6" + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" }, "engines": { - "node": ">=0.6" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "tslib": "^2.1.0" } }, "node_modules/@angular-devkit/build-angular/node_modules/sass": { - "version": "1.85.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz", - "integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==", + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", + "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1502,9 +1436,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -1514,114 +1448,58 @@ "node": ">=10" } }, - "node_modules/@angular-devkit/build-angular/node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "node_modules/@angular-devkit/build-angular/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "dev": true, "license": "MIT", "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" }, "engines": { - "node": ">= 0.8.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/@angular-devkit/build-angular/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@angular-devkit/build-angular/node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" + "ansi-regex": "^6.0.1" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "node": ">=12" }, - "engines": { - "node": ">= 0.6" + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/@angular-devkit/build-angular/node_modules/webpack": { - "version": "5.98.0", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.98.0.tgz", - "integrity": "sha512-UFynvx+gM44Gv9qFgj0acCQK2VE1CtdfwFdimkapco3hlPCJ/zeq73n2yVKimVbtm+TnApIugGhLJnkU6gjYXA==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.2.tgz", + "integrity": "sha512-4JLXU0tD6OZNVqlwzm3HGEhAHufSiyv+skb7q0d2367VDMzrU1Q/ZeepvkcHH0rZie6uqEtTQQe0OEOOluH3Mg==", "dev": true, "license": "MIT", "dependencies": { "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", + "@types/estree": "^1.0.8", + "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", + "acorn": "^8.15.0", + "acorn-import-phases": "^1.0.3", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", + "enhanced-resolve": "^5.17.3", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", @@ -1631,11 +1509,11 @@ "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", - "schema-utils": "^4.3.0", + "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" + "webpack-sources": "^3.3.3" }, "bin": { "webpack": "bin/webpack.js" @@ -1653,138 +1531,18 @@ } } }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.0.tgz", - "integrity": "sha512-90SqqYXA2SK36KcT6o1bvwvZfJFcmoamqeJY7+boioffX9g9C0wjjJRGUrQIuh43pb0ttX7+ssavmj/WN2RHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.13", - "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.21", - "@types/serve-index": "^1.9.4", - "@types/serve-static": "^1.15.5", - "@types/sockjs": "^0.3.36", - "@types/ws": "^8.5.10", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "express": "^4.21.2", - "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.7", - "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", - "open": "^10.0.3", - "p-retry": "^6.2.0", - "schema-utils": "^4.2.0", - "selfsigned": "^2.4.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.2", - "ws": "^8.18.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@angular-devkit/build-angular/node_modules/webpack-dev-server/node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1902.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1902.14.tgz", - "integrity": "sha512-XDNB8Nlau/v59Ukd6UgBRBRnTnUmC244832SECmMxXHs1ljJMWGlI1img2xPErGd8426rUA9Iws4RkQiqbsybQ==", + "version": "0.2003.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2003.12.tgz", + "integrity": "sha512-IkhCU0nAsXYBQOfHu2gQBcYBKhaV1c8wYtu7MmelBcN/iUrG8hRf1sZx+ppUgsdZuBYxCiDiLpcfRVRCIASkvw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1902.14", - "rxjs": "7.8.1" + "@angular-devkit/architect": "0.2003.12", + "rxjs": "7.8.2" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -1793,66 +1551,32 @@ "webpack-dev-server": "^5.0.2" } }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1902.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.14.tgz", - "integrity": "sha512-rgMkqOrxedzqLZ8w59T/0YrpWt7LDmGwt+ZhNHE7cn27jZ876yGC2Bhcn58YZh2+R03WEJ9q0ePblaBYz03SMw==", + "node_modules/@angular-devkit/build-webpack/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@angular-devkit/core": "19.2.14", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.14.tgz", - "integrity": "sha512-aaPEnRNIBoYT4XrrYcZlHadX8vFDTUR+4wUgcmr0cNDLeWzWtoPFeVq8TQD6kFDeqovSx/UVEblGgg/28WvHyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "tslib": "^2.1.0" } }, "node_modules/@angular-devkit/core": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.15.tgz", - "integrity": "sha512-pU2RZYX6vhd7uLSdLwPnuBcr0mXJSjp3EgOXKsrlQFQZevc+Qs+2JdXgIElnOT/aDqtRtriDmLlSbtdE8n3ZbA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-20.3.12.tgz", + "integrity": "sha512-ReFxd/UOoVDr3+kIUjmYILQZF89qg62POdY7a7OqBH7plmInFlYVSEDouJvGqj3LVCPiqTk2ZOSChbhS/eLxXA==", "dev": true, "license": "MIT", "dependencies": { "ajv": "8.17.1", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" + "picomatch": "4.0.3", + "rxjs": "7.8.2", + "source-map": "0.7.6" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, @@ -1865,34 +1589,286 @@ } } }, + "node_modules/@angular-devkit/core/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@angular-devkit/core/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/core/node_modules/source-map": { + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, "node_modules/@angular-devkit/schematics": { - "version": "19.2.15", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.15.tgz", - "integrity": "sha512-kNOJ+3vekJJCQKWihNmxBkarJzNW09kP5a9E1SRNiQVNOUEeSwcRR0qYotM65nx821gNzjjhJXnAZ8OazWldrg==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-20.3.12.tgz", + "integrity": "sha512-JqJ1u59y+Ud51k/8MHYzSP+aQOeC2PJBaDmMnvqfWVaIt6n3x4gc/VtuhqhpJ0SKulbFuOWgAfI6QbPFrgUYQQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.15", + "@angular-devkit/core": "20.3.12", "jsonc-parser": "3.3.1", "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" + "ora": "8.2.0", + "rxjs": "7.8.2" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular-eslint/builder": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-19.6.0.tgz", - "integrity": "sha512-hUdYS1mSB09b5ABi2tuWeMTVprYHW+x6KmeAFJfXC6aMOa4NYQBdetIjOLwr7qUDlq1S/+2+HiX/FO76FPHClw==", + "node_modules/@angular-devkit/schematics/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": ">= 0.1900.0 < 0.2000.0", - "@angular-devkit/core": ">= 19.0.0 < 20.0.0" + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular-devkit/schematics/node_modules/is-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@angular-eslint/builder": { + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/builder/-/builder-20.7.0.tgz", + "integrity": "sha512-qgf4Cfs1z0VsVpzF/OnxDRvBp60OIzeCsp4mzlckWYVniKo19EPIN6kFDol5eTAIOMPgiBQlMIwgQMHgocXEig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/architect": ">= 0.2000.0 < 0.2100.0", + "@angular-devkit/core": ">= 20.0.0 < 21.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", @@ -1900,21 +1876,22 @@ } }, "node_modules/@angular-eslint/bundled-angular-compiler": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-19.6.0.tgz", - "integrity": "sha512-ro+seaTAg5GvtJ72uWEEnP9J5mT0vtgdqH6YMrmMt4pZbSZxvkLfLjZGkXo/HjVDVcCjPnmZeMwKN+uoEc27Jg==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-20.7.0.tgz", + "integrity": "sha512-9KPz24YoiL0SvTtTX6sd1zmysU5cKOCcmpEiXkCoO3L2oYZGlVxmMT4hfSaHMt8qmfvV2KzQMoR6DZM84BwRzQ==", "dev": true, "license": "MIT" }, "node_modules/@angular-eslint/eslint-plugin": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-19.6.0.tgz", - "integrity": "sha512-IOMfFi/rPNrPwxZwIGTqWw0C5pC2Facwg3llmJoQFq8w2sUE0nNBL5uSQv5dT8s6ucum4g+RFNYHNe20SEOvRw==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-20.7.0.tgz", + "integrity": "sha512-aHH2YTiaonojsKN+y2z4IMugCwdsH/dYIjYBig6kfoSPyf9rGK4zx+gnNGq/pGRjF3bOYrmFgIviYpQVb80inQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.6.0", - "@angular-eslint/utils": "19.6.0" + "@angular-eslint/bundled-angular-compiler": "20.7.0", + "@angular-eslint/utils": "20.7.0", + "ts-api-utils": "^2.1.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -1923,18 +1900,19 @@ } }, "node_modules/@angular-eslint/eslint-plugin-template": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-19.6.0.tgz", - "integrity": "sha512-SDGbNSCUuPmqVesy5SvRE2MV7AKvvA/bVJwL9Fz5KYCHYxJz1rrJ8FknjWAfmg0qO2TMs1ZI9hov8JL+Bc4BBw==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-20.7.0.tgz", + "integrity": "sha512-WFmvW2vBR6ExsSKEaActQTteyw6ikWyuJau9XmWEPFd+2eusEt/+wO21ybjDn3uc5FTp1IcdhfYy+U5OdDjH5w==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.6.0", - "@angular-eslint/utils": "19.6.0", + "@angular-eslint/bundled-angular-compiler": "20.7.0", + "@angular-eslint/utils": "20.7.0", "aria-query": "5.3.2", "axobject-query": "4.1.0" }, "peerDependencies": { + "@angular-eslint/template-parser": "20.7.0", "@typescript-eslint/types": "^7.11.0 || ^8.0.0", "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", "eslint": "^8.57.0 || ^9.0.0", @@ -1942,57 +1920,61 @@ } }, "node_modules/@angular-eslint/schematics": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-19.6.0.tgz", - "integrity": "sha512-lJzwHju7bhJ3p+SZnY0JVwGjxF2q68gUdOYhdU62pglfYkS5lm+A5LM/VznRvdpZOH69vvZ9gizQ8W1P525cdw==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/schematics/-/schematics-20.7.0.tgz", + "integrity": "sha512-S0onfRipDUIL6gFGTFjiWwUDhi42XYrBoi3kJ3wBbKBeIgYv9SP1ppTKDD4ZoDaDU9cQE8nToX7iPn9ifMw6eQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": ">= 19.0.0 < 20.0.0", - "@angular-devkit/schematics": ">= 19.0.0 < 20.0.0", - "@angular-eslint/eslint-plugin": "19.6.0", - "@angular-eslint/eslint-plugin-template": "19.6.0", - "ignore": "7.0.4", - "semver": "7.7.2", + "@angular-devkit/core": ">= 20.0.0 < 21.0.0", + "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", + "@angular-eslint/eslint-plugin": "20.7.0", + "@angular-eslint/eslint-plugin-template": "20.7.0", + "ignore": "7.0.5", + "semver": "7.7.3", "strip-json-comments": "3.1.1" } }, - "node_modules/@angular-eslint/schematics/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@angular-eslint/template-parser": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-19.6.0.tgz", - "integrity": "sha512-NGxXUZkI5lXjoKnmL51C8DoJx8AjwF9sonieC2EVxgXycK2MYAamFWYGHMiVemzFsg1nIv+JvhHITgjSjyC3HQ==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-20.7.0.tgz", + "integrity": "sha512-CVskZnF38IIxVVlKWi1VCz7YH/gHMJu2IY9bD1AVoBBGIe0xA4FRXJkW2Y+EDs9vQqZTkZZljhK5gL65Ro1PeQ==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.6.0", - "eslint-scope": "^8.0.2" + "@angular-eslint/bundled-angular-compiler": "20.7.0", + "eslint-scope": "^9.0.0" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0", "typescript": "*" } }, + "node_modules/@angular-eslint/template-parser/node_modules/eslint-scope": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.0.0.tgz", + "integrity": "sha512-+Yh0LeQKq+mW/tQArNj67tljR3L1HajDTQPuZOEwC00oBdoIDQrr89yBgjAlzAwRrY/5zDkM3v99iGHwz9y0dw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/@angular-eslint/utils": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-19.6.0.tgz", - "integrity": "sha512-ygtsmRKHNqrzG2mpUj1XwLNRoG+ikYkizsOuq5xPRM8o6dCw03H5eel4s7hnXT4c09WbpnoaVNi9O3xFLIETJQ==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-20.7.0.tgz", + "integrity": "sha512-B6EJHbsk2W/lnS3kS/gm56VGvX735419z/DzgbRDcOvqMGMLwD1ILzv5OTEcL1rzpnB0AHW+IxOu6y/aCzSNUA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-eslint/bundled-angular-compiler": "19.6.0" + "@angular-eslint/bundled-angular-compiler": "20.7.0" }, "peerDependencies": { "@typescript-eslint/utils": "^7.11.0 || ^8.0.0", @@ -2001,81 +1983,90 @@ } }, "node_modules/@angular/animations": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-19.2.14.tgz", - "integrity": "sha512-xhl8fLto5HHJdVj8Nb6EoBEiTAcXuWDYn1q5uHcGxyVH3kiwENWy/2OQXgCr2CuWo2e6hNUGzSLf/cjbsMNqEA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-20.3.15.tgz", + "integrity": "sha512-ikyKfhkxoqQA6JcBN0B9RaN6369sM1XYX81Id0lI58dmWCe7gYfrTp8ejqxxKftl514psQO3pkW8Gn1nJ131Gw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "19.2.14", - "@angular/core": "19.2.14" + "@angular/core": "20.3.15" } }, "node_modules/@angular/build": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-19.2.14.tgz", - "integrity": "sha512-PAUR8vZpGKXy0Vc5gpJkigOthoj5YeGDpeykl/yLi6sx6yAIlXcE0MD+LGehKeqFSBL56rEpn9n710lI7eTJwg==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-20.3.12.tgz", + "integrity": "sha512-iAZve4VPviC8y6RFctyh3qFXSlP5mth9K46/0zasB4LV4pcmu8BrzIHERxIn/jCDNdVdPh973kxo1ksO4WpyuA==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.1902.14", - "@babel/core": "7.26.10", - "@babel/helper-annotate-as-pure": "7.25.9", + "@angular-devkit/architect": "0.2003.12", + "@babel/core": "7.28.3", + "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", - "@babel/plugin-syntax-import-attributes": "7.26.0", - "@inquirer/confirm": "5.1.6", - "@vitejs/plugin-basic-ssl": "1.2.0", - "beasties": "0.3.2", + "@inquirer/confirm": "5.1.14", + "@vitejs/plugin-basic-ssl": "2.1.0", + "beasties": "0.3.5", "browserslist": "^4.23.0", - "esbuild": "0.25.4", - "fast-glob": "3.3.3", + "esbuild": "0.25.9", "https-proxy-agent": "7.0.6", "istanbul-lib-instrument": "6.0.3", - "listr2": "8.2.5", + "jsonc-parser": "3.3.1", + "listr2": "9.0.1", "magic-string": "0.30.17", "mrmime": "2.0.1", - "parse5-html-rewriting-stream": "7.0.0", - "picomatch": "4.0.2", - "piscina": "4.8.0", - "rollup": "4.34.8", - "sass": "1.85.0", - "semver": "7.7.1", + "parse5-html-rewriting-stream": "8.0.0", + "picomatch": "4.0.3", + "piscina": "5.1.3", + "rollup": "4.52.3", + "sass": "1.90.0", + "semver": "7.7.2", "source-map-support": "0.5.21", - "vite": "6.2.7", - "watchpack": "2.4.2" + "tinyglobby": "0.2.14", + "vite": "7.1.11", + "watchpack": "2.4.4" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "optionalDependencies": { - "lmdb": "3.2.6" + "lmdb": "3.4.2" }, "peerDependencies": { - "@angular/compiler": "^19.0.0 || ^19.2.0-next.0", - "@angular/compiler-cli": "^19.0.0 || ^19.2.0-next.0", - "@angular/localize": "^19.0.0 || ^19.2.0-next.0", - "@angular/platform-server": "^19.0.0 || ^19.2.0-next.0", - "@angular/service-worker": "^19.0.0 || ^19.2.0-next.0", - "@angular/ssr": "^19.2.14", + "@angular/compiler": "^20.0.0", + "@angular/compiler-cli": "^20.0.0", + "@angular/core": "^20.0.0", + "@angular/localize": "^20.0.0", + "@angular/platform-browser": "^20.0.0", + "@angular/platform-server": "^20.0.0", + "@angular/service-worker": "^20.0.0", + "@angular/ssr": "^20.3.12", "karma": "^6.4.0", "less": "^4.2.0", - "ng-packagr": "^19.0.0 || ^19.2.0-next.0", + "ng-packagr": "^20.0.0", "postcss": "^8.4.0", "tailwindcss": "^2.0.0 || ^3.0.0 || ^4.0.0", - "typescript": ">=5.5 <5.9" + "tslib": "^2.3.0", + "typescript": ">=5.8 <6.0", + "vitest": "^3.1.1" }, "peerDependenciesMeta": { + "@angular/core": { + "optional": true + }, "@angular/localize": { "optional": true }, + "@angular/platform-browser": { + "optional": true + }, "@angular/platform-server": { "optional": true }, @@ -2099,70 +2090,29 @@ }, "tailwindcss": { "optional": true - } - } - }, - "node_modules/@angular/build/node_modules/@angular-devkit/architect": { - "version": "0.1902.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.14.tgz", - "integrity": "sha512-rgMkqOrxedzqLZ8w59T/0YrpWt7LDmGwt+ZhNHE7cn27jZ876yGC2Bhcn58YZh2+R03WEJ9q0ePblaBYz03SMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.14", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@angular/build/node_modules/@angular-devkit/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.14.tgz", - "integrity": "sha512-aaPEnRNIBoYT4XrrYcZlHadX8vFDTUR+4wUgcmr0cNDLeWzWtoPFeVq8TQD6kFDeqovSx/UVEblGgg/28WvHyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { + }, + "vitest": { "optional": true } } }, "node_modules/@angular/build/node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -2194,10 +2144,23 @@ "dev": true, "license": "MIT" }, + "node_modules/@angular/build/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@angular/build/node_modules/sass": { - "version": "1.85.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.85.0.tgz", - "integrity": "sha512-3ToiC1xZ1Y8aU7+CkgCI/tqyuPXEmYGJXO7H4uqp0xkLXUqp88rQQ4j1HmP37xSJLbCJPaIiv+cT1y+grssrww==", + "version": "1.90.0", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.90.0.tgz", + "integrity": "sha512-9GUyuksjw70uNpb1MTYWsH9MQHOHY6kwfnkafC24+7aOMZn9+rVMBxRbLvw756mrBFbIsFg6Xw9IkR2Fnn3k+Q==", "dev": true, "license": "MIT", "dependencies": { @@ -2216,9 +2179,9 @@ } }, "node_modules/@angular/build/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -2228,194 +2191,108 @@ "node": ">=10" } }, - "node_modules/@angular/build/node_modules/vite": { - "version": "6.2.7", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.7.tgz", - "integrity": "sha512-qg3LkeuinTrZoJHHF94coSaTfIPyBYoywp+ys4qu20oSJFbKMYoIJo0FWJT9q6Vp49l6z9IsJRbHdcGtiKbGoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "postcss": "^8.5.3", - "rollup": "^4.30.1" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, "node_modules/@angular/cdk": { - "version": "19.2.18", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-19.2.18.tgz", - "integrity": "sha512-aGMHOYK/VV9PhxGTUDwiu/4ozoR/RKz8cimI+QjRxEBhzn4EPqjUDSganvlhmgS7cTN3+aqozdvF/GopMRJjLg==", + "version": "20.2.14", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-20.2.14.tgz", + "integrity": "sha512-7bZxc01URbiPiIBWThQ69XwOxVduqEKN4PhpbF2AAyfMc/W8Hcr4VoIJOwL0O1Nkq5beS8pCAqoOeIgFyXd/kg==", "license": "MIT", "dependencies": { - "parse5": "^7.1.2", + "parse5": "^8.0.0", "tslib": "^2.3.0" }, "peerDependencies": { - "@angular/common": "^19.0.0 || ^20.0.0", - "@angular/core": "^19.0.0 || ^20.0.0", + "@angular/common": "^20.0.0 || ^21.0.0", + "@angular/core": "^20.0.0 || ^21.0.0", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/cli": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-19.2.14.tgz", - "integrity": "sha512-jZvNHAwmyhgUqSIs6OW8YH1rX9XKytm4zPxJol1Xk56F8yAhnrUtukcOi3b7Dv19Z+9eXkwV/Db+2dGjWIE0DA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-20.3.12.tgz", + "integrity": "sha512-vqVyVjbFPCRMjA5evL7tV2JeR6Anuzb9WcXTMB17fr7uzKNNAvo7KyRaOJjp+TU4JDARTNyGPy0aywfPx7R60A==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/architect": "0.1902.14", - "@angular-devkit/core": "19.2.14", - "@angular-devkit/schematics": "19.2.14", - "@inquirer/prompts": "7.3.2", - "@listr2/prompt-adapter-inquirer": "2.0.18", - "@schematics/angular": "19.2.14", + "@angular-devkit/architect": "0.2003.12", + "@angular-devkit/core": "20.3.12", + "@angular-devkit/schematics": "20.3.12", + "@inquirer/prompts": "7.8.2", + "@listr2/prompt-adapter-inquirer": "3.0.1", + "@modelcontextprotocol/sdk": "1.17.3", + "@schematics/angular": "20.3.12", "@yarnpkg/lockfile": "1.1.0", + "algoliasearch": "5.35.0", "ini": "5.0.0", "jsonc-parser": "3.3.1", - "listr2": "8.2.5", - "npm-package-arg": "12.0.2", - "npm-pick-manifest": "10.0.0", - "pacote": "20.0.0", + "listr2": "9.0.1", + "npm-package-arg": "13.0.0", + "pacote": "21.0.0", "resolve": "1.22.10", - "semver": "7.7.1", - "symbol-observable": "4.0.0", - "yargs": "17.7.2" + "semver": "7.7.2", + "yargs": "18.0.0", + "zod": "3.25.76" }, "bin": { "ng": "bin/ng.js" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.1902.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1902.14.tgz", - "integrity": "sha512-rgMkqOrxedzqLZ8w59T/0YrpWt7LDmGwt+ZhNHE7cn27jZ876yGC2Bhcn58YZh2+R03WEJ9q0ePblaBYz03SMw==", + "node_modules/@angular/cli/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.14", - "rxjs": "7.8.1" - }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.14.tgz", - "integrity": "sha512-aaPEnRNIBoYT4XrrYcZlHadX8vFDTUR+4wUgcmr0cNDLeWzWtoPFeVq8TQD6kFDeqovSx/UVEblGgg/28WvHyg==", + "node_modules/@angular/cli/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=12" }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.14.tgz", - "integrity": "sha512-s89/MWXHy8+GP/cRfFbSECIG3FQQQwNVv44OOmghPVgKQgQ+EoE/zygL2hqKYTUPoPaS/IhNXdXjSE5pS9yLeg==", + "node_modules/@angular/cli/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "dev": true, - "license": "MIT", + "license": "ISC", "dependencies": { - "@angular-devkit/core": "19.2.14", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" + "node": ">=20" } }, + "node_modules/@angular/cli/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, "node_modules/@angular/cli/node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "license": "ISC", "bin": { @@ -2425,80 +2302,164 @@ "node": ">=10" } }, + "node_modules/@angular/cli/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/cli/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@angular/cli/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@angular/cli/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@angular/cli/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, "node_modules/@angular/common": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-19.2.14.tgz", - "integrity": "sha512-NcNklcuyqaTjOVGf7aru8APX9mjsnZ01gFZrn47BxHozhaR0EMRrotYQTdi8YdVjPkeYFYanVntSLfhyobq/jg==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-20.3.15.tgz", + "integrity": "sha512-k4mCXWRFiOHK3bUKfWkRQQ8KBPxW8TAJuKLYCsSHPCpMz6u0eA1F0VlrnOkZVKWPI792fOaEAWH2Y4PTaXlUHw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "19.2.14", + "@angular/core": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-19.2.14.tgz", - "integrity": "sha512-ZqJDYOdhgKpVGNq3+n/Gbxma8DVYElDsoRe0tvNtjkWBVdaOxdZZUqmJ3kdCBsqD/aqTRvRBu0KGo9s2fCChkA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-20.3.15.tgz", + "integrity": "sha512-lMicIAFAKZXa+BCZWs3soTjNQPZZXrF/WMVDinm8dQcggNarnDj4UmXgKSyXkkyqK5SLfnLsXVzrX6ndVT6z7A==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@angular/compiler-cli": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-19.2.14.tgz", - "integrity": "sha512-e9/h86ETjoIK2yTLE9aUeMCKujdg/du2pq7run/aINjop4RtnNOw+ZlSTUa6R65lP5CVwDup1kPytpAoifw8cA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-20.3.15.tgz", + "integrity": "sha512-8sJoxodxsfyZ8eJ5r6Bx7BCbazXYgsZ1+dE8t5u5rTQ6jNggwNtYEzkyReoD5xvP+MMtRkos3xpwq4rtFnpI6A==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "7.26.9", + "@babel/core": "7.28.3", "@jridgewell/sourcemap-codec": "^1.4.14", "chokidar": "^4.0.0", "convert-source-map": "^1.5.1", "reflect-metadata": "^0.2.0", "semver": "^7.0.0", "tslib": "^2.3.0", - "yargs": "^17.2.1" + "yargs": "^18.0.0" }, "bin": { "ng-xi18n": "bundles/src/bin/ng_xi18n.js", - "ngc": "bundles/src/bin/ngc.js", - "ngcc": "bundles/ngcc/index.js" + "ngc": "bundles/src/bin/ngc.js" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "19.2.14", - "typescript": ">=5.5 <5.9" + "@angular/compiler": "20.3.15", + "typescript": ">=5.8 <6.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, "node_modules/@angular/compiler-cli/node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", + "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.3", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.3", + "@babel/parser": "^7.28.3", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.3", + "@babel/types": "^7.28.2", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -2530,55 +2491,192 @@ "semver": "bin/semver.js" } }, + "node_modules/@angular/compiler-cli/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@angular/compiler-cli/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@angular/compiler-cli/node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@angular/compiler-cli/node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@angular/compiler-cli/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@angular/compiler-cli/node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@angular/compiler-cli/node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@angular/compiler-cli/node_modules/yargs": { + "version": "18.0.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", + "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^7.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/@angular/compiler-cli/node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, "node_modules/@angular/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-19.2.14.tgz", - "integrity": "sha512-EVErpW9tGqJ/wNcAN3G/ErH8pHCJ8mM1E6bsJ8UJIpDTZkpqqYjBMtZS9YWH5n3KwUd1tAkAB2w8FK125AjDUQ==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-20.3.15.tgz", + "integrity": "sha512-NMbX71SlTZIY9+rh/SPhRYFJU0pMJYW7z/TBD4lqiO+b0DTOIg1k7Pg9ydJGqSjFO1Z4dQaA6TteNuF99TJCNw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { + "@angular/compiler": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0" + }, + "peerDependenciesMeta": { + "@angular/compiler": { + "optional": true + }, + "zone.js": { + "optional": true + } } }, "node_modules/@angular/forms": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-19.2.14.tgz", - "integrity": "sha512-hWtDOj2B0AuRTf+nkMJeodnFpDpmEK9OIhIv1YxcRe73ooaxrIdjgugkElO8I9Tj0E4/7m117ezhWDUkbqm1zA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-20.3.15.tgz", + "integrity": "sha512-gS5hQkinq52pm/7mxz4yHPCzEcmRWjtUkOVddPH0V1BW/HMni/p4Y6k2KqKBeGb9p8S5EAp6PDxDVLOPukp3mg==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "19.2.14", - "@angular/core": "19.2.14", - "@angular/platform-browser": "19.2.14", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15", + "@angular/platform-browser": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/platform-browser": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-19.2.14.tgz", - "integrity": "sha512-hzkT5nmA64oVBQl6PRjdL4dIFT1n7lfM9rm5cAoS+6LUUKRgiE2d421Kpn/Hz3jaCJfo+calMIdtSMIfUJBmww==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-20.3.15.tgz", + "integrity": "sha512-TxRM/wTW/oGXv/3/Iohn58yWoiYXOaeEnxSasiGNS1qhbkcKtR70xzxW6NjChBUYAixz2ERkLURkpx3pI8Q6Dw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "19.2.14", - "@angular/common": "19.2.14", - "@angular/core": "19.2.14" + "@angular/animations": "20.3.15", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15" }, "peerDependenciesMeta": { "@angular/animations": { @@ -2587,38 +2685,38 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-19.2.14.tgz", - "integrity": "sha512-Hfz0z1KDQmIdnFXVFCwCPykuIsHPkr1uW2aY396eARwZ6PK8i0Aadcm1ZOnpd3MR1bMyDrJo30VRS5kx89QWvA==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-20.3.15.tgz", + "integrity": "sha512-RizuRdBt0d6ongQ2y8cr8YsXFyjF8f91vFfpSNw+cFj+oiEmRC1txcWUlH5bPLD9qSDied8qazUi0Tb8VPQDGw==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "19.2.14", - "@angular/compiler": "19.2.14", - "@angular/core": "19.2.14", - "@angular/platform-browser": "19.2.14" + "@angular/common": "20.3.15", + "@angular/compiler": "20.3.15", + "@angular/core": "20.3.15", + "@angular/platform-browser": "20.3.15" } }, "node_modules/@angular/router": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-19.2.14.tgz", - "integrity": "sha512-cBTWY9Jx7YhbmDYDb7Hqz4Q7UNIMlKTkdKToJd2pbhIXyoS+kHVQrySmyca+jgvYMjWnIjsAEa3dpje12D4mFw==", + "version": "20.3.15", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-20.3.15.tgz", + "integrity": "sha512-6+qgk8swGSoAu7ISSY//GatAyCP36hEvvUgvjbZgkXLLH9yUQxdo77ij05aJ5s0OyB25q/JkqS8VTY0z1yE9NQ==", "license": "MIT", "dependencies": { "tslib": "^2.3.0" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "19.2.14", - "@angular/core": "19.2.14", - "@angular/platform-browser": "19.2.14", + "@angular/common": "20.3.15", + "@angular/core": "20.3.15", + "@angular/platform-browser": "20.3.15", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -2726,16 +2824,16 @@ } }, "node_modules/@babel/generator": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.10.tgz", - "integrity": "sha512-rRHT8siFIXQrAYOYqZQVsAr8vJ+cBNqcVAY6m5V8/4QqzaPl+zDBe6cLEPRDuNOUf3ww8RfJVlOyQMoSI+5Ang==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", + "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.10", - "@babel/types": "^7.26.10", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.3", + "@babel/types": "^7.28.2", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -2743,13 +2841,12 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", + "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -2801,18 +2898,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-class-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2839,18 +2924,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-create-regexp-features-plugin/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -2966,18 +3039,6 @@ "@babel/core": "^7.0.0" } }, - "node_modules/@babel/helper-remap-async-to-generator/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/helper-replace-supers": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", @@ -3280,12 +3341,12 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3482,15 +3543,14 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", - "dev": true, + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", + "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.28.0" }, "engines": { "node": ">=6.9.0" @@ -3500,15 +3560,14 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", - "dev": true, + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -3599,18 +3658,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-classes/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-computed-properties": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", @@ -4085,18 +4132,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-private-property-in-object/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-property-literals": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", @@ -4159,16 +4194,16 @@ } }, "node_modules/@babel/plugin-transform-runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.26.10.tgz", - "integrity": "sha512-NWaL2qG6HRpONTnj4JvDU6th4jYeZOJgu3QhmFTCihib0ermtOJqktA5BduGm3suhhVe9EMP9c9+mfJ/I9slqw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.28.3.tgz", + "integrity": "sha512-Y6ab1kGqZ0u42Zv/4a7l0l72n9DKP/MKoKWaUSBylrhNZO2prYuqFOLbn5aW5SIFXwSH93yfjbgllL8lxuGKLg==", "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.26.5", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "babel-plugin-polyfill-corejs2": "^0.4.14", + "babel-plugin-polyfill-corejs3": "^0.13.0", + "babel-plugin-polyfill-regenerator": "^0.6.5", "semver": "^6.3.1" }, "engines": { @@ -4282,18 +4317,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-typescript/node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@babel/plugin-transform-unicode-escapes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", @@ -4441,68 +4464,6 @@ "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/preset-env/node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.28.0.tgz", - "integrity": "sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.28.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-env/node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", - "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.5", - "core-js-compat": "^3.43.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, "node_modules/@babel/preset-env/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", @@ -4546,13 +4507,10 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.3.tgz", + "integrity": "sha512-9uIQ10o0WGdpP6GDhXcdOJPJuDgFtIDtN/9+ArJQ2NAfAmiuhTQdzkaTGR33v43GYS2UrSA0eX2pPPHoFVvpxA==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } @@ -4657,9 +4615,9 @@ "link": true }, "node_modules/@bitwarden/commercial-sdk-internal": { - "version": "0.2.0-main.395", - "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.395.tgz", - "integrity": "sha512-DrxL3iA29hzWpyxPyZjiXx0m+EHOgk4CVb+BAi2SoxsacmyHYuTgXuASFMieRz2rv85wS3UR0N64Ok9lC+xNYA==", + "version": "0.2.0-main.403", + "resolved": "https://registry.npmjs.org/@bitwarden/commercial-sdk-internal/-/commercial-sdk-internal-0.2.0-main.403.tgz", + "integrity": "sha512-M2ZUu29oua7CaDTNK7mCwY7PhaIUbNYogAAvxLOmkJuyHNxxqvS9usjjlD2CkQVNBeTUFqvAQpaZQo9vgzEEFA==", "license": "BITWARDEN SOFTWARE DEVELOPMENT KIT LICENSE AGREEMENT", "dependencies": { "type-fest": "^4.41.0" @@ -4762,9 +4720,9 @@ "link": true }, "node_modules/@bitwarden/sdk-internal": { - "version": "0.2.0-main.395", - "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.395.tgz", - "integrity": "sha512-biExeL2Grp11VQjjK6QM16+WOYk87mTgUhYKFm+Bu/A0zZBzhL/6AocpA9h2T5M8rLCGVVJVUMaXUW3YrSTqEA==", + "version": "0.2.0-main.403", + "resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.403.tgz", + "integrity": "sha512-ROEZdTbeKU68kDh9WYm9wKsLQD5jdTRclXLKl8x0aTj+Tx0nKyyXmLyUfOP+qh3EHIetij4jwPx2z3uS+7r8mg==", "license": "GPL-3.0", "dependencies": { "type-fest": "^4.41.0" @@ -6144,9 +6102,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.4.tgz", - "integrity": "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", + "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", "cpu": [ "ppc64" ], @@ -6161,9 +6119,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.4.tgz", - "integrity": "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", + "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", "cpu": [ "arm" ], @@ -6178,9 +6136,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.4.tgz", - "integrity": "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", + "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", "cpu": [ "arm64" ], @@ -6195,9 +6153,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.4.tgz", - "integrity": "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", + "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", "cpu": [ "x64" ], @@ -6212,9 +6170,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.4.tgz", - "integrity": "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", + "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", "cpu": [ "arm64" ], @@ -6229,9 +6187,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.4.tgz", - "integrity": "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", + "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", "cpu": [ "x64" ], @@ -6246,9 +6204,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.4.tgz", - "integrity": "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", + "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", "cpu": [ "arm64" ], @@ -6263,9 +6221,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.4.tgz", - "integrity": "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", + "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", "cpu": [ "x64" ], @@ -6280,9 +6238,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.4.tgz", - "integrity": "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", + "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", "cpu": [ "arm" ], @@ -6297,9 +6255,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.4.tgz", - "integrity": "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", + "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", "cpu": [ "arm64" ], @@ -6314,9 +6272,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.4.tgz", - "integrity": "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", + "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", "cpu": [ "ia32" ], @@ -6331,9 +6289,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.4.tgz", - "integrity": "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", + "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", "cpu": [ "loong64" ], @@ -6348,9 +6306,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.4.tgz", - "integrity": "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", + "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", "cpu": [ "mips64el" ], @@ -6365,9 +6323,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.4.tgz", - "integrity": "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", + "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", "cpu": [ "ppc64" ], @@ -6382,9 +6340,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.4.tgz", - "integrity": "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", + "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", "cpu": [ "riscv64" ], @@ -6399,9 +6357,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.4.tgz", - "integrity": "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", + "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", "cpu": [ "s390x" ], @@ -6416,9 +6374,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.4.tgz", - "integrity": "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", + "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", "cpu": [ "x64" ], @@ -6433,9 +6391,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.4.tgz", - "integrity": "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", + "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", "cpu": [ "arm64" ], @@ -6450,9 +6408,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.4.tgz", - "integrity": "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", + "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", "cpu": [ "x64" ], @@ -6467,9 +6425,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.4.tgz", - "integrity": "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", + "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", "cpu": [ "arm64" ], @@ -6484,9 +6442,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.4.tgz", - "integrity": "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", + "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", "cpu": [ "x64" ], @@ -6500,10 +6458,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", + "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.4.tgz", - "integrity": "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", + "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", "cpu": [ "x64" ], @@ -6518,9 +6493,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.4.tgz", - "integrity": "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", + "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", "cpu": [ "arm64" ], @@ -6535,9 +6510,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.4.tgz", - "integrity": "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", + "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", "cpu": [ "ia32" ], @@ -6552,9 +6527,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.4.tgz", - "integrity": "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", + "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", "cpu": [ "x64" ], @@ -7020,18 +6995,28 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@inquirer/ansi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-1.0.2.tgz", + "integrity": "sha512-S8qNSZiYzFd0wAcyG5AXCvUHC5Sr7xpZ9wZ2py9XR88jUz8wooStVx5M6dRzczbBWjic9NP7+rY0Xi7qqK/aMQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@inquirer/checkbox": { - "version": "4.1.8", - "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.1.8.tgz", - "integrity": "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/checkbox/-/checkbox-4.3.2.tgz", + "integrity": "sha512-VXukHf0RR1doGe6Sm4F0Em7SWYLTHSsbGfJdS9Ja2bX5/D5uwVOEjr07cncLROdBvmnvCATYEWlHqYmXv2IlQA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7046,14 +7031,14 @@ } }, "node_modules/@inquirer/confirm": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.6.tgz", - "integrity": "sha512-6ZXYK3M1XmaVBZX6FCfChgtponnL0R6I7k8Nu+kaoNkT828FVZTcca1MqmWQipaW2oNREQl5AaPCUOOCVNdRMw==", + "version": "5.1.14", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-5.1.14.tgz", + "integrity": "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.7", - "@inquirer/type": "^3.0.4" + "@inquirer/core": "^10.1.15", + "@inquirer/type": "^3.0.8" }, "engines": { "node": ">=18" @@ -7068,20 +7053,20 @@ } }, "node_modules/@inquirer/core": { - "version": "10.1.13", - "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.1.13.tgz", - "integrity": "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA==", + "version": "10.3.2", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-10.3.2.tgz", + "integrity": "sha512-43RTuEbfP8MbKzedNqBrlhhNKVwoK//vUFNW3Q3vZ88BLcrs4kYpGg+B2mm5p2K/HfygoCxuKwJJiv8PbGmE0A==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", + "@inquirer/ansi": "^1.0.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", - "yoctocolors-cjs": "^2.1.2" + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7096,15 +7081,15 @@ } }, "node_modules/@inquirer/editor": { - "version": "4.2.13", - "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.13.tgz", - "integrity": "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA==", + "version": "4.2.23", + "resolved": "https://registry.npmjs.org/@inquirer/editor/-/editor-4.2.23.tgz", + "integrity": "sha512-aLSROkEwirotxZ1pBaP8tugXRFCxW94gwrQLxXfrZsKkfjOYC1aRvAZuhpJOb5cu4IBTJdsCigUlf2iCOu4ZDQ==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "external-editor": "^3.1.0" + "@inquirer/core": "^10.3.2", + "@inquirer/external-editor": "^1.0.3", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -7119,15 +7104,15 @@ } }, "node_modules/@inquirer/expand": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.15.tgz", - "integrity": "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/expand/-/expand-4.0.23.tgz", + "integrity": "sha512-nRzdOyFYnpeYTTR2qFwEVmIWypzdAx/sIkCMeTNTcflFOovfqUk+HcFhQQVBftAh9gmGrpFj6QcGEqrDMDOiew==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7141,10 +7126,56 @@ } } }, + "node_modules/@inquirer/external-editor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz", + "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chardet": "^2.1.1", + "iconv-lite": "^0.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@types/node": ">=18" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/@inquirer/external-editor/node_modules/chardet": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz", + "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/external-editor/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/@inquirer/figures": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.12.tgz", - "integrity": "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ==", + "version": "1.0.15", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz", + "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==", "dev": true, "license": "MIT", "engines": { @@ -7152,14 +7183,14 @@ } }, "node_modules/@inquirer/input": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.1.12.tgz", - "integrity": "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@inquirer/input/-/input-4.3.1.tgz", + "integrity": "sha512-kN0pAM4yPrLjJ1XJBjDxyfDduXOuQHrBB8aLDMueuwUGn+vNpF7Gq7TvyVxx8u4SHlFFj4trmj+a2cbpG4Jn1g==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -7174,14 +7205,14 @@ } }, "node_modules/@inquirer/number": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.15.tgz", - "integrity": "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g==", + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/number/-/number-3.0.23.tgz", + "integrity": "sha512-5Smv0OK7K0KUzUfYUXDXQc9jrf8OHo4ktlEayFlelCjwMXz0299Y8OrI+lj7i4gCBY15UObk76q0QtxjzFcFcg==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -7196,15 +7227,15 @@ } }, "node_modules/@inquirer/password": { - "version": "4.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.15.tgz", - "integrity": "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw==", + "version": "4.0.23", + "resolved": "https://registry.npmjs.org/@inquirer/password/-/password-4.0.23.tgz", + "integrity": "sha512-zREJHjhT5vJBMZX/IUbyI9zVtVfOLiTO66MrF/3GFZYZ7T4YILW5MSkEYHceSii/KtRk+4i3RE7E1CUXA2jHcA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10" }, "engines": { "node": ">=18" @@ -7219,22 +7250,22 @@ } }, "node_modules/@inquirer/prompts": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.3.2.tgz", - "integrity": "sha512-G1ytyOoHh5BphmEBxSwALin3n1KGNYB6yImbICcRQdzXfOGbuJ9Jske/Of5Sebk339NSGGNfUshnzK8YWkTPsQ==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/@inquirer/prompts/-/prompts-7.8.2.tgz", + "integrity": "sha512-nqhDw2ZcAUrKNPwhjinJny903bRhI0rQhiDz1LksjeRxqa36i3l75+4iXbOy0rlDpLJGxqtgoPavQjmmyS5UJw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/checkbox": "^4.1.2", - "@inquirer/confirm": "^5.1.6", - "@inquirer/editor": "^4.2.7", - "@inquirer/expand": "^4.0.9", - "@inquirer/input": "^4.1.6", - "@inquirer/number": "^3.0.9", - "@inquirer/password": "^4.0.9", - "@inquirer/rawlist": "^4.0.9", - "@inquirer/search": "^3.0.9", - "@inquirer/select": "^4.0.9" + "@inquirer/checkbox": "^4.2.1", + "@inquirer/confirm": "^5.1.14", + "@inquirer/editor": "^4.2.17", + "@inquirer/expand": "^4.0.17", + "@inquirer/input": "^4.2.1", + "@inquirer/number": "^3.0.17", + "@inquirer/password": "^4.0.17", + "@inquirer/rawlist": "^4.1.5", + "@inquirer/search": "^3.1.0", + "@inquirer/select": "^4.3.1" }, "engines": { "node": ">=18" @@ -7249,15 +7280,15 @@ } }, "node_modules/@inquirer/rawlist": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.3.tgz", - "integrity": "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA==", + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/@inquirer/rawlist/-/rawlist-4.1.11.tgz", + "integrity": "sha512-+LLQB8XGr3I5LZN/GuAHo+GpDJegQwuPARLChlMICNdwW7OwV2izlCSCxN6cqpL0sMXmbKbFcItJgdQq5EBXTw==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7272,16 +7303,16 @@ } }, "node_modules/@inquirer/search": { - "version": "3.0.15", - "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.0.15.tgz", - "integrity": "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@inquirer/search/-/search-3.2.2.tgz", + "integrity": "sha512-p2bvRfENXCZdWF/U2BXvnSI9h+tuA8iNqtUKb9UWbmLYCRQxd8WkvwWvYn+3NgYaNwdUkHytJMGG4MMLucI1kA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7296,17 +7327,17 @@ } }, "node_modules/@inquirer/select": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.2.3.tgz", - "integrity": "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg==", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@inquirer/select/-/select-4.4.2.tgz", + "integrity": "sha512-l4xMuJo55MAe+N7Qr4rX90vypFwCajSakx59qe/tMaC1aEHWLyw68wF4o0A4SLAY4E0nd+Vt+EyskeDIqu1M6w==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/core": "^10.1.13", - "@inquirer/figures": "^1.0.12", - "@inquirer/type": "^3.0.7", - "ansi-escapes": "^4.3.2", - "yoctocolors-cjs": "^2.1.2" + "@inquirer/ansi": "^1.0.2", + "@inquirer/core": "^10.3.2", + "@inquirer/figures": "^1.0.15", + "@inquirer/type": "^3.0.10", + "yoctocolors-cjs": "^2.1.3" }, "engines": { "node": ">=18" @@ -7321,9 +7352,9 @@ } }, "node_modules/@inquirer/type": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.7.tgz", - "integrity": "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA==", + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-3.0.10.tgz", + "integrity": "sha512-BvziSRxfz5Ov8ch0z/n3oijRSEcEsHnhggm4xFZe93DHcUCTlutlq9Ox4SVENAfcRD22UQq7T/atg9Wr3k09eA==", "dev": true, "license": "MIT", "engines": { @@ -8173,42 +8204,20 @@ "license": "MIT" }, "node_modules/@listr2/prompt-adapter-inquirer": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-2.0.18.tgz", - "integrity": "sha512-0hz44rAcrphyXcA8IS7EJ2SCoaBZD2u5goE8S/e+q/DL+dOGpqpcLidVOFeLG3VgML62SXmfRLAhWt0zL1oW4Q==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@listr2/prompt-adapter-inquirer/-/prompt-adapter-inquirer-3.0.1.tgz", + "integrity": "sha512-3XFmGwm3u6ioREG+ynAQB7FoxfajgQnMhIu8wC5eo/Lsih4aKDg0VuIMGaOsYn7hJSJagSeaD4K8yfpkEoDEmA==", "dev": true, "license": "MIT", "dependencies": { - "@inquirer/type": "^1.5.5" + "@inquirer/type": "^3.0.7" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "peerDependencies": { - "@inquirer/prompts": ">= 3 < 8" - } - }, - "node_modules/@listr2/prompt-adapter-inquirer/node_modules/@inquirer/type": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.5.tgz", - "integrity": "sha512-MzICLu4yS7V8AA61sANROZ9vT1H3ooca5dSmI1FjZkzq7o/koMsRfQSzRtFo+F3Ao4Sf1C0bpLKejpKB/+j6MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "mute-stream": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@listr2/prompt-adapter-inquirer/node_modules/mute-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", - "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@inquirer/prompts": ">= 3 < 8", + "listr2": "9.0.1" } }, "node_modules/@lit-labs/react": { @@ -8245,9 +8254,9 @@ } }, "node_modules/@lmdb/lmdb-darwin-arm64": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.2.6.tgz", - "integrity": "sha512-yF/ih9EJJZc72psFQbwnn8mExIWfTnzWJg+N02hnpXtDPETYLmQswIMBn7+V88lfCaFrMozJsUvcEQIkEPU0Gg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-arm64/-/lmdb-darwin-arm64-3.4.2.tgz", + "integrity": "sha512-NK80WwDoODyPaSazKbzd3NEJ3ygePrkERilZshxBViBARNz21rmediktGHExoj9n5t9+ChlgLlxecdFKLCuCKg==", "cpu": [ "arm64" ], @@ -8259,9 +8268,9 @@ ] }, "node_modules/@lmdb/lmdb-darwin-x64": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.2.6.tgz", - "integrity": "sha512-5BbCumsFLbCi586Bb1lTWQFkekdQUw8/t8cy++Uq251cl3hbDIGEwD9HAwh8H6IS2F6QA9KdKmO136LmipRNkg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-darwin-x64/-/lmdb-darwin-x64-3.4.2.tgz", + "integrity": "sha512-zevaowQNmrp3U7Fz1s9pls5aIgpKRsKb3dZWDINtLiozh3jZI9fBrI19lYYBxqdyiIyNdlyiidPnwPShj4aK+w==", "cpu": [ "x64" ], @@ -8273,9 +8282,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.2.6.tgz", - "integrity": "sha512-+6XgLpMb7HBoWxXj+bLbiiB4s0mRRcDPElnRS3LpWRzdYSe+gFk5MT/4RrVNqd2MESUDmb53NUXw1+BP69bjiQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm/-/lmdb-linux-arm-3.4.2.tgz", + "integrity": "sha512-OmHCULY17rkx/RoCoXlzU7LyR8xqrksgdYWwtYa14l/sseezZ8seKWXcogHcjulBddER5NnEFV4L/Jtr2nyxeg==", "cpu": [ "arm" ], @@ -8287,9 +8296,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-arm64": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.2.6.tgz", - "integrity": "sha512-l5VmJamJ3nyMmeD1ANBQCQqy7do1ESaJQfKPSm2IG9/ADZryptTyCj8N6QaYgIWewqNUrcbdMkJajRQAt5Qjfg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-arm64/-/lmdb-linux-arm64-3.4.2.tgz", + "integrity": "sha512-ZBEfbNZdkneebvZs98Lq30jMY8V9IJzckVeigGivV7nTHJc+89Ctomp1kAIWKlwIG0ovCDrFI448GzFPORANYg==", "cpu": [ "arm64" ], @@ -8301,9 +8310,9 @@ ] }, "node_modules/@lmdb/lmdb-linux-x64": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.2.6.tgz", - "integrity": "sha512-nDYT8qN9si5+onHYYaI4DiauDMx24OAiuZAUsEqrDy+ja/3EbpXPX/VAkMV8AEaQhy3xc4dRC+KcYIvOFefJ4Q==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-linux-x64/-/lmdb-linux-x64-3.4.2.tgz", + "integrity": "sha512-vL9nM17C77lohPYE4YaAQvfZCSVJSryE4fXdi8M7uWPBnU+9DJabgKVAeyDb84ZM2vcFseoBE4/AagVtJeRE7g==", "cpu": [ "x64" ], @@ -8314,10 +8323,24 @@ "linux" ] }, + "node_modules/@lmdb/lmdb-win32-arm64": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-arm64/-/lmdb-win32-arm64-3.4.2.tgz", + "integrity": "sha512-SXWjdBfNDze4ZPeLtYIzsIeDJDJ/SdsA0pEXcUBayUIMO0FQBHfVZZyHXQjjHr4cvOAzANBgIiqaXRwfMhzmLw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@lmdb/lmdb-win32-x64": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.2.6.tgz", - "integrity": "sha512-XlqVtILonQnG+9fH2N3Aytria7P/1fwDgDhl29rde96uH2sLB8CHORIf2PfuLVzFQJ7Uqp8py9AYwr3ZUCFfWg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@lmdb/lmdb-win32-x64/-/lmdb-win32-x64-3.4.2.tgz", + "integrity": "sha512-IY+r3bxKW6Q6sIPiMC0L533DEfRJSXibjSI3Ft/w9Q8KQBNqEIvUFXt+09wV8S5BRk0a8uSF19YWxuRwEfI90g==", "cpu": [ "x64" ], @@ -8384,9 +8407,9 @@ } }, "node_modules/@mdx-js/react": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.0.tgz", - "integrity": "sha512-QjHtSaoameoalGnKDT3FoIl4+9RwyTmo9ZJGBdLOks/YOiWHoRDI3PUwEzOE7kEmGcV3AFcp9K6dYu9rEuKLAQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@mdx-js/react/-/react-3.1.1.tgz", + "integrity": "sha512-f++rKLQgUVYDAtECQ6fn/is15GkEH9+nZPM3MS0RcxVqoTfawHvDlSCH7JbMhAM6uJ32v3eXLvLmLvjGu7PTQw==", "dev": true, "license": "MIT", "dependencies": { @@ -8461,9 +8484,9 @@ } }, "node_modules/@modelcontextprotocol/sdk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.13.1.tgz", - "integrity": "sha512-8q6+9aF0yA39/qWT/uaIj6zTpC+Qu07DnN/lb9mjoquCJsAh6l3HyYqc9O3t2j7GilseOQOQimLg7W3By6jqvg==", + "version": "1.17.3", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.17.3.tgz", + "integrity": "sha512-JPwUKWSsbzx+DLFznf/QZ32Qa+ptfbUlHhRLrBQBAFu9iI1iYvizM4p+zhhRDceSsPutXp4z+R/HPVphlIiclg==", "license": "MIT", "dependencies": { "ajv": "^6.12.6", @@ -8471,6 +8494,7 @@ "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", @@ -8627,9 +8651,9 @@ } }, "node_modules/@napi-rs/nice": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.0.1.tgz", - "integrity": "sha512-zM0mVWSXE0a0h9aKACLwKmD6nHcRiKrPpCfvaKqG1CqDEyjEawId0ocXxVzPMCAm6kkWr2P025msfxXEnt8UGQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice/-/nice-1.1.1.tgz", + "integrity": "sha512-xJIPs+bYuc9ASBl+cvGsKbGrJmS6fAKaSZCnT0lhahT5rhA2VVy9/EcIgd2JhtEuFOJNx7UHNn/qiTPTY4nrQw==", "dev": true, "license": "MIT", "optional": true, @@ -8641,28 +8665,29 @@ "url": "https://github.com/sponsors/Brooooooklyn" }, "optionalDependencies": { - "@napi-rs/nice-android-arm-eabi": "1.0.1", - "@napi-rs/nice-android-arm64": "1.0.1", - "@napi-rs/nice-darwin-arm64": "1.0.1", - "@napi-rs/nice-darwin-x64": "1.0.1", - "@napi-rs/nice-freebsd-x64": "1.0.1", - "@napi-rs/nice-linux-arm-gnueabihf": "1.0.1", - "@napi-rs/nice-linux-arm64-gnu": "1.0.1", - "@napi-rs/nice-linux-arm64-musl": "1.0.1", - "@napi-rs/nice-linux-ppc64-gnu": "1.0.1", - "@napi-rs/nice-linux-riscv64-gnu": "1.0.1", - "@napi-rs/nice-linux-s390x-gnu": "1.0.1", - "@napi-rs/nice-linux-x64-gnu": "1.0.1", - "@napi-rs/nice-linux-x64-musl": "1.0.1", - "@napi-rs/nice-win32-arm64-msvc": "1.0.1", - "@napi-rs/nice-win32-ia32-msvc": "1.0.1", - "@napi-rs/nice-win32-x64-msvc": "1.0.1" + "@napi-rs/nice-android-arm-eabi": "1.1.1", + "@napi-rs/nice-android-arm64": "1.1.1", + "@napi-rs/nice-darwin-arm64": "1.1.1", + "@napi-rs/nice-darwin-x64": "1.1.1", + "@napi-rs/nice-freebsd-x64": "1.1.1", + "@napi-rs/nice-linux-arm-gnueabihf": "1.1.1", + "@napi-rs/nice-linux-arm64-gnu": "1.1.1", + "@napi-rs/nice-linux-arm64-musl": "1.1.1", + "@napi-rs/nice-linux-ppc64-gnu": "1.1.1", + "@napi-rs/nice-linux-riscv64-gnu": "1.1.1", + "@napi-rs/nice-linux-s390x-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-gnu": "1.1.1", + "@napi-rs/nice-linux-x64-musl": "1.1.1", + "@napi-rs/nice-openharmony-arm64": "1.1.1", + "@napi-rs/nice-win32-arm64-msvc": "1.1.1", + "@napi-rs/nice-win32-ia32-msvc": "1.1.1", + "@napi-rs/nice-win32-x64-msvc": "1.1.1" } }, "node_modules/@napi-rs/nice-android-arm-eabi": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.0.1.tgz", - "integrity": "sha512-5qpvOu5IGwDo7MEKVqqyAxF90I6aLj4n07OzpARdgDRfz8UbBztTByBp0RC59r3J1Ij8uzYi6jI7r5Lws7nn6w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm-eabi/-/nice-android-arm-eabi-1.1.1.tgz", + "integrity": "sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==", "cpu": [ "arm" ], @@ -8677,9 +8702,9 @@ } }, "node_modules/@napi-rs/nice-android-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.0.1.tgz", - "integrity": "sha512-GqvXL0P8fZ+mQqG1g0o4AO9hJjQaeYG84FRfZaYjyJtZZZcMjXW5TwkL8Y8UApheJgyE13TQ4YNUssQaTgTyvA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-android-arm64/-/nice-android-arm64-1.1.1.tgz", + "integrity": "sha512-blG0i7dXgbInN5urONoUCNf+DUEAavRffrO7fZSeoRMJc5qD+BJeNcpr54msPF6qfDD6kzs9AQJogZvT2KD5nw==", "cpu": [ "arm64" ], @@ -8694,9 +8719,9 @@ } }, "node_modules/@napi-rs/nice-darwin-arm64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.0.1.tgz", - "integrity": "sha512-91k3HEqUl2fsrz/sKkuEkscj6EAj3/eZNCLqzD2AA0TtVbkQi8nqxZCZDMkfklULmxLkMxuUdKe7RvG/T6s2AA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-arm64/-/nice-darwin-arm64-1.1.1.tgz", + "integrity": "sha512-s/E7w45NaLqTGuOjC2p96pct4jRfo61xb9bU1unM/MJ/RFkKlJyJDx7OJI/O0ll/hrfpqKopuAFDV8yo0hfT7A==", "cpu": [ "arm64" ], @@ -8711,9 +8736,9 @@ } }, "node_modules/@napi-rs/nice-darwin-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.0.1.tgz", - "integrity": "sha512-jXnMleYSIR/+TAN/p5u+NkCA7yidgswx5ftqzXdD5wgy/hNR92oerTXHc0jrlBisbd7DpzoaGY4cFD7Sm5GlgQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-darwin-x64/-/nice-darwin-x64-1.1.1.tgz", + "integrity": "sha512-dGoEBnVpsdcC+oHHmW1LRK5eiyzLwdgNQq3BmZIav+9/5WTZwBYX7r5ZkQC07Nxd3KHOCkgbHSh4wPkH1N1LiQ==", "cpu": [ "x64" ], @@ -8728,9 +8753,9 @@ } }, "node_modules/@napi-rs/nice-freebsd-x64": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.0.1.tgz", - "integrity": "sha512-j+iJ/ezONXRQsVIB/FJfwjeQXX7A2tf3gEXs4WUGFrJjpe/z2KB7sOv6zpkm08PofF36C9S7wTNuzHZ/Iiccfw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-freebsd-x64/-/nice-freebsd-x64-1.1.1.tgz", + "integrity": "sha512-kHv4kEHAylMYmlNwcQcDtXjklYp4FCf0b05E+0h6nDHsZ+F0bDe04U/tXNOqrx5CmIAth4vwfkjjUmp4c4JktQ==", "cpu": [ "x64" ], @@ -8745,9 +8770,9 @@ } }, "node_modules/@napi-rs/nice-linux-arm-gnueabihf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.0.1.tgz", - "integrity": "sha512-G8RgJ8FYXYkkSGQwywAUh84m946UTn6l03/vmEXBYNJxQJcD+I3B3k5jmjFG/OPiU8DfvxutOP8bi+F89MCV7Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm-gnueabihf/-/nice-linux-arm-gnueabihf-1.1.1.tgz", + "integrity": "sha512-E1t7K0efyKXZDoZg1LzCOLxgolxV58HCkaEkEvIYQx12ht2pa8hoBo+4OB3qh7e+QiBlp1SRf+voWUZFxyhyqg==", "cpu": [ "arm" ], @@ -8762,9 +8787,9 @@ } }, "node_modules/@napi-rs/nice-linux-arm64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.0.1.tgz", - "integrity": "sha512-IMDak59/W5JSab1oZvmNbrms3mHqcreaCeClUjwlwDr0m3BoR09ZiN8cKFBzuSlXgRdZ4PNqCYNeGQv7YMTjuA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-gnu/-/nice-linux-arm64-gnu-1.1.1.tgz", + "integrity": "sha512-CIKLA12DTIZlmTaaKhQP88R3Xao+gyJxNWEn04wZwC2wmRapNnxCUZkVwggInMJvtVElA+D4ZzOU5sX4jV+SmQ==", "cpu": [ "arm64" ], @@ -8779,9 +8804,9 @@ } }, "node_modules/@napi-rs/nice-linux-arm64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.0.1.tgz", - "integrity": "sha512-wG8fa2VKuWM4CfjOjjRX9YLIbysSVV1S3Kgm2Fnc67ap/soHBeYZa6AGMeR5BJAylYRjnoVOzV19Cmkco3QEPw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-arm64-musl/-/nice-linux-arm64-musl-1.1.1.tgz", + "integrity": "sha512-+2Rzdb3nTIYZ0YJF43qf2twhqOCkiSrHx2Pg6DJaCPYhhaxbLcdlV8hCRMHghQ+EtZQWGNcS2xF4KxBhSGeutg==", "cpu": [ "arm64" ], @@ -8796,9 +8821,9 @@ } }, "node_modules/@napi-rs/nice-linux-ppc64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.0.1.tgz", - "integrity": "sha512-lxQ9WrBf0IlNTCA9oS2jg/iAjQyTI6JHzABV664LLrLA/SIdD+I1i3Mjf7TsnoUbgopBcCuDztVLfJ0q9ubf6Q==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-ppc64-gnu/-/nice-linux-ppc64-gnu-1.1.1.tgz", + "integrity": "sha512-4FS8oc0GeHpwvv4tKciKkw3Y4jKsL7FRhaOeiPei0X9T4Jd619wHNe4xCLmN2EMgZoeGg+Q7GY7BsvwKpL22Tg==", "cpu": [ "ppc64" ], @@ -8813,9 +8838,9 @@ } }, "node_modules/@napi-rs/nice-linux-riscv64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.0.1.tgz", - "integrity": "sha512-3xs69dO8WSWBb13KBVex+yvxmUeEsdWexxibqskzoKaWx9AIqkMbWmE2npkazJoopPKX2ULKd8Fm9veEn0g4Ig==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-riscv64-gnu/-/nice-linux-riscv64-gnu-1.1.1.tgz", + "integrity": "sha512-HU0nw9uD4FO/oGCCk409tCi5IzIZpH2agE6nN4fqpwVlCn5BOq0MS1dXGjXaG17JaAvrlpV5ZeyZwSon10XOXw==", "cpu": [ "riscv64" ], @@ -8830,9 +8855,9 @@ } }, "node_modules/@napi-rs/nice-linux-s390x-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.0.1.tgz", - "integrity": "sha512-lMFI3i9rlW7hgToyAzTaEybQYGbQHDrpRkg+1gJWEpH0PLAQoZ8jiY0IzakLfNWnVda1eTYYlxxFYzW8Rqczkg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-s390x-gnu/-/nice-linux-s390x-gnu-1.1.1.tgz", + "integrity": "sha512-2YqKJWWl24EwrX0DzCQgPLKQBxYDdBxOHot1KWEq7aY2uYeX+Uvtv4I8xFVVygJDgf6/92h9N3Y43WPx8+PAgQ==", "cpu": [ "s390x" ], @@ -8847,9 +8872,9 @@ } }, "node_modules/@napi-rs/nice-linux-x64-gnu": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.0.1.tgz", - "integrity": "sha512-XQAJs7DRN2GpLN6Fb+ZdGFeYZDdGl2Fn3TmFlqEL5JorgWKrQGRUrpGKbgZ25UeZPILuTKJ+OowG2avN8mThBA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-gnu/-/nice-linux-x64-gnu-1.1.1.tgz", + "integrity": "sha512-/gaNz3R92t+dcrfCw/96pDopcmec7oCcAQ3l/M+Zxr82KT4DljD37CpgrnXV+pJC263JkW572pdbP3hP+KjcIg==", "cpu": [ "x64" ], @@ -8864,9 +8889,9 @@ } }, "node_modules/@napi-rs/nice-linux-x64-musl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.0.1.tgz", - "integrity": "sha512-/rodHpRSgiI9o1faq9SZOp/o2QkKQg7T+DK0R5AkbnI/YxvAIEHf2cngjYzLMQSQgUhxym+LFr+UGZx4vK4QdQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-linux-x64-musl/-/nice-linux-x64-musl-1.1.1.tgz", + "integrity": "sha512-xScCGnyj/oppsNPMnevsBe3pvNaoK7FGvMjT35riz9YdhB2WtTG47ZlbxtOLpjeO9SqqQ2J2igCmz6IJOD5JYw==", "cpu": [ "x64" ], @@ -8880,10 +8905,27 @@ "node": ">= 10" } }, + "node_modules/@napi-rs/nice-openharmony-arm64": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-openharmony-arm64/-/nice-openharmony-arm64-1.1.1.tgz", + "integrity": "sha512-6uJPRVwVCLDeoOaNyeiW0gp2kFIM4r7PL2MczdZQHkFi9gVlgm+Vn+V6nTWRcu856mJ2WjYJiumEajfSm7arPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">= 10" + } + }, "node_modules/@napi-rs/nice-win32-arm64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.0.1.tgz", - "integrity": "sha512-rEcz9vZymaCB3OqEXoHnp9YViLct8ugF+6uO5McifTedjq4QMQs3DHz35xBEGhH3gJWEsXMUbzazkz5KNM5YUg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-arm64-msvc/-/nice-win32-arm64-msvc-1.1.1.tgz", + "integrity": "sha512-uoTb4eAvM5B2aj/z8j+Nv8OttPf2m+HVx3UjA5jcFxASvNhQriyCQF1OB1lHL43ZhW+VwZlgvjmP5qF3+59atA==", "cpu": [ "arm64" ], @@ -8898,9 +8940,9 @@ } }, "node_modules/@napi-rs/nice-win32-ia32-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.0.1.tgz", - "integrity": "sha512-t7eBAyPUrWL8su3gDxw9xxxqNwZzAqKo0Szv3IjVQd1GpXXVkb6vBBQUuxfIYaXMzZLwlxRQ7uzM2vdUE9ULGw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-ia32-msvc/-/nice-win32-ia32-msvc-1.1.1.tgz", + "integrity": "sha512-CNQqlQT9MwuCsg1Vd/oKXiuH+TcsSPJmlAFc5frFyX/KkOh0UpBLEj7aoY656d5UKZQMQFP7vJNa1DNUNORvug==", "cpu": [ "ia32" ], @@ -8915,9 +8957,9 @@ } }, "node_modules/@napi-rs/nice-win32-x64-msvc": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.0.1.tgz", - "integrity": "sha512-JlF+uDcatt3St2ntBG8H02F1mM45i5SF9W+bIKiReVE6wiy3o16oBP/yxt+RZ+N6LbCImJXJ6bXNO2kn9AXicg==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@napi-rs/nice-win32-x64-msvc/-/nice-win32-x64-msvc-1.1.1.tgz", + "integrity": "sha512-vB+4G/jBQCAh0jelMTY3+kgFy00Hlx2f2/1zjMoH821IbplbWZOkLiTYXQkygNTzQJTq5cvwBDgn2ppHD+bglQ==", "cpu": [ "x64" ], @@ -8943,37 +8985,36 @@ } }, "node_modules/@ng-select/ng-select": { - "version": "14.9.0", - "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-14.9.0.tgz", - "integrity": "sha512-f/E3EaSVwdKmwvZL43nS961bGaXR90F0Gtb8vA+ub8Hfwqjr1NTI6X7+yu5iMkqfy5ZW5cJdoGvo+kv8zcAkjQ==", + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/@ng-select/ng-select/-/ng-select-20.7.0.tgz", + "integrity": "sha512-tOAhUnb4LFjTnn9gi6e6tSrUDi1GqCzjwnubBIxxa2XFHXqGOjYlqIMkPcSRByF4G9olzdZM38nLUtZOO3YKqg==", "license": "MIT", "dependencies": { - "tslib": "^2.3.1" + "tslib": "^2.8.1" }, "engines": { - "node": ">= 18", - "npm": ">= 8" + "node": "^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "^19.0.0", - "@angular/core": "^19.0.0", - "@angular/forms": "^19.0.0" + "@angular/common": "^20.0.0", + "@angular/core": "^20.0.0", + "@angular/forms": "^20.0.0" } }, "node_modules/@ngtools/webpack": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-19.2.14.tgz", - "integrity": "sha512-PqrY+eeSUoF6JC6NCEQRPE/0Y2umSllD/fsDE6pnQrvGfztBpj0Jt1WMhgEI8BBcl4S7QW0LhPynkBmnCvTUmw==", + "version": "20.3.11", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-20.3.11.tgz", + "integrity": "sha512-c2/66tObP9YevCt7jyhwiGifS8ldfce6vYQ63Wwj8tlXSSutHk8+3VEQmbW3wW1JH7+0aNf3kF+pA97EbGj6QA==", "dev": true, "license": "MIT", "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" }, "peerDependencies": { - "@angular/compiler-cli": "^19.0.0 || ^19.2.0-next.0", - "typescript": ">=5.5 <5.9", + "@angular/compiler-cli": "^20.0.0", + "typescript": ">=5.8 <6.0", "webpack": "^5.54.0" } }, @@ -9253,9 +9294,9 @@ } }, "node_modules/@npmcli/package-json/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -9353,9 +9394,9 @@ } }, "node_modules/@npmcli/promise-spawn": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz", - "integrity": "sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.3.tgz", + "integrity": "sha512-Yb00SWaL4F8w+K8YGhQ55+xE4RUNdMHV43WZGsiTM92gS+lC0mGsn7I4hLug7pbao035S6bj3Y3w0cUNGLfmkg==", "dev": true, "license": "ISC", "dependencies": { @@ -11670,9 +11711,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.8.tgz", - "integrity": "sha512-q217OSE8DTp8AFHuNHXo0Y86e1wtlfVrXiAlwkIvGRQv9zbc6mE3sjIVfwI8sYUyNxwOg0j/Vm1RKM04JcWLJw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz", + "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==", "cpu": [ "arm" ], @@ -11684,9 +11725,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.34.8.tgz", - "integrity": "sha512-Gigjz7mNWaOL9wCggvoK3jEIUUbGul656opstjaUSGC3eT0BM7PofdAJaBfPFWWkXNVAXbaQtC99OCg4sJv70Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz", + "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==", "cpu": [ "arm64" ], @@ -11698,9 +11739,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.34.8.tgz", - "integrity": "sha512-02rVdZ5tgdUNRxIUrFdcMBZQoaPMrxtwSb+/hOfBdqkatYHR3lZ2A2EGyHq2sGOd0Owk80oV3snlDASC24He3Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz", + "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==", "cpu": [ "arm64" ], @@ -11712,9 +11753,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.34.8.tgz", - "integrity": "sha512-qIP/elwR/tq/dYRx3lgwK31jkZvMiD6qUtOycLhTzCvrjbZ3LjQnEM9rNhSGpbLXVJYQ3rq39A6Re0h9tU2ynw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz", + "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==", "cpu": [ "x64" ], @@ -11726,9 +11767,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.34.8.tgz", - "integrity": "sha512-IQNVXL9iY6NniYbTaOKdrlVP3XIqazBgJOVkddzJlqnCpRi/yAeSOa8PLcECFSQochzqApIOE1GHNu3pCz+BDA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz", + "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==", "cpu": [ "arm64" ], @@ -11740,9 +11781,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.34.8.tgz", - "integrity": "sha512-TYXcHghgnCqYFiE3FT5QwXtOZqDj5GmaFNTNt3jNC+vh22dc/ukG2cG+pi75QO4kACohZzidsq7yKTKwq/Jq7Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz", + "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==", "cpu": [ "x64" ], @@ -11754,9 +11795,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.34.8.tgz", - "integrity": "sha512-A4iphFGNkWRd+5m3VIGuqHnG3MVnqKe7Al57u9mwgbyZ2/xF9Jio72MaY7xxh+Y87VAHmGQr73qoKL9HPbXj1g==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz", + "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==", "cpu": [ "arm" ], @@ -11768,9 +11809,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.34.8.tgz", - "integrity": "sha512-S0lqKLfTm5u+QTxlFiAnb2J/2dgQqRy/XvziPtDd1rKZFXHTyYLoVL58M/XFwDI01AQCDIevGLbQrMAtdyanpA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz", + "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==", "cpu": [ "arm" ], @@ -11782,9 +11823,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.34.8.tgz", - "integrity": "sha512-jpz9YOuPiSkL4G4pqKrus0pn9aYwpImGkosRKwNi+sJSkz+WU3anZe6hi73StLOQdfXYXC7hUfsQlTnjMd3s1A==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz", + "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==", "cpu": [ "arm64" ], @@ -11796,9 +11837,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.34.8.tgz", - "integrity": "sha512-KdSfaROOUJXgTVxJNAZ3KwkRc5nggDk+06P6lgi1HLv1hskgvxHUKZ4xtwHkVYJ1Rep4GNo+uEfycCRRxht7+Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz", + "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==", "cpu": [ "arm64" ], @@ -11809,10 +11850,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.34.8.tgz", - "integrity": "sha512-NyF4gcxwkMFRjgXBM6g2lkT58OWztZvw5KkV2K0qqSnUEqCVcqdh2jN4gQrTn/YUpAcNKyFHfoOZEer9nwo6uQ==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz", + "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==", "cpu": [ "loong64" ], @@ -11823,10 +11864,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.34.8.tgz", - "integrity": "sha512-LMJc999GkhGvktHU85zNTDImZVUCJ1z/MbAJTnviiWmmjyckP5aQsHtcujMjpNdMZPT2rQEDBlJfubhs3jsMfw==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz", + "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==", "cpu": [ "ppc64" ], @@ -11838,9 +11879,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.34.8.tgz", - "integrity": "sha512-xAQCAHPj8nJq1PI3z8CIZzXuXCstquz7cIOL73HHdXiRcKk8Ywwqtx2wrIy23EcTn4aZ2fLJNBB8d0tQENPCmw==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz", + "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==", "cpu": [ "riscv64" ], @@ -11852,9 +11893,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz", - "integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz", + "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==", "cpu": [ "riscv64" ], @@ -11863,13 +11904,12 @@ "optional": true, "os": [ "linux" - ], - "peer": true + ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.34.8.tgz", - "integrity": "sha512-DdePVk1NDEuc3fOe3dPPTb+rjMtuFw89gw6gVWxQFAuEqqSdDKnrwzZHrUYdac7A7dXl9Q2Vflxpme15gUWQFA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz", + "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==", "cpu": [ "s390x" ], @@ -11881,9 +11921,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.34.8.tgz", - "integrity": "sha512-8y7ED8gjxITUltTUEJLQdgpbPh1sUQ0kMTmufRF/Ns5tI9TNMNlhWtmPKKHCU0SilX+3MJkZ0zERYYGIVBYHIA==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz", + "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==", "cpu": [ "x64" ], @@ -11895,9 +11935,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.34.8.tgz", - "integrity": "sha512-SCXcP0ZpGFIe7Ge+McxY5zKxiEI5ra+GT3QRxL0pMMtxPfpyLAKleZODi1zdRHkz5/BhueUrYtYVgubqe9JBNQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz", + "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==", "cpu": [ "x64" ], @@ -11908,10 +11948,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz", + "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.34.8.tgz", - "integrity": "sha512-YHYsgzZgFJzTRbth4h7Or0m5O74Yda+hLin0irAIobkLQFRQd1qWmnoVfwmKm9TXIZVAD0nZ+GEb2ICicLyCnQ==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz", + "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==", "cpu": [ "arm64" ], @@ -11923,9 +11977,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.34.8.tgz", - "integrity": "sha512-r3NRQrXkHr4uWy5TOjTpTYojR9XmF0j/RYgKCef+Ag46FWUTltm5ziticv8LdNsDMehjJ543x/+TJAek/xBA2w==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz", + "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==", "cpu": [ "ia32" ], @@ -11936,10 +11990,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz", + "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.34.8.tgz", - "integrity": "sha512-U0FaE5O1BCpZSeE6gBl3c5ObhePQSfk9vDRToMmTkbhCOgW4jqvtS5LGyQ76L1fH8sM0keRp4uDTsbjiUyjk0g==", + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz", + "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==", "cpu": [ "x64" ], @@ -11958,65 +12026,18 @@ "license": "MIT" }, "node_modules/@schematics/angular": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-19.2.14.tgz", - "integrity": "sha512-p/jvMwth67g7tOrziTx+yWRagIPtjx21TF2uU2Pv5bqTY+JjRTczJs3yHPmVpzJN+ptmw47K4/NeLJmVUGuBgA==", + "version": "20.3.12", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-20.3.12.tgz", + "integrity": "sha512-ikl+nkWUab/Z4eSkBHgq9FLIUH8qh4OcYKeBQ0fyWqIUFHyjjK0JOfwmH1g/3zAmuUMtkthHCehAtyKzCTQjVA==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": "19.2.14", - "@angular-devkit/schematics": "19.2.14", + "@angular-devkit/core": "20.3.12", + "@angular-devkit/schematics": "20.3.12", "jsonc-parser": "3.3.1" }, "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - } - }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-19.2.14.tgz", - "integrity": "sha512-aaPEnRNIBoYT4XrrYcZlHadX8vFDTUR+4wUgcmr0cNDLeWzWtoPFeVq8TQD6kFDeqovSx/UVEblGgg/28WvHyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "8.17.1", - "ajv-formats": "3.0.1", - "jsonc-parser": "3.3.1", - "picomatch": "4.0.2", - "rxjs": "7.8.1", - "source-map": "0.7.4" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", - "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", - "yarn": ">= 1.13.0" - }, - "peerDependencies": { - "chokidar": "^4.0.0" - }, - "peerDependenciesMeta": { - "chokidar": { - "optional": true - } - } - }, - "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-19.2.14.tgz", - "integrity": "sha512-s89/MWXHy8+GP/cRfFbSECIG3FQQQwNVv44OOmghPVgKQgQ+EoE/zygL2hqKYTUPoPaS/IhNXdXjSE5pS9yLeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@angular-devkit/core": "19.2.14", - "jsonc-parser": "3.3.1", - "magic-string": "0.30.17", - "ora": "5.4.1", - "rxjs": "7.8.1" - }, - "engines": { - "node": "^18.19.1 || ^20.11.1 || >=22.0.0", + "node": "^20.19.0 || ^22.12.0 || >=24.0.0", "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", "yarn": ">= 1.13.0" } @@ -12157,9 +12178,9 @@ } }, "node_modules/@sigstore/sign/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -12271,9 +12292,9 @@ } }, "node_modules/@sigstore/sign/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -12283,26 +12304,10 @@ "node": ">= 18" } }, - "node_modules/@sigstore/sign/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@sigstore/sign/node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", "engines": { @@ -12353,17 +12358,16 @@ } }, "node_modules/@sigstore/sign/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -12454,19 +12458,6 @@ "url": "https://github.com/sindresorhus/is?sponsor=1" } }, - "node_modules/@sindresorhus/merge-streams": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", - "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", @@ -12494,15 +12485,13 @@ "license": "MIT" }, "node_modules/@storybook/addon-a11y": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-8.6.12.tgz", - "integrity": "sha512-H28zHiL8uuv29XsVNf9VjNWsCeht/l66GPYHT7aom1jh+f3fS9+sutrCGEBC/T7cnRpy8ZyuHCtihUqS+RI4pg==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/addon-a11y/-/addon-a11y-9.1.16.tgz", + "integrity": "sha512-DpUqAMOgkC/K/DgB9osqbBYmiWWj7V444HeYLHcx7GdPtg2guq1jAcalsOnQeU3wXgUE+wNuyMm6qZKm7of11g==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/addon-highlight": "8.6.12", "@storybook/global": "^5.0.0", - "@storybook/test": "8.6.12", "axe-core": "^4.2.0" }, "funding": { @@ -12510,106 +12499,26 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-actions": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-8.6.12.tgz", - "integrity": "sha512-B5kfiRvi35oJ0NIo53CGH66H471A3XTzrfaa6SxXEJsgxxSeKScG5YeXcCvLiZfvANRQ7QDsmzPUgg0o3hdMXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "@types/uuid": "^9.0.1", - "dequal": "^2.0.2", - "polished": "^4.2.2", - "uuid": "^9.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-actions/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "dev": true, - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/@storybook/addon-backgrounds": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-backgrounds/-/addon-backgrounds-8.6.12.tgz", - "integrity": "sha512-lmIAma9BiiCTbJ8YfdZkXjpnAIrOUcgboLkt1f6XJ78vNEMnLNzD9gnh7Tssz1qrqvm34v9daDjIb+ggdiKp3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "memoizerific": "^1.11.3", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-controls": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-controls/-/addon-controls-8.6.12.tgz", - "integrity": "sha512-9VSRPJWQVb9wLp21uvpxDGNctYptyUX0gbvxIWOHMH3R2DslSoq41lsC/oQ4l4zSHVdL+nq8sCTkhBxIsjKqdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "dequal": "^2.0.2", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@storybook/addon-designs": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-8.2.1.tgz", - "integrity": "sha512-orwihs1D5alhh4Qu3BSJKbSgQOdSagvRX/25m5fYZQAaqVErBY0lRR4vCAU/G/STkcdv+MHwIQ5U+0kX5Tm2+w==", + "version": "9.0.0-next.3", + "resolved": "https://registry.npmjs.org/@storybook/addon-designs/-/addon-designs-9.0.0-next.3.tgz", + "integrity": "sha512-xkpuet68AOYVmO/wGIkhovTpGlDamtdAiPxnm6TaJEBs9sQi5L8KNeLFwcEteloHzSyOXsiKw8Swtu0i600PEQ==", "dev": true, "license": "MIT", "dependencies": { "@figspec/react": "^1.0.0" }, "peerDependencies": { - "@storybook/blocks": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0", - "@storybook/components": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0", - "@storybook/theming": "^8.0.0 || ^8.1.0-0 || ^8.2.0-0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" + "@storybook/addon-docs": "^0.0.0-0 || ^9.0.0 || ^9.0.0-0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "storybook": "^0.0.0-0 || ^9.0.0 || ^9.0.0-0" }, "peerDependenciesMeta": { - "@storybook/blocks": { - "optional": true - }, - "@storybook/components": { - "optional": true - }, - "@storybook/theming": { + "@storybook/addon-docs": { "optional": true }, "react": { @@ -12621,16 +12530,16 @@ } }, "node_modules/@storybook/addon-docs": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-8.6.12.tgz", - "integrity": "sha512-kEezQjAf/p3SpDzLABgg4fbT48B6dkT2LiZCKTRmCrJVtuReaAr4R9MMM6Jsph6XjbIj/SvOWf3CMeOPXOs9sg==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-9.1.16.tgz", + "integrity": "sha512-JfaUD6fC7ySLg5duRdaWZ0FUUXrgUvqbZe/agCbSyOaIHOtJdhGaPjOC3vuXTAcV8/8/wWmbu0iXFMD08iKvdw==", "dev": true, "license": "MIT", "dependencies": { "@mdx-js/react": "^3.0.0", - "@storybook/blocks": "8.6.12", - "@storybook/csf-plugin": "8.6.12", - "@storybook/react-dom-shim": "8.6.12", + "@storybook/csf-plugin": "9.1.16", + "@storybook/icons": "^1.4.0", + "@storybook/react-dom-shim": "9.1.16", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "ts-dedent": "^2.0.0" @@ -12640,39 +12549,13 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, - "node_modules/@storybook/addon-essentials": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-essentials/-/addon-essentials-8.6.12.tgz", - "integrity": "sha512-Y/7e8KFlttaNfv7q2zoHMPdX6hPXHdsuQMAjYl5NG9HOAJREu4XBy4KZpbcozRe4ApZ78rYsN/MO1EuA+bNMIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/addon-actions": "8.6.12", - "@storybook/addon-backgrounds": "8.6.12", - "@storybook/addon-controls": "8.6.12", - "@storybook/addon-docs": "8.6.12", - "@storybook/addon-highlight": "8.6.12", - "@storybook/addon-measure": "8.6.12", - "@storybook/addon-outline": "8.6.12", - "@storybook/addon-toolbars": "8.6.12", - "@storybook/addon-viewport": "8.6.12", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-highlight": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-highlight/-/addon-highlight-8.6.12.tgz", - "integrity": "sha512-9FITVxdoycZ+eXuAZL9ElWyML/0fPPn9UgnnAkrU7zkMi+Segq/Tx7y+WWanC5zfWZrXAuG6WTOYEXeWQdm//w==", + "node_modules/@storybook/addon-links": { + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-9.1.16.tgz", + "integrity": "sha512-21SJAEuOX4Fh/5VSeakuiJJeSH2ezXBia0cZMTkKYz6GOtoojeGigo3tuebVlsn9myqnkMZxiufnnRa7Zne8vg==", "dev": true, "license": "MIT", "dependencies": { @@ -12682,48 +12565,9 @@ "type": "opencollective", "url": "https://opencollective.com/storybook" }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-interactions": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-interactions/-/addon-interactions-8.6.12.tgz", - "integrity": "sha512-cTAJlTq6uVZBEbtwdXkXoPQ4jHOAGKQnYSezBT4pfNkdjn/FnEeaQhMBDzf14h2wr5OgBnJa6Lmd8LD9ficz4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.6.12", - "@storybook/test": "8.6.12", - "polished": "^4.2.2", - "ts-dedent": "^2.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-links": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-links/-/addon-links-8.6.12.tgz", - "integrity": "sha512-AfKujFHoAxhxq4yu+6NwylltS9lf5MPs1eLLXvOlwo3l7Y/c68OdxJ7j68vLQhs9H173WVYjKyjbjFxJWf/YYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.12" + "storybook": "^9.1.16" }, "peerDependenciesMeta": { "react": { @@ -12731,46 +12575,10 @@ } } }, - "node_modules/@storybook/addon-measure": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-measure/-/addon-measure-8.6.12.tgz", - "integrity": "sha512-tACmwqqOvutaQSduw8SMb62wICaT1rWaHtMN3vtWXuxgDPSdJQxLP+wdVyRYMAgpxhLyIO7YRf++Hfha9RHgFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "tiny-invariant": "^1.3.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-outline": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-outline/-/addon-outline-8.6.12.tgz", - "integrity": "sha512-1ylwm+n1s40S91No0v9T4tCjZORu3GbnjINlyjYTDLLhQHyBQd3nWR1Y1eewU4xH4cW9SnSLcMQFS/82xHqU6A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "ts-dedent": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, "node_modules/@storybook/addon-themes": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-8.6.12.tgz", - "integrity": "sha512-eqE40MUKTz9lLEOusXjRuDC7DwCSIwlgEnlbvhhEEme8IeKf2di6yvlhenY4nn5QfkUwY1POnZxfJ2OpXj0gqQ==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/addon-themes/-/addon-themes-9.1.16.tgz", + "integrity": "sha512-wAB11HfXmK7KcYI6an1+WQi2m9VPfFnM4EV66VOWR+1e1PUThfwr0LhaPXj1g32lFBWdmTZp/9YLGXTyJqSQwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -12781,91 +12589,47 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-toolbars": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-toolbars/-/addon-toolbars-8.6.12.tgz", - "integrity": "sha512-HEcSzo1DyFtIu5/ikVOmh5h85C1IvK9iFKSzBR6ice33zBOaehVJK+Z5f487MOXxPsZ63uvWUytwPyViGInj+g==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/addon-viewport": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/addon-viewport/-/addon-viewport-8.6.12.tgz", - "integrity": "sha512-EXK2LArAnABsPP0leJKy78L/lbMWow+EIJfytEP5fHaW4EhMR6h7Hzaqzre6U0IMMr/jVFa1ci+m0PJ0eQc2bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "memoizerific": "^1.11.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@storybook/angular": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-8.6.12.tgz", - "integrity": "sha512-hYbx+CaftAWuomGQ+wXpheodM5C7dTK2m/dpJ0JiWMxhMBt5Jh0SerW7KiFvODHwctXy0KZ8ZUT1PMhqPIldwg==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/angular/-/angular-9.1.16.tgz", + "integrity": "sha512-QaqMZ+KLqNj0xGiLjuL8l6iMJgQOiWIUwhebjcXC6Z7/k85KCDcBD/igxLQSj7xbzKyQGvgfZ8ykhYljndGUBA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-webpack5": "8.6.12", - "@storybook/components": "8.6.12", - "@storybook/core-webpack": "8.6.12", + "@storybook/builder-webpack5": "9.1.16", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.6.12", - "@storybook/preview-api": "8.6.12", - "@storybook/theming": "8.6.12", - "@types/react": "^18.0.37", - "@types/react-dom": "^18.0.11", - "@types/semver": "^7.3.4", - "@types/webpack-env": "^1.18.0", - "fd-package-json": "^1.2.0", - "find-up": "^5.0.0", - "semver": "^7.3.7", - "telejson": "^7.2.0", + "telejson": "8.0.0", "ts-dedent": "^2.0.0", "tsconfig-paths-webpack-plugin": "^4.0.1", - "util-deprecate": "^1.0.2", "webpack": "5" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "@angular-devkit/architect": ">=0.1500.0 < 0.2000.0", - "@angular-devkit/build-angular": ">=15.0.0 < 20.0.0", - "@angular-devkit/core": ">=15.0.0 < 20.0.0", - "@angular/animations": ">=15.0.0 < 20.0.0", - "@angular/cli": ">=15.0.0 < 20.0.0", - "@angular/common": ">=15.0.0 < 20.0.0", - "@angular/compiler": ">=15.0.0 < 20.0.0", - "@angular/compiler-cli": ">=15.0.0 < 20.0.0", - "@angular/core": ">=15.0.0 < 20.0.0", - "@angular/forms": ">=15.0.0 < 20.0.0", - "@angular/platform-browser": ">=15.0.0 < 20.0.0", - "@angular/platform-browser-dynamic": ">=15.0.0 < 20.0.0", - "rxjs": "^6.0.0 || ^7.4.0", - "storybook": "^8.6.12", - "typescript": "^4.0.0 || ^5.0.0", - "zone.js": ">= 0.11.1 < 1.0.0" + "@angular-devkit/architect": ">=0.1800.0 < 0.2100.0", + "@angular-devkit/build-angular": ">=18.0.0 < 21.0.0", + "@angular-devkit/core": ">=18.0.0 < 21.0.0", + "@angular/animations": ">=18.0.0 < 21.0.0", + "@angular/cli": ">=18.0.0 < 21.0.0", + "@angular/common": ">=18.0.0 < 21.0.0", + "@angular/compiler": ">=18.0.0 < 21.0.0", + "@angular/compiler-cli": ">=18.0.0 < 21.0.0", + "@angular/core": ">=18.0.0 < 21.0.0", + "@angular/forms": ">=18.0.0 < 21.0.0", + "@angular/platform-browser": ">=18.0.0 < 21.0.0", + "@angular/platform-browser-dynamic": ">=18.0.0 < 21.0.0", + "rxjs": "^6.5.3 || ^7.4.0", + "storybook": "^9.1.16", + "typescript": "^4.9.0 || ^5.0.0", + "zone.js": ">=0.14.0" }, "peerDependenciesMeta": { "@angular/animations": { @@ -12879,14 +12643,14 @@ } } }, - "node_modules/@storybook/blocks": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/blocks/-/blocks-8.6.12.tgz", - "integrity": "sha512-DohlTq6HM1jDbHYiXL4ZvZ00VkhpUp5uftzj/CZDLY1fYHRjqtaTwWm2/OpceivMA8zDitLcq5atEZN+f+siTg==", + "node_modules/@storybook/builder-vite": { + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/builder-vite/-/builder-vite-9.1.16.tgz", + "integrity": "sha512-CyvYA5w1BKeSVaRavKi+euWxLffshq0v9Rz/5E9MKCitbYtjwkDH6UMIYmcbTs906mEBuYqrbz3nygDP0ppodw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/icons": "^1.2.12", + "@storybook/csf-plugin": "9.1.16", "ts-dedent": "^2.0.0" }, "funding": { @@ -12894,46 +12658,28 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "storybook": "^8.6.12" - }, - "peerDependenciesMeta": { - "react": { - "optional": true - }, - "react-dom": { - "optional": true - } + "storybook": "^9.1.16", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/@storybook/builder-webpack5": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-8.6.12.tgz", - "integrity": "sha512-Z7RsQ/1+HbxdbM69JrEFcTL+pnVKUTMmeURMn5/eOvYTGjBtM18vbQTj0LjCUDIjC+v9U+uX8ZJEUVxFbGcxBw==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/builder-webpack5/-/builder-webpack5-9.1.16.tgz", + "integrity": "sha512-AkhGTLze11XMPTtEpQXFc9pUCCPnWeO3rxwbRKB+H42+WKq0YXFx0rlPVpd1HIaQ6qbBmonasRBa0bvI04O/Rg==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core-webpack": "8.6.12", - "@types/semver": "^7.3.4", - "browser-assert": "^1.2.1", + "@storybook/core-webpack": "9.1.16", "case-sensitive-paths-webpack-plugin": "^2.4.0", "cjs-module-lexer": "^1.2.3", - "constants-browserify": "^1.0.0", "css-loader": "^6.7.1", "es-module-lexer": "^1.5.0", "fork-ts-checker-webpack-plugin": "^8.0.0", "html-webpack-plugin": "^5.5.0", "magic-string": "^0.30.5", - "path-browserify": "^1.0.1", - "process": "^0.11.10", - "semver": "^7.3.7", "style-loader": "^3.3.1", "terser-webpack-plugin": "^5.3.1", "ts-dedent": "^2.0.0", - "url": "^0.11.0", - "util": "^0.12.4", - "util-deprecate": "^1.0.2", "webpack": "5", "webpack-dev-middleware": "^6.1.2", "webpack-hot-middleware": "^2.25.1", @@ -12944,7 +12690,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" }, "peerDependenciesMeta": { "typescript": { @@ -13057,56 +12803,10 @@ } } }, - "node_modules/@storybook/components": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/components/-/components-8.6.12.tgz", - "integrity": "sha512-FiaE8xvCdvKC2arYusgtlDNZ77b8ysr8njAYQZwwaIHjy27TbR2tEpLDCmUwSbANNmivtc/xGEiDDwcNppMWlQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, - "node_modules/@storybook/core": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core/-/core-8.6.12.tgz", - "integrity": "sha512-t+ZuDzAlsXKa6tLxNZT81gEAt4GNwsKP/Id2wluhmUWD/lwYW0uum1JiPUuanw8xD6TdakCW/7ULZc7aQUBLCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/theming": "8.6.12", - "better-opn": "^3.0.2", - "browser-assert": "^1.2.1", - "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", - "esbuild-register": "^3.5.0", - "jsdoc-type-pratt-parser": "^4.0.0", - "process": "^0.11.10", - "recast": "^0.23.5", - "semver": "^7.6.2", - "util": "^0.12.5", - "ws": "^8.2.3" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "prettier": "^2 || ^3" - }, - "peerDependenciesMeta": { - "prettier": { - "optional": true - } - } - }, "node_modules/@storybook/core-webpack": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-8.6.12.tgz", - "integrity": "sha512-TiE+KOm0hxb/p0JxeGHKxqTNX+xnTOFsBh6uloCSuvodutJ5pR/XpxKVxwo1gtSc0Uq3qpgbMhW6qYlYQetnKA==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/core-webpack/-/core-webpack-9.1.16.tgz", + "integrity": "sha512-LyvG/MS8PFyZI+PQB6NQK5k5jjLKImxQBl37Yztbb3SjFrB0jQhSccmal1CcNH9RgaSLqUI1DKLeosGylAcbxQ==", "dev": true, "license": "MIT", "dependencies": { @@ -13117,7 +12817,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@storybook/csf": { @@ -13131,9 +12831,9 @@ } }, "node_modules/@storybook/csf-plugin": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-8.6.12.tgz", - "integrity": "sha512-6s8CnP1aoKPb3XtC0jRLUp8M5vTA8RhGAwQDKUsFpCC7g89JR9CaKs9FY2ZSzsNbjR15uASi7b3K8BzeYumYQg==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/csf-plugin/-/csf-plugin-9.1.16.tgz", + "integrity": "sha512-GKlNNlmWeFBQxhQY5hZOSnFGbeKq69jal0dYNWoSImTjor28eYRHb9iQkDzRpijLPizBaB9MlxLsLrgFDp7adA==", "dev": true, "license": "MIT", "dependencies": { @@ -13144,7 +12844,7 @@ "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@storybook/global": { @@ -13155,9 +12855,9 @@ "license": "MIT" }, "node_modules/@storybook/icons": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.4.0.tgz", - "integrity": "sha512-Td73IeJxOyalzvjQL+JXx72jlIYHgs+REaHiREOqfpo3A2AYYG71AUbcv+lg7mEDIweKVCxsMQ0UKo634c8XeA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-1.6.0.tgz", + "integrity": "sha512-hcFZIjW8yQz8O8//2WTIXylm5Xsgc+lW9ISLgUk1xGmptIJQRdlhVIXCpSyLrQaaRiyhQRaVg7l3BD9S216BHw==", "dev": true, "license": "MIT", "engines": { @@ -13168,56 +12868,10 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta" } }, - "node_modules/@storybook/instrumenter": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/instrumenter/-/instrumenter-8.6.12.tgz", - "integrity": "sha512-VK5fYAF8jMwWP/u3YsmSwKGh+FeSY8WZn78flzRUwirp2Eg1WWjsqPRubAk7yTpcqcC/km9YMF3KbqfzRv2s/A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "@vitest/utils": "^2.1.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/manager-api": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/manager-api/-/manager-api-8.6.12.tgz", - "integrity": "sha512-O0SpISeJLNTQvhSBOsWzzkCgs8vCjOq1578rwqHlC6jWWm4QmtfdyXqnv7rR1Hk08kQ+Dzqh0uhwHx0nfwy4nQ==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, - "node_modules/@storybook/preview-api": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/preview-api/-/preview-api-8.6.12.tgz", - "integrity": "sha512-84FE3Hrs0AYKHqpDZOwx1S/ffOfxBdL65lhCoeI8GoWwCkzwa9zEP3kvXBo/BnEDO7nAfxvMhjASTZXbKRJh5Q==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, "node_modules/@storybook/react-dom-shim": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-8.6.12.tgz", - "integrity": "sha512-51QvoimkBzYs8s3rCYnY5h0cFqLz/Mh0vRcughwYaXckWzDBV8l67WBO5Xf5nBsukCbWyqBVPpEQLww8s7mrLA==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/react-dom-shim/-/react-dom-shim-9.1.16.tgz", + "integrity": "sha512-MsI4qTxdT6lMXQmo3IXhw3EaCC+vsZboyEZBx4pOJ+K/5cDJ6ZoQ3f0d4yGpVhumDxaxlnNAg954+f8WWXE1rQ==", "dev": true, "license": "MIT", "funding": { @@ -13227,30 +12881,7 @@ "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta", - "storybook": "^8.6.12" - } - }, - "node_modules/@storybook/test": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/test/-/test-8.6.12.tgz", - "integrity": "sha512-0BK1Eg+VD0lNMB1BtxqHE3tP9FdkUmohtvWG7cq6lWvMrbCmAmh3VWai3RMCCDOukPFpjabOr8BBRLVvhNpv2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@storybook/global": "^5.0.0", - "@storybook/instrumenter": "8.6.12", - "@testing-library/dom": "10.4.0", - "@testing-library/jest-dom": "6.5.0", - "@testing-library/user-event": "14.5.2", - "@vitest/expect": "2.0.5", - "@vitest/spy": "2.0.5" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@storybook/test-runner": { @@ -13290,37 +12921,19 @@ "storybook": "^0.0.0-0 || ^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0 || ^9.0.0-0" } }, - "node_modules/@storybook/theming": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/theming/-/theming-8.6.12.tgz", - "integrity": "sha512-6VjZg8HJ2Op7+KV7ihJpYrDnFtd9D1jrQnUS8LckcpuBXrIEbaut5+34ObY8ssQnSqkk2GwIZBBBQYQBCVvkOw==", - "dev": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/storybook" - }, - "peerDependencies": { - "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0" - } - }, "node_modules/@storybook/web-components": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-8.6.12.tgz", - "integrity": "sha512-j+609VT8abBlpV+tB/vqSRO/fKA1QpnKWlbE0JpolzmEbgla//pAZomPysoOnvTLL3lSX3conjiAAaTpwbjyLg==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/web-components/-/web-components-9.1.16.tgz", + "integrity": "sha512-YQbJhybF8QBI2yWNrgJbL+eTcZ7IX38xZOlN7U51/yybX40FIunHlLsektGMImI7rvmtf/2AGFHAj5FMqSfYiw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/components": "8.6.12", "@storybook/global": "^5.0.0", - "@storybook/manager-api": "8.6.12", - "@storybook/preview-api": "8.6.12", - "@storybook/theming": "8.6.12", "tiny-invariant": "^1.3.1", "ts-dedent": "^2.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "funding": { "type": "opencollective", @@ -13328,29 +12941,28 @@ }, "peerDependencies": { "lit": "^2.0.0 || ^3.0.0", - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, - "node_modules/@storybook/web-components-webpack5": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/@storybook/web-components-webpack5/-/web-components-webpack5-8.6.12.tgz", - "integrity": "sha512-DUrmxufzd9KZdPVaaYS5CG590EL/LM+wjber///sPtI0C/WdUUTCrg+2R5rYkX4f8bfqSH1y/MkJCiD+cW0TKg==", + "node_modules/@storybook/web-components-vite": { + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/@storybook/web-components-vite/-/web-components-vite-9.1.16.tgz", + "integrity": "sha512-WPmyTUy9DlWHP+sJY3eVyxYUtHPIXOQyhhvKU4YKBmihIUz8r0ObxLYnJXFHXWZcFuayn2B46MMHAWrnkXjMhw==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/builder-webpack5": "8.6.12", - "@storybook/web-components": "8.6.12" + "@storybook/builder-vite": "9.1.16", + "@storybook/web-components": "9.1.16" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/storybook" }, "peerDependencies": { - "lit": "^2.0.0 || ^3.0.0", - "storybook": "^8.6.12" + "storybook": "^9.1.16" } }, "node_modules/@swc/core": { @@ -13611,19 +13223,20 @@ } }, "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", - "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", + "picocolors": "1.1.1", "pretty-format": "^27.0.2" }, "engines": { @@ -13636,23 +13249,23 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "dequal": "^2.0.3" } }, "node_modules/@testing-library/jest-dom": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.5.0.tgz", - "integrity": "sha512-xGGHpBXYSHUUr6XsKBfs85TWlYKpTc37cSBBVrXcib2MkHLboWlkClhWF37JKlDb9KEq3dHs+f2xR7XJEWGBxA==", + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", "dev": true, "license": "MIT", "dependencies": { "@adobe/css-tools": "^4.4.0", "aria-query": "^5.0.0", - "chalk": "^3.0.0", "css.escape": "^1.5.1", "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", + "picocolors": "^1.1.1", "redent": "^3.0.0" }, "engines": { @@ -13661,20 +13274,6 @@ "yarn": ">=1" } }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", @@ -13683,9 +13282,9 @@ "license": "MIT" }, "node_modules/@testing-library/user-event": { - "version": "14.5.2", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", - "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "version": "14.6.1", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", + "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", "dev": true, "license": "MIT", "engines": { @@ -13867,7 +13466,8 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -13942,6 +13542,17 @@ "@types/responselike": "^1.0.0" } }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/chrome": { "version": "0.1.28", "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.28.tgz", @@ -14002,6 +13613,13 @@ "@types/ms": "*" } }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/eslint": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", @@ -14490,7 +14108,8 @@ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@types/proper-lockfile": { "version": "4.1.4", @@ -14520,21 +14139,12 @@ "integrity": "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, "node_modules/@types/responselike": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", @@ -14634,13 +14244,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/verror": { "version": "1.10.11", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", @@ -14659,13 +14262,6 @@ "@types/node": "*" } }, - "node_modules/@types/webpack-env": { - "version": "1.18.8", - "resolved": "https://registry.npmjs.org/@types/webpack-env/-/webpack-env-1.18.8.tgz", - "integrity": "sha512-G9eAoJRMLjcvN4I08wB5I7YofOb/kaJNd5uoCMX+LbKXTPCF+ZIHuqTnFaK9Jz1rgs035f9JUPUhNFtqgucy/A==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -15496,99 +15092,98 @@ ] }, "node_modules/@vitejs/plugin-basic-ssl": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-1.2.0.tgz", - "integrity": "sha512-mkQnxTkcldAzIsomk1UuLfAu9n+kpQ3JbHcpCp7d2Oo6ITtji8pHS3QToOWjhPFvNQSnhlkAjmGbhv2QvwO/7Q==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-basic-ssl/-/plugin-basic-ssl-2.1.0.tgz", + "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.21.3" + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" + "vite": "^6.0.0 || ^7.0.0" } }, "node_modules/@vitest/expect": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", - "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "2.0.5", - "@vitest/utils": "2.0.5", - "chai": "^5.1.1", - "tinyrainbow": "^1.2.0" + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, - "node_modules/@vitest/expect/node_modules/@vitest/pretty-format": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", - "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - } - }, - "node_modules/@vitest/expect/node_modules/@vitest/utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", - "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/pretty-format": "2.0.5", + "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", - "loupe": "^3.1.1", - "tinyrainbow": "^1.2.0" + "magic-string": "^0.30.17" }, "funding": { "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } } }, "node_modules/@vitest/pretty-format": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", - "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", "dev": true, "license": "MIT", "dependencies": { - "tinyrainbow": "^1.2.0" + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", - "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", "dev": true, "license": "MIT", "dependencies": { - "tinyspy": "^3.0.0" + "tinyspy": "^4.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { - "version": "2.1.9", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", - "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "2.1.9", - "loupe": "^3.1.2", - "tinyrainbow": "^1.2.0" + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" }, "funding": { "url": "https://opencollective.com/vitest" @@ -16272,20 +15867,46 @@ "ajv": "^8.8.2" } }, - "node_modules/angular-eslint": { - "version": "19.6.0", - "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-19.6.0.tgz", - "integrity": "sha512-9qfP6rR6De5xe9WyviD9Vdpg2F3iHTlo7T1129ms0AQXrG9/U/upIQmNUN+Jz9CiJcHDUsniyd+EL8hjuNYnOg==", + "node_modules/algoliasearch": { + "version": "5.35.0", + "resolved": "https://registry.npmjs.org/algoliasearch/-/algoliasearch-5.35.0.tgz", + "integrity": "sha512-Y+moNhsqgLmvJdgTsO4GZNgsaDWv8AOGAaPeIeHKlDn/XunoAqYbA+XNpBd1dW8GOXAUDyxC9Rxc7AV4kpFcIg==", "dev": true, "license": "MIT", "dependencies": { - "@angular-devkit/core": ">= 19.0.0 < 20.0.0", - "@angular-devkit/schematics": ">= 19.0.0 < 20.0.0", - "@angular-eslint/builder": "19.6.0", - "@angular-eslint/eslint-plugin": "19.6.0", - "@angular-eslint/eslint-plugin-template": "19.6.0", - "@angular-eslint/schematics": "19.6.0", - "@angular-eslint/template-parser": "19.6.0", + "@algolia/abtesting": "1.1.0", + "@algolia/client-abtesting": "5.35.0", + "@algolia/client-analytics": "5.35.0", + "@algolia/client-common": "5.35.0", + "@algolia/client-insights": "5.35.0", + "@algolia/client-personalization": "5.35.0", + "@algolia/client-query-suggestions": "5.35.0", + "@algolia/client-search": "5.35.0", + "@algolia/ingestion": "1.35.0", + "@algolia/monitoring": "1.35.0", + "@algolia/recommend": "5.35.0", + "@algolia/requester-browser-xhr": "5.35.0", + "@algolia/requester-fetch": "5.35.0", + "@algolia/requester-node-http": "5.35.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/angular-eslint": { + "version": "20.7.0", + "resolved": "https://registry.npmjs.org/angular-eslint/-/angular-eslint-20.7.0.tgz", + "integrity": "sha512-BCiTCLO3dr8pGPaM7qLcCruWNcoNNHnLn4DPqE5tHk1TAnTx5TcGy0p/FygharZw5RjWfDHLBjFfpeh4XWLMmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@angular-devkit/core": ">= 20.0.0 < 21.0.0", + "@angular-devkit/schematics": ">= 20.0.0 < 21.0.0", + "@angular-eslint/builder": "20.7.0", + "@angular-eslint/eslint-plugin": "20.7.0", + "@angular-eslint/eslint-plugin-template": "20.7.0", + "@angular-eslint/schematics": "20.7.0", + "@angular-eslint/template-parser": "20.7.0", "@typescript-eslint/types": "^8.0.0", "@typescript-eslint/utils": "^8.0.0" }, @@ -17168,13 +16789,13 @@ } }, "node_modules/babel-plugin-polyfill-corejs3": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.13.0.tgz", + "integrity": "sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==", "license": "MIT", "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" + "@babel/helper-define-polyfill-provider": "^0.6.5", + "core-js-compat": "^3.43.0" }, "peerDependencies": { "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" @@ -17331,14 +16952,14 @@ "license": "MIT" }, "node_modules/beasties": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.2.tgz", - "integrity": "sha512-p4AF8uYzm9Fwu8m/hSVTCPXrRBPmB34hQpHsec2KOaR9CZmgoU8IOv4Cvwq4hgz2p4hLMNbsdNl5XeA6XbAQwA==", + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/beasties/-/beasties-0.3.5.tgz", + "integrity": "sha512-NaWu+f4YrJxEttJSm16AzMIFtVldCvaJ68b1L098KpqXmxt9xOLtKoLkKxb8ekhOrLqEJAbvT6n6SEvB/sac7A==", "dev": true, "license": "Apache-2.0", "dependencies": { - "css-select": "^5.1.0", - "css-what": "^6.1.0", + "css-select": "^6.0.0", + "css-what": "^7.0.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "htmlparser2": "^10.0.0", @@ -17350,6 +16971,36 @@ "node": ">=14.0.0" } }, + "node_modules/beasties/node_modules/css-select": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-6.0.0.tgz", + "integrity": "sha512-rZZVSLle8v0+EY8QAkDWrKhpgt6SA5OtHsgBnsj6ZaLb5dmDVOWUDtQitd9ydxxvEjhewNudS6eTVU7uOyzvXw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^7.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "nth-check": "^2.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/beasties/node_modules/css-what": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-7.0.0.tgz", + "integrity": "sha512-wD5oz5xibMOPHzy13CyGmogB3phdvcDaB5t0W/Nr5Z2O/agcB8YwOz6e2Lsp10pNDzBoDO9nVa3RGs/2BttpHQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/bent": { "version": "7.3.12", "resolved": "https://registry.npmjs.org/bent/-/bent-7.3.12.tgz", @@ -17637,12 +17288,6 @@ "braintree-web": "3.123.2" } }, - "node_modules/browser-assert": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/browser-assert/-/browser-assert-1.2.1.tgz", - "integrity": "sha512-nfulgvOR6S4gt9UKCeGJOuSGBPGiFT6oQ/2UBnvTY/5aQ1PnksW72fhZkM30DzoRRv2WpwZf1vHHEr3mtuXIWQ==", - "dev": true - }, "node_modules/browser-hrtime": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/browser-hrtime/-/browser-hrtime-1.1.8.tgz", @@ -18321,9 +17966,9 @@ } }, "node_modules/chai": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz", - "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", "dev": true, "license": "MIT", "dependencies": { @@ -18334,7 +17979,7 @@ "pathval": "^2.0.0" }, "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/chalk": { @@ -19238,13 +18883,6 @@ "dev": true, "license": "MIT" }, - "node_modules/constants-browserify": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", - "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==", - "dev": true, - "license": "MIT" - }, "node_modules/content-disposition": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", @@ -20615,7 +20253,8 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/dom-converter": { "version": "0.2.0", @@ -21488,9 +21127,9 @@ "license": "MIT" }, "node_modules/esbuild": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.4.tgz", - "integrity": "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", + "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", "devOptional": true, "hasInstallScript": true, "license": "MIT", @@ -21501,31 +21140,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.4", - "@esbuild/android-arm": "0.25.4", - "@esbuild/android-arm64": "0.25.4", - "@esbuild/android-x64": "0.25.4", - "@esbuild/darwin-arm64": "0.25.4", - "@esbuild/darwin-x64": "0.25.4", - "@esbuild/freebsd-arm64": "0.25.4", - "@esbuild/freebsd-x64": "0.25.4", - "@esbuild/linux-arm": "0.25.4", - "@esbuild/linux-arm64": "0.25.4", - "@esbuild/linux-ia32": "0.25.4", - "@esbuild/linux-loong64": "0.25.4", - "@esbuild/linux-mips64el": "0.25.4", - "@esbuild/linux-ppc64": "0.25.4", - "@esbuild/linux-riscv64": "0.25.4", - "@esbuild/linux-s390x": "0.25.4", - "@esbuild/linux-x64": "0.25.4", - "@esbuild/netbsd-arm64": "0.25.4", - "@esbuild/netbsd-x64": "0.25.4", - "@esbuild/openbsd-arm64": "0.25.4", - "@esbuild/openbsd-x64": "0.25.4", - "@esbuild/sunos-x64": "0.25.4", - "@esbuild/win32-arm64": "0.25.4", - "@esbuild/win32-ia32": "0.25.4", - "@esbuild/win32-x64": "0.25.4" + "@esbuild/aix-ppc64": "0.25.9", + "@esbuild/android-arm": "0.25.9", + "@esbuild/android-arm64": "0.25.9", + "@esbuild/android-x64": "0.25.9", + "@esbuild/darwin-arm64": "0.25.9", + "@esbuild/darwin-x64": "0.25.9", + "@esbuild/freebsd-arm64": "0.25.9", + "@esbuild/freebsd-x64": "0.25.9", + "@esbuild/linux-arm": "0.25.9", + "@esbuild/linux-arm64": "0.25.9", + "@esbuild/linux-ia32": "0.25.9", + "@esbuild/linux-loong64": "0.25.9", + "@esbuild/linux-mips64el": "0.25.9", + "@esbuild/linux-ppc64": "0.25.9", + "@esbuild/linux-riscv64": "0.25.9", + "@esbuild/linux-s390x": "0.25.9", + "@esbuild/linux-x64": "0.25.9", + "@esbuild/netbsd-arm64": "0.25.9", + "@esbuild/netbsd-x64": "0.25.9", + "@esbuild/openbsd-arm64": "0.25.9", + "@esbuild/openbsd-x64": "0.25.9", + "@esbuild/openharmony-arm64": "0.25.9", + "@esbuild/sunos-x64": "0.25.9", + "@esbuild/win32-arm64": "0.25.9", + "@esbuild/win32-ia32": "0.25.9", + "@esbuild/win32-x64": "0.25.9" } }, "node_modules/esbuild-register": { @@ -21542,9 +21182,9 @@ } }, "node_modules/esbuild-wasm": { - "version": "0.25.4", - "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.4.tgz", - "integrity": "sha512-2HlCS6rNvKWaSKhWaG/YIyRsTsL3gUrMP2ToZMBIjw9LM7vVcIs+rz8kE2vExvTJgvM8OKPqNpcHawY/BQc/qQ==", + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.25.9.tgz", + "integrity": "sha512-Jpv5tCSwQg18aCqCRD3oHIX/prBhXMDapIoG//A+6+dV0e7KQMGFg85ihJ5T1EeMjbZjON3TqFy0VrGAnIHLDA==", "dev": true, "license": "MIT", "bin": { @@ -21908,21 +21548,20 @@ } }, "node_modules/eslint-plugin-storybook": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-0.12.0.tgz", - "integrity": "sha512-Lg5I0+npTgiYgZ4KSvGWGDFZi3eOCNJPaWX0c9rTEEXC5wvooOClsP9ZtbI4hhFKyKgYR877KiJxbRTSJq9gWA==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/eslint-plugin-storybook/-/eslint-plugin-storybook-9.1.16.tgz", + "integrity": "sha512-I8f3DXniPxFbcptVgOjtIHNvW6sDu1O2d1zNsxLKmeAvEaRLus1ij8iFHCgkNzMthrU5U2F4Wdo/aaSpz5kHjA==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/csf": "^0.1.11", - "@typescript-eslint/utils": "^8.8.1", - "ts-dedent": "^2.2.0" + "@typescript-eslint/utils": "^8.8.1" }, "engines": { - "node": ">= 18" + "node": ">=20.0.0" }, "peerDependencies": { - "eslint": ">=8" + "eslint": ">=8", + "storybook": "^9.1.16" } }, "node_modules/eslint-plugin-tailwindcss": { @@ -22578,16 +22217,6 @@ "bser": "2.1.1" } }, - "node_modules/fd-package-json": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fd-package-json/-/fd-package-json-1.2.0.tgz", - "integrity": "sha512-45LSPmWf+gC5tdCQMNH4s9Sr00bIkiD9aN7dc5hqkrEw1geRYyDQS1v1oMHAW3ysfxfndqGsrDREHHjNNbKUfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "walk-up-path": "^3.0.1" - } - }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", @@ -22599,10 +22228,13 @@ } }, "node_modules/fdir": { - "version": "6.4.6", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", - "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, "peerDependencies": { "picomatch": "^3 || ^4" }, @@ -23682,39 +23314,11 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/globby": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", - "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sindresorhus/merge-streams": "^2.1.0", - "fast-glob": "^3.3.3", - "ignore": "^7.0.3", - "path-type": "^6.0.0", - "slash": "^5.1.0", - "unicorn-magic": "^0.3.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby/node_modules/slash": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", - "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "license": "MIT" }, "node_modules/gopd": { "version": "1.2.0", @@ -24550,9 +24154,9 @@ "license": "BSD-3-Clause" }, "node_modules/ignore": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", - "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { @@ -24560,16 +24164,32 @@ } }, "node_modules/ignore-walk": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-7.0.0.tgz", - "integrity": "sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-8.0.0.tgz", + "integrity": "sha512-FCeMZT4NiRQGh+YkeKMtWrOmBgWjHjMJ26WQWrRQyoyzqevdaGSakUaJW5xQYmjLlUVk2qUnCjYVBax9EKKg8A==", "dev": true, "license": "ISC", "dependencies": { - "minimatch": "^9.0.0" + "minimatch": "^10.0.3" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/ignore-walk/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/image-size": { @@ -27556,16 +27176,6 @@ "dev": true, "license": "MIT" }, - "node_modules/jsdoc-type-pratt-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", - "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - } - }, "node_modules/jsdom": { "version": "26.1.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz", @@ -28133,9 +27743,9 @@ "license": "MIT" }, "node_modules/less": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/less/-/less-4.2.2.tgz", - "integrity": "sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/less/-/less-4.4.0.tgz", + "integrity": "sha512-kdTwsyRuncDfjEs0DlRILWNvxhDG/Zij4YLO4TMJgDLW+8OzpfkdPnRgrsRuY1o+oaxJGWsps5f/RVBgGmmN0w==", "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", @@ -28146,7 +27756,7 @@ "lessc": "bin/lessc" }, "engines": { - "node": ">=6" + "node": ">=14" }, "optionalDependencies": { "errno": "^0.1.1", @@ -28159,9 +27769,9 @@ } }, "node_modules/less-loader": { - "version": "12.2.0", - "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.2.0.tgz", - "integrity": "sha512-MYUxjSQSBUQmowc0l5nPieOYwMzGPUaTzB6inNW/bdPEG9zOL3eAAD1Qw5ZxSPk7we5dMojHwNODYMV1hq4EVg==", + "version": "12.3.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-12.3.0.tgz", + "integrity": "sha512-0M6+uYulvYIWs52y0LqN4+QM9TqWAohYSNTo4htE8Z7Cn3G/qQMEmktfHmyJT23k+20kU9zHH2wrfFXkxNLtVw==", "dev": true, "license": "MIT", "engines": { @@ -28520,9 +28130,9 @@ } }, "node_modules/listr2": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz", - "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-9.0.1.tgz", + "integrity": "sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==", "dev": true, "license": "MIT", "dependencies": { @@ -28534,13 +28144,13 @@ "wrap-ansi": "^9.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/listr2/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -28551,9 +28161,9 @@ } }, "node_modules/listr2/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -28581,9 +28191,9 @@ } }, "node_modules/listr2/node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "dev": true, "license": "MIT" }, @@ -28643,9 +28253,9 @@ } }, "node_modules/listr2/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { @@ -28659,9 +28269,9 @@ } }, "node_modules/listr2/node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "dev": true, "license": "MIT", "dependencies": { @@ -28677,9 +28287,9 @@ } }, "node_modules/lit": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.0.tgz", - "integrity": "sha512-DGVsqsOIHBww2DqnuZzW7QsuCdahp50ojuDaBPC7jUDRpYoH0z7kHBBYZewRzer75FwtrkmkKk7iOAwSaWdBmw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.1.tgz", + "integrity": "sha512-Ksr/8L3PTapbdXJCk+EJVB78jDodUMaP54gD24W186zGRARvwrsPfS60wae/SSCTCNZVPd1chXqio1qHQmu4NA==", "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^2.1.0", @@ -28708,9 +28318,9 @@ } }, "node_modules/lmdb": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.2.6.tgz", - "integrity": "sha512-SuHqzPl7mYStna8WRotY8XX/EUZBjjv3QyKIByeCLFfC9uXT/OIHByEcA07PzbMfQAM0KYJtLgtpMRlIe5dErQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/lmdb/-/lmdb-3.4.2.tgz", + "integrity": "sha512-nwVGUfTBUwJKXd6lRV8pFNfnrCC1+l49ESJRM19t/tFb/97QfJEixe5DYRvug5JO7DSFKoKaVy7oGMt5rVqZvg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -28726,22 +28336,15 @@ "download-lmdb-prebuilds": "bin/download-prebuilds.js" }, "optionalDependencies": { - "@lmdb/lmdb-darwin-arm64": "3.2.6", - "@lmdb/lmdb-darwin-x64": "3.2.6", - "@lmdb/lmdb-linux-arm": "3.2.6", - "@lmdb/lmdb-linux-arm64": "3.2.6", - "@lmdb/lmdb-linux-x64": "3.2.6", - "@lmdb/lmdb-win32-x64": "3.2.6" + "@lmdb/lmdb-darwin-arm64": "3.4.2", + "@lmdb/lmdb-darwin-x64": "3.4.2", + "@lmdb/lmdb-linux-arm": "3.4.2", + "@lmdb/lmdb-linux-arm64": "3.4.2", + "@lmdb/lmdb-linux-x64": "3.4.2", + "@lmdb/lmdb-win32-arm64": "3.4.2", + "@lmdb/lmdb-win32-x64": "3.4.2" } }, - "node_modules/lmdb/node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "dev": true, - "license": "MIT", - "optional": true - }, "node_modules/loader-runner": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", @@ -29160,6 +28763,7 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", + "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -29327,13 +28931,6 @@ "tmpl": "1.0.5" } }, - "node_modules/map-or-similar": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/map-or-similar/-/map-or-similar-1.5.0.tgz", - "integrity": "sha512-0aF7ZmVon1igznGI4VS30yugpduQW3y3GkcgGJOp7d8x8QrizhigUxjI/m2UojsXXto+jLAH3KSz+xOJTiORjg==", - "dev": true, - "license": "MIT" - }, "node_modules/map-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", @@ -29628,16 +29225,6 @@ "node": ">= 4.0.0" } }, - "node_modules/memoizerific": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/memoizerific/-/memoizerific-1.11.3.tgz", - "integrity": "sha512-/EuHYwAPdLtXwAwSZkh/Gutery6pD2KYd44oQLhAvQp/50mpyduZh8Q7PYHXTCJ+wuXxt7oij2LXyIJOOYFPog==", - "dev": true, - "license": "MIT", - "dependencies": { - "map-or-similar": "^1.5.0" - } - }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -30725,9 +30312,9 @@ "license": "MIT" }, "node_modules/msgpackr": { - "version": "1.11.4", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.4.tgz", - "integrity": "sha512-uaff7RG9VIC4jacFW9xzL3jc0iM32DNHe4jYVycBcjUePT/Klnfj7pqtWJt9khvDFizmjN2TlYniYmSS2LIaZg==", + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", + "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", "dev": true, "license": "MIT", "optional": true, @@ -31061,6 +30648,14 @@ "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", "license": "MIT" }, + "node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/node-api-version": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", @@ -31628,9 +31223,9 @@ } }, "node_modules/npm-install-checks": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.1.tgz", - "integrity": "sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.2.tgz", + "integrity": "sha512-z9HJBCYw9Zr8BqXcllKIs5nI+QggAImbBdHphOzVYrz2CB4iQ6FzWyKmlqDZua+51nAu7FcemlbTc9VgQN5XDQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -31651,6 +31246,115 @@ } }, "node_modules/npm-package-arg": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-13.0.0.tgz", + "integrity": "sha512-+t2etZAGcB7TbbLHfDwooV9ppB2LhhcT6A+L9cahsf9mEUAoQ6CktLEVvEnpD0N5CkX7zJqnPGaFtoQDy9EkHQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^9.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg/node_modules/hosted-git-info": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", + "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^11.1.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-package-arg/node_modules/lru-cache": { + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/npm-package-arg/node_modules/proc-log": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", + "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-packlist": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.3.tgz", + "integrity": "sha512-zPukTwJMOu5X5uvm0fztwS5Zxyvmk38H/LfidkOMt3gbZVCyro2cD/ETzwzVPcWZA3JOyPznfUN/nkyFiyUbxg==", + "dev": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^8.0.0", + "proc-log": "^6.0.0" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-packlist/node_modules/proc-log": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", + "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", + "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^7.1.0", + "npm-normalize-package-bin": "^4.0.0", + "npm-package-arg": "^12.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-pick-manifest/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/npm-pick-manifest/node_modules/npm-package-arg": { "version": "12.0.2", "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", @@ -31666,27 +31370,7 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm-package-arg/node_modules/hosted-git-info": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", - "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", - "dev": true, - "license": "ISC", - "dependencies": { - "lru-cache": "^10.0.1" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-package-arg/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/npm-package-arg/node_modules/proc-log": { + "node_modules/npm-pick-manifest/node_modules/proc-log": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz", "integrity": "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ==", @@ -31696,35 +31380,6 @@ "node": "^18.17.0 || >=20.5.0" } }, - "node_modules/npm-packlist": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-9.0.0.tgz", - "integrity": "sha512-8qSayfmHJQTx3nJWYbbUmflpyarbLMBc6LCAjYsiGtXxDB68HaZpb8re6zeaLGxZzDuMdhsg70jryJe+RrItVQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "ignore-walk": "^7.0.0" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, - "node_modules/npm-pick-manifest": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz", - "integrity": "sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "npm-install-checks": "^7.1.0", - "npm-normalize-package-bin": "^4.0.0", - "npm-package-arg": "^12.0.0", - "semver": "^7.3.5" - }, - "engines": { - "node": "^18.17.0 || >=20.5.0" - } - }, "node_modules/npm-registry-fetch": { "version": "18.0.2", "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz", @@ -31806,9 +31461,9 @@ } }, "node_modules/npm-registry-fetch/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -31826,6 +31481,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/npm-registry-fetch/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/npm-registry-fetch/node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -31920,9 +31588,9 @@ } }, "node_modules/npm-registry-fetch/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -31932,26 +31600,26 @@ "node": ">= 18" } }, - "node_modules/npm-registry-fetch/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/npm-registry-fetch/node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/npm-registry-fetch/node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", "engines": { @@ -32002,17 +31670,16 @@ } }, "node_modules/npm-registry-fetch/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -32878,9 +32545,9 @@ } }, "node_modules/ordered-binary": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.5.3.tgz", - "integrity": "sha512-oGFr3T+pYdTGJ+YFEILMpS3es+GiIbs9h/XQrclBXUtd44ey7XwfsMzM31f64I1SQOawDoDr/D823kNCADI8TA==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/ordered-binary/-/ordered-binary-1.6.0.tgz", + "integrity": "sha512-IQh2aMfMIDbPjI/8a3Edr+PiOpcsB7yo8NdW7aHWVaoR/pcDldunMvnnwbk/auPGqmKeAdxtZl7MHX/QmPwhvQ==", "dev": true, "license": "MIT", "optional": true @@ -33069,9 +32736,9 @@ "license": "BlueOak-1.0.0" }, "node_modules/pacote": { - "version": "20.0.0", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-20.0.0.tgz", - "integrity": "sha512-pRjC5UFwZCgx9kUFDVM9YEahv4guZ1nSLqwmWiLUnDbGsjs+U5w7z6Uc8HNR1a6x8qnu5y9xtGE6D1uAuYz+0A==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-21.0.0.tgz", + "integrity": "sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA==", "dev": true, "license": "ISC", "dependencies": { @@ -33084,7 +32751,7 @@ "fs-minipass": "^3.0.0", "minipass": "^7.0.2", "npm-package-arg": "^12.0.0", - "npm-packlist": "^9.0.0", + "npm-packlist": "^10.0.0", "npm-pick-manifest": "^10.0.0", "npm-registry-fetch": "^18.0.0", "proc-log": "^5.0.0", @@ -33097,7 +32764,7 @@ "pacote": "bin/index.js" }, "engines": { - "node": "^18.17.0 || >=20.5.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/pacote/node_modules/@npmcli/fs": { @@ -33138,17 +32805,16 @@ } }, "node_modules/pacote/node_modules/cacache/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -33179,9 +32845,9 @@ } }, "node_modules/pacote/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -33199,6 +32865,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pacote/node_modules/hosted-git-info": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.1.0.tgz", + "integrity": "sha512-Rw/B2DNQaPBICNXEm8balFz9a6WpZrkCGpcWFpy7nCj+NyhSdqXipmfvtmWt9xGfp0wZnBxB+iVpLmQMYt47Tw==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, "node_modules/pacote/node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -33252,9 +32931,9 @@ } }, "node_modules/pacote/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -33264,26 +32943,26 @@ "node": ">= 18" } }, - "node_modules/pacote/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", + "node_modules/pacote/node_modules/npm-package-arg": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.2.tgz", + "integrity": "sha512-f1NpFjNI9O4VbKMOlA5QoBq/vSQPORHcTZ2feJpFkTHJ9eQkdlmZEKSjcAhxTGInC7RlEyScT9ui67NaOsjFWA==", "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" + "license": "ISC", + "dependencies": { + "hosted-git-info": "^8.0.0", + "proc-log": "^5.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^6.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/pacote/node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", "engines": { @@ -33466,20 +33145,33 @@ } }, "node_modules/parse5-html-rewriting-stream": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-7.0.0.tgz", - "integrity": "sha512-mazCyGWkmCRWDI15Zp+UiCqMp/0dgEmkZRvhlsqqKYr4SsVm/TvnSpD9fCvqCA2zoWJcfRym846ejWBBHRiYEg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-8.0.0.tgz", + "integrity": "sha512-wzh11mj8KKkno1pZEu+l2EVeWsuKDfR5KNWZOTsslfUX8lPDZx77m9T0kIoAVkFtD1nx6YF8oh4BnPHvxMtNMw==", "dev": true, "license": "MIT", "dependencies": { - "entities": "^4.3.0", - "parse5": "^7.0.0", - "parse5-sax-parser": "^7.0.0" + "entities": "^6.0.0", + "parse5": "^8.0.0", + "parse5-sax-parser": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" } }, + "node_modules/parse5-html-rewriting-stream/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", @@ -33495,13 +33187,13 @@ } }, "node_modules/parse5-sax-parser": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-7.0.0.tgz", - "integrity": "sha512-5A+v2SNsq8T6/mG3ahcz8ZtQ0OUFTatxPbeidoMB7tkJSGDY3tdfl4MHovtLQHkEn5CGxijNWRQHhRQ6IRpXKg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5-sax-parser/-/parse5-sax-parser-8.0.0.tgz", + "integrity": "sha512-/dQ8UzHZwnrzs3EvDj6IkKrD/jIZyTlB+8XrHJvcjNgRdmWruNdN9i9RK/JtxakmlUdPwKubKPTCqvbTgzGhrw==", "dev": true, "license": "MIT", "dependencies": { - "parse5": "^7.0.0" + "parse5": "^8.0.0" }, "funding": { "url": "https://github.com/inikulin/parse5?sponsor=1" @@ -33603,23 +33295,10 @@ "node": ">=16" } }, - "node_modules/path-type": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", - "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/pathval": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", - "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", "dev": true, "license": "MIT", "engines": { @@ -33711,13 +33390,16 @@ } }, "node_modules/piscina": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/piscina/-/piscina-4.8.0.tgz", - "integrity": "sha512-EZJb+ZxDrQf3dihsUL7p42pjNyrNIFJCrRHPMgxu/svsj+P3xS3fuEWp7k2+rfsavfl1N0G29b1HGs7J0m8rZA==", + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-5.1.3.tgz", + "integrity": "sha512-0u3N7H4+hbr40KjuVn2uNhOcthu/9usKhnw5vT3J7ply79v3D3M8naI00el9Klcy16x557VsEkkUQaHCWFXC/g==", "dev": true, "license": "MIT", + "engines": { + "node": ">=20.x" + }, "optionalDependencies": { - "@napi-rs/nice": "^1.0.1" + "@napi-rs/nice": "^1.0.4" } }, "node_modules/pkce-challenge": { @@ -33967,19 +33649,6 @@ "node": ">=10.4.0" } }, - "node_modules/polished": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/polished/-/polished-4.3.1.tgz", - "integrity": "sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.17.8" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -34801,16 +34470,18 @@ } }, "node_modules/prettier-plugin-tailwindcss": { - "version": "0.6.11", - "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.11.tgz", - "integrity": "sha512-YxaYSIvZPAqhrrEpRtonnrXdghZg1irNg4qrjboCXrpybLWVs55cW2N3juhspVJiO0JBvYJT8SYsJpc8OQSnsA==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.7.1.tgz", + "integrity": "sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==", "dev": true, "license": "MIT", "engines": { - "node": ">=14.21.3" + "node": ">=20.19" }, "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", + "@prettier/plugin-hermes": "*", + "@prettier/plugin-oxc": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", @@ -34818,20 +34489,24 @@ "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", - "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", - "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "peerDependenciesMeta": { "@ianvs/prettier-plugin-sort-imports": { "optional": true }, + "@prettier/plugin-hermes": { + "optional": true + }, + "@prettier/plugin-oxc": { + "optional": true + }, "@prettier/plugin-pug": { "optional": true }, @@ -34850,9 +34525,6 @@ "prettier-plugin-css-order": { "optional": true }, - "prettier-plugin-import-sort": { - "optional": true - }, "prettier-plugin-jsdoc": { "optional": true }, @@ -34871,9 +34543,6 @@ "prettier-plugin-sort-imports": { "optional": true }, - "prettier-plugin-style-order": { - "optional": true - }, "prettier-plugin-svelte": { "optional": true } @@ -34896,6 +34565,7 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -34911,6 +34581,7 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=10" }, @@ -35297,7 +34968,8 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/read-binary-file-arch": { "version": "1.0.6", @@ -35472,12 +35144,6 @@ "node": ">=4" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/regex-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/regex-parser/-/regex-parser-2.3.1.tgz", @@ -36031,13 +35697,13 @@ } }, "node_modules/rollup": { - "version": "4.34.8", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.34.8.tgz", - "integrity": "sha512-489gTVMzAYdiZHFVA/ig/iYFllCcWFHMvUHI1rpFmkoUtRlQxqh6/yiNqnYibjMZ2b/+FUQwldG+aLsEt6bglQ==", - "dev": true, + "version": "4.52.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz", + "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==", + "devOptional": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -36047,35 +35713,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.34.8", - "@rollup/rollup-android-arm64": "4.34.8", - "@rollup/rollup-darwin-arm64": "4.34.8", - "@rollup/rollup-darwin-x64": "4.34.8", - "@rollup/rollup-freebsd-arm64": "4.34.8", - "@rollup/rollup-freebsd-x64": "4.34.8", - "@rollup/rollup-linux-arm-gnueabihf": "4.34.8", - "@rollup/rollup-linux-arm-musleabihf": "4.34.8", - "@rollup/rollup-linux-arm64-gnu": "4.34.8", - "@rollup/rollup-linux-arm64-musl": "4.34.8", - "@rollup/rollup-linux-loongarch64-gnu": "4.34.8", - "@rollup/rollup-linux-powerpc64le-gnu": "4.34.8", - "@rollup/rollup-linux-riscv64-gnu": "4.34.8", - "@rollup/rollup-linux-s390x-gnu": "4.34.8", - "@rollup/rollup-linux-x64-gnu": "4.34.8", - "@rollup/rollup-linux-x64-musl": "4.34.8", - "@rollup/rollup-win32-arm64-msvc": "4.34.8", - "@rollup/rollup-win32-ia32-msvc": "4.34.8", - "@rollup/rollup-win32-x64-msvc": "4.34.8", + "@rollup/rollup-android-arm-eabi": "4.52.3", + "@rollup/rollup-android-arm64": "4.52.3", + "@rollup/rollup-darwin-arm64": "4.52.3", + "@rollup/rollup-darwin-x64": "4.52.3", + "@rollup/rollup-freebsd-arm64": "4.52.3", + "@rollup/rollup-freebsd-x64": "4.52.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.52.3", + "@rollup/rollup-linux-arm-musleabihf": "4.52.3", + "@rollup/rollup-linux-arm64-gnu": "4.52.3", + "@rollup/rollup-linux-arm64-musl": "4.52.3", + "@rollup/rollup-linux-loong64-gnu": "4.52.3", + "@rollup/rollup-linux-ppc64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-gnu": "4.52.3", + "@rollup/rollup-linux-riscv64-musl": "4.52.3", + "@rollup/rollup-linux-s390x-gnu": "4.52.3", + "@rollup/rollup-linux-x64-gnu": "4.52.3", + "@rollup/rollup-linux-x64-musl": "4.52.3", + "@rollup/rollup-openharmony-arm64": "4.52.3", + "@rollup/rollup-win32-arm64-msvc": "4.52.3", + "@rollup/rollup-win32-ia32-msvc": "4.52.3", + "@rollup/rollup-win32-x64-gnu": "4.52.3", + "@rollup/rollup-win32-x64-msvc": "4.52.3", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", @@ -37701,9 +37363,9 @@ } }, "node_modules/spdx-license-ids": { - "version": "3.0.21", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", - "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", "dev": true, "license": "CC0-1.0" }, @@ -37851,6 +37513,19 @@ "node": ">= 0.8" } }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/steno": { "version": "0.4.4", "resolved": "https://registry.npmjs.org/steno/-/steno-0.4.4.tgz", @@ -37875,17 +37550,26 @@ } }, "node_modules/storybook": { - "version": "8.6.12", - "resolved": "https://registry.npmjs.org/storybook/-/storybook-8.6.12.tgz", - "integrity": "sha512-Z/nWYEHBTLK1ZBtAWdhxC0l5zf7ioJ7G4+zYqtTdYeb67gTnxNj80gehf8o8QY9L2zA2+eyMRGLC2V5fI7Z3Tw==", + "version": "9.1.16", + "resolved": "https://registry.npmjs.org/storybook/-/storybook-9.1.16.tgz", + "integrity": "sha512-339U14K6l46EFyRvaPS2ZlL7v7Pb+LlcXT8KAETrGPxq8v1sAjj2HAOB6zrlAK3M+0+ricssfAwsLCwt7Eg8TQ==", "dev": true, "license": "MIT", "dependencies": { - "@storybook/core": "8.6.12" + "@storybook/global": "^5.0.0", + "@testing-library/jest-dom": "^6.6.3", + "@testing-library/user-event": "^14.6.1", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/spy": "3.2.4", + "better-opn": "^3.0.2", + "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0", + "esbuild-register": "^3.5.0", + "recast": "^0.23.5", + "semver": "^7.6.2", + "ws": "^8.18.0" }, "bin": { - "getstorybook": "bin/index.cjs", - "sb": "bin/index.cjs", "storybook": "bin/index.cjs" }, "funding": { @@ -38382,16 +38066,6 @@ "node": ">= 10" } }, - "node_modules/symbol-observable": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", - "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10" - } - }, "node_modules/symbol-tree": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", @@ -38666,14 +38340,11 @@ "license": "ISC" }, "node_modules/telejson": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/telejson/-/telejson-7.2.0.tgz", - "integrity": "sha512-1QTEcJkJEhc8OnStBx/ILRu5J2p0GjvWsBx56bmZRqnrkdBMUe+nX92jxV+p3dB4CP6PZCdJMQJwCggkNBMzkQ==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/telejson/-/telejson-8.0.0.tgz", + "integrity": "sha512-8mCI1dHX80nchOkIEgvyWlGLgeh/SxO7JZPOud0DxvfFdI6MgwxRL8ff7rVdj6436uHhpWaxLQjU74Jb2I0u9g==", "dev": true, - "license": "MIT", - "dependencies": { - "memoizerific": "^1.11.3" - } + "license": "MIT" }, "node_modules/temp": { "version": "0.9.4", @@ -38795,13 +38466,13 @@ } }, "node_modules/terser": { - "version": "5.39.0", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.39.0.tgz", - "integrity": "sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==", + "version": "5.43.1", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", + "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", "license": "BSD-2-Clause", "dependencies": { "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.8.2", + "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, @@ -39036,9 +38707,9 @@ } }, "node_modules/tinyrainbow": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", - "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", "dev": true, "license": "MIT", "engines": { @@ -39046,9 +38717,9 @@ } }, "node_modules/tinyspy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", - "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", "dev": true, "license": "MIT", "engines": { @@ -39056,12 +38727,12 @@ } }, "node_modules/tldts": { - "version": "7.0.18", - "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.18.tgz", - "integrity": "sha512-lCcgTAgMxQ1JKOWrVGo6E69Ukbnx4Gc1wiYLRf6J5NN4HRYJtCby1rPF8rkQ4a6qqoFBK5dvjJ1zJ0F7VfDSvw==", + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", "license": "MIT", "dependencies": { - "tldts-core": "^7.0.18" + "tldts-core": "^7.0.19" }, "bin": { "tldts": "bin/cli.js" @@ -39392,6 +39063,26 @@ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "license": "MIT" }, + "node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "license": "MIT", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -39524,15 +39215,15 @@ "license": "0BSD" }, "node_modules/tuf-js": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.0.1.tgz", - "integrity": "sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tuf-js/-/tuf-js-3.1.0.tgz", + "integrity": "sha512-3T3T04WzowbwV2FDiGXBbr81t64g1MUGGJRgT4x5o97N+8ArdhVCAF9IxFrxuSJmM3E5Asn7nKHkao0ibcZXAg==", "dev": true, "license": "MIT", "dependencies": { "@tufjs/models": "3.0.1", - "debug": "^4.3.6", - "make-fetch-happen": "^14.0.1" + "debug": "^4.4.1", + "make-fetch-happen": "^14.0.3" }, "engines": { "node": "^18.17.0 || >=20.5.0" @@ -39599,9 +39290,9 @@ } }, "node_modules/tuf-js/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { @@ -39713,9 +39404,9 @@ } }, "node_modules/tuf-js/node_modules/minizlib": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz", - "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", "dependencies": { @@ -39725,26 +39416,10 @@ "node": ">= 18" } }, - "node_modules/tuf-js/node_modules/mkdirp": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz", - "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/tuf-js/node_modules/p-map": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz", - "integrity": "sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", "engines": { @@ -39795,17 +39470,16 @@ } }, "node_modules/tuf-js/node_modules/tar": { - "version": "7.4.3", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz", - "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==", + "version": "7.5.2", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.2.tgz", + "integrity": "sha512-7NyxrTE4Anh8km8iEy7o0QYPs+0JKBTj5ZaqHg6B39erLg0qYXN3BijtShwbsNSvQ+LN75+KV+C4QR/f6Gwnpg==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", - "minizlib": "^3.0.1", - "mkdirp": "^3.0.1", + "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { @@ -40256,19 +39930,6 @@ "node": ">=4" } }, - "node_modules/unicorn-magic": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", - "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/unified": { "version": "11.0.5", "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", @@ -40639,9 +40300,9 @@ } }, "node_modules/validate-npm-package-name": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.1.tgz", - "integrity": "sha512-OaI//3H0J7ZkR1OqlhGA8cA+Cbk/2xFOQpJOt5+s27/ta9eZwpeervh4Mxh4w0im/kdgktowaqVNR7QOrUd7Yg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.2.tgz", + "integrity": "sha512-IUoow1YUtvoBBC06dXs8bR8B9vuA3aJfmQNKMoaPG/OFsPmoQvw8xh+6Ye25Gx9DQhoEom3Pcu9MKHerm/NpUQ==", "dev": true, "license": "ISC", "engines": { @@ -40733,25 +40394,24 @@ } }, "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "dev": true, + "version": "7.1.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.1.11.tgz", + "integrity": "sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==", + "devOptional": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" @@ -40760,14 +40420,14 @@ "fsevents": "~2.3.3" }, "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", - "less": "*", + "less": "^4.0.0", "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" @@ -40808,330 +40468,53 @@ } } }, - "node_modules/vite/node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz", - "integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==", - "cpu": [ - "arm" - ], - "dev": true, + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-android-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz", - "integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz", - "integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-darwin-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz", - "integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz", - "integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz", - "integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz", - "integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz", - "integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz", - "integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz", - "integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz", - "integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz", - "integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz", - "integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz", - "integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz", - "integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz", - "integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz", - "integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz", - "integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/vite/node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz", - "integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "peer": true - }, - "node_modules/vite/node_modules/rollup": { - "version": "4.44.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz", - "integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==", - "dev": true, - "license": "MIT", - "peer": true, "dependencies": { - "@types/estree": "1.0.8" + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" }, - "bin": { - "rollup": "dist/bin/rollup" + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" + "node": ">=12.0.0" }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.44.0", - "@rollup/rollup-android-arm64": "4.44.0", - "@rollup/rollup-darwin-arm64": "4.44.0", - "@rollup/rollup-darwin-x64": "4.44.0", - "@rollup/rollup-freebsd-arm64": "4.44.0", - "@rollup/rollup-freebsd-x64": "4.44.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", - "@rollup/rollup-linux-arm-musleabihf": "4.44.0", - "@rollup/rollup-linux-arm64-gnu": "4.44.0", - "@rollup/rollup-linux-arm64-musl": "4.44.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-gnu": "4.44.0", - "@rollup/rollup-linux-riscv64-musl": "4.44.0", - "@rollup/rollup-linux-s390x-gnu": "4.44.0", - "@rollup/rollup-linux-x64-gnu": "4.44.0", - "@rollup/rollup-linux-x64-musl": "4.44.0", - "@rollup/rollup-win32-arm64-msvc": "4.44.0", - "@rollup/rollup-win32-ia32-msvc": "4.44.0", - "@rollup/rollup-win32-x64-msvc": "4.44.0", - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/w3c-xmlserializer": { @@ -41315,13 +40698,6 @@ "node": ">=4" } }, - "node_modules/walk-up-path": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-3.0.1.tgz", - "integrity": "sha512-9YlCL/ynK3CTlrSRrDxZvUauLzAswPCrsaCgilqFevUYpeEW0/3ScEjaa3kbW/T0ghhkEr7mv+fpjqn1Y1YuTA==", - "dev": true, - "license": "ISC" - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", @@ -41332,10 +40708,9 @@ } }, "node_modules/watchpack": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz", - "integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==", - "dev": true, + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", + "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", "license": "MIT", "dependencies": { "glob-to-regexp": "^0.4.1", @@ -42188,19 +41563,6 @@ "node": ">= 0.6" } }, - "node_modules/webpack/node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, "node_modules/websocket-driver": { "version": "0.7.4", "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", @@ -42529,6 +41891,22 @@ } } }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", @@ -42666,9 +42044,9 @@ } }, "node_modules/yoctocolors-cjs": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", - "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz", + "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==", "dev": true, "license": "MIT", "engines": { @@ -42679,9 +42057,9 @@ } }, "node_modules/zod": { - "version": "3.25.67", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz", - "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==", + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index f905b1d25e4..ab83b981b66 100644 --- a/package.json +++ b/package.json @@ -38,10 +38,10 @@ "libs/**/*" ], "devDependencies": { - "@angular-devkit/build-angular": "19.2.14", - "@angular-eslint/schematics": "19.6.0", - "@angular/cli": "19.2.14", - "@angular/compiler-cli": "19.2.14", + "@angular-devkit/build-angular": "20.3.12", + "@angular-eslint/schematics": "20.7.0", + "@angular/cli": "20.3.12", + "@angular/compiler-cli": "20.3.15", "@babel/core": "7.28.5", "@babel/preset-env": "7.28.5", "@compodoc/compodoc": "1.1.26", @@ -49,19 +49,15 @@ "@electron/rebuild": "4.0.1", "@eslint/compat": "2.0.0", "@lit-labs/signals": "0.1.2", - "@ngtools/webpack": "19.2.14", - "@storybook/addon-a11y": "8.6.12", - "@storybook/addon-actions": "8.6.12", - "@storybook/addon-designs": "8.2.1", - "@storybook/addon-essentials": "8.6.12", - "@storybook/addon-interactions": "8.6.12", - "@storybook/addon-links": "8.6.12", - "@storybook/addon-themes": "8.6.12", - "@storybook/angular": "8.6.12", - "@storybook/manager-api": "8.6.12", + "@ngtools/webpack": "20.3.11", + "@storybook/addon-a11y": "9.1.16", + "@storybook/addon-designs": "9.0.0-next.3", + "@storybook/addon-docs": "9.1.16", + "@storybook/addon-links": "9.1.16", + "@storybook/addon-themes": "9.1.16", + "@storybook/angular": "9.1.16", "@storybook/test-runner": "0.22.0", - "@storybook/theming": "8.6.12", - "@storybook/web-components-webpack5": "8.6.12", + "@storybook/web-components-vite": "9.1.16", "@tailwindcss/container-queries": "0.1.1", "@types/chrome": "0.1.28", "@types/firefox-webext-browser": "143.0.0", @@ -86,7 +82,7 @@ "@typescript-eslint/utils": "8.31.0", "@webcomponents/custom-elements": "1.6.0", "@yao-pkg/pkg": "6.5.1", - "angular-eslint": "19.6.0", + "angular-eslint": "20.7.0", "autoprefixer": "10.4.21", "axe-playwright": "2.2.2", "babel-loader": "9.2.1", @@ -109,7 +105,7 @@ "eslint-plugin-import": "2.31.0", "eslint-plugin-rxjs": "5.0.3", "eslint-plugin-rxjs-angular": "2.0.1", - "eslint-plugin-storybook": "0.12.0", + "eslint-plugin-storybook": "9.1.16", "eslint-plugin-tailwindcss": "3.18.0", "html-loader": "5.1.0", "html-webpack-injector": "1.1.4", @@ -123,16 +119,17 @@ "lint-staged": "16.0.0", "mini-css-extract-plugin": "2.9.4", "nx": "21.6.9", + "path-browserify": "1.0.1", "postcss": "8.5.6", "postcss-loader": "8.2.0", "prettier": "3.6.2", - "prettier-plugin-tailwindcss": "0.6.11", + "prettier-plugin-tailwindcss": "0.7.1", "process": "0.11.10", "remark-gfm": "4.0.1", "rimraf": "6.0.1", "sass": "1.94.2", "sass-loader": "16.0.6", - "storybook": "8.6.12", + "storybook": "9.1.16", "style-loader": "4.0.0", "tailwindcss": "3.4.17", "ts-jest": "29.4.5", @@ -151,24 +148,24 @@ "webpack-node-externals": "3.0.0" }, "dependencies": { - "@angular/animations": "19.2.14", - "@angular/cdk": "19.2.18", - "@angular/common": "19.2.14", - "@angular/compiler": "19.2.14", - "@angular/core": "19.2.14", - "@angular/forms": "19.2.14", - "@angular/platform-browser": "19.2.14", - "@angular/platform-browser-dynamic": "19.2.14", - "@angular/router": "19.2.14", - "@bitwarden/sdk-internal": "0.2.0-main.395", - "@bitwarden/commercial-sdk-internal": "0.2.0-main.395", + "@angular/animations": "20.3.15", + "@angular/cdk": "20.2.14", + "@angular/common": "20.3.15", + "@angular/compiler": "20.3.15", + "@angular/core": "20.3.15", + "@angular/forms": "20.3.15", + "@angular/platform-browser": "20.3.15", + "@angular/platform-browser-dynamic": "20.3.15", + "@angular/router": "20.3.15", + "@bitwarden/sdk-internal": "0.2.0-main.403", + "@bitwarden/commercial-sdk-internal": "0.2.0-main.403", "@electron/fuses": "1.8.0", "@emotion/css": "11.13.5", "@koa/multer": "4.0.0", "@koa/router": "14.0.0", "@microsoft/signalr": "8.0.7", "@microsoft/signalr-protocol-msgpack": "8.0.7", - "@ng-select/ng-select": "14.9.0", + "@ng-select/ng-select": "20.7.0", "@nx/devkit": "21.6.9", "@nx/eslint": "21.6.9", "@nx/jest": "21.6.9", @@ -189,7 +186,7 @@ "koa": "2.16.3", "koa-bodyparser": "4.4.1", "koa-json": "2.0.2", - "lit": "3.3.0", + "lit": "3.3.1", "lowdb": "1.0.0", "lunr": "2.3.9", "multer": "2.0.2", @@ -205,9 +202,10 @@ "rxjs": "7.8.1", "semver": "7.7.3", "tabbable": "6.3.0", - "tldts": "7.0.18", + "tldts": "7.0.19", "ts-node": "10.9.2", "utf-8-validate": "6.0.5", + "vite-tsconfig-paths": "5.1.4", "zone.js": "0.15.1", "zxcvbn": "4.4.2" }, @@ -229,7 +227,7 @@ "*.ts": "eslint --cache --cache-strategy content --fix" }, "engines": { - "node": "~22", + "node": ">=22.12.0", "npm": "~10" } }